diff --git a/lib/python3.12/site-packages/click/__init__.py b/lib/python3.12/site-packages/click/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..1aa547c57af68954b3bcd6012873881030e53d91 --- /dev/null +++ b/lib/python3.12/site-packages/click/__init__.py @@ -0,0 +1,123 @@ +""" +Click is a simple Python module inspired by the stdlib optparse to make +writing command line scripts fun. Unlike other modules, it's based +around a simple API that does not come with too much magic and is +composable. +""" + +from __future__ import annotations + +from .core import Argument as Argument +from .core import Command as Command +from .core import CommandCollection as CommandCollection +from .core import Context as Context +from .core import Group as Group +from .core import Option as Option +from .core import Parameter as Parameter +from .decorators import argument as argument +from .decorators import command as command +from .decorators import confirmation_option as confirmation_option +from .decorators import group as group +from .decorators import help_option as help_option +from .decorators import make_pass_decorator as make_pass_decorator +from .decorators import option as option +from .decorators import pass_context as pass_context +from .decorators import pass_obj as pass_obj +from .decorators import password_option as password_option +from .decorators import version_option as version_option +from .exceptions import Abort as Abort +from .exceptions import BadArgumentUsage as BadArgumentUsage +from .exceptions import BadOptionUsage as BadOptionUsage +from .exceptions import BadParameter as BadParameter +from .exceptions import ClickException as ClickException +from .exceptions import FileError as FileError +from .exceptions import MissingParameter as MissingParameter +from .exceptions import NoSuchOption as NoSuchOption +from .exceptions import UsageError as UsageError +from .formatting import HelpFormatter as HelpFormatter +from .formatting import wrap_text as wrap_text +from .globals import get_current_context as get_current_context +from .termui import clear as clear +from .termui import confirm as confirm +from .termui import echo_via_pager as echo_via_pager +from .termui import edit as edit +from .termui import getchar as getchar +from .termui import launch as launch +from .termui import pause as pause +from .termui import progressbar as progressbar +from .termui import prompt as prompt +from .termui import secho as secho +from .termui import style as style +from .termui import unstyle as unstyle +from .types import BOOL as BOOL +from .types import Choice as Choice +from .types import DateTime as DateTime +from .types import File as File +from .types import FLOAT as FLOAT +from .types import FloatRange as FloatRange +from .types import INT as INT +from .types import IntRange as IntRange +from .types import ParamType as ParamType +from .types import Path as Path +from .types import STRING as STRING +from .types import Tuple as Tuple +from .types import UNPROCESSED as UNPROCESSED +from .types import UUID as UUID +from .utils import echo as echo +from .utils import format_filename as format_filename +from .utils import get_app_dir as get_app_dir +from .utils import get_binary_stream as get_binary_stream +from .utils import get_text_stream as get_text_stream +from .utils import open_file as open_file + + +def __getattr__(name: str) -> object: + import warnings + + if name == "BaseCommand": + from .core import _BaseCommand + + warnings.warn( + "'BaseCommand' is deprecated and will be removed in Click 9.0. Use" + " 'Command' instead.", + DeprecationWarning, + stacklevel=2, + ) + return _BaseCommand + + if name == "MultiCommand": + from .core import _MultiCommand + + warnings.warn( + "'MultiCommand' is deprecated and will be removed in Click 9.0. Use" + " 'Group' instead.", + DeprecationWarning, + stacklevel=2, + ) + return _MultiCommand + + if name == "OptionParser": + from .parser import _OptionParser + + warnings.warn( + "'OptionParser' is deprecated and will be removed in Click 9.0. The" + " old parser is available in 'optparse'.", + DeprecationWarning, + stacklevel=2, + ) + return _OptionParser + + if name == "__version__": + import importlib.metadata + import warnings + + warnings.warn( + "The '__version__' attribute is deprecated and will be removed in" + " Click 9.1. Use feature detection or" + " 'importlib.metadata.version(\"click\")' instead.", + DeprecationWarning, + stacklevel=2, + ) + return importlib.metadata.version("click") + + raise AttributeError(name) diff --git a/lib/python3.12/site-packages/click/__pycache__/__init__.cpython-312.pyc b/lib/python3.12/site-packages/click/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7bc7badf2a4ffe7be3aa0e8512fd37bc3d6eb3dc Binary files /dev/null and b/lib/python3.12/site-packages/click/__pycache__/__init__.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/click/__pycache__/_compat.cpython-312.pyc b/lib/python3.12/site-packages/click/__pycache__/_compat.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3423565bb740c92766da23401a9ef42dfdea8966 Binary files /dev/null and b/lib/python3.12/site-packages/click/__pycache__/_compat.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/click/__pycache__/_termui_impl.cpython-312.pyc b/lib/python3.12/site-packages/click/__pycache__/_termui_impl.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2e0db0f712eb5c60d6b406db61000704380c8eab Binary files /dev/null and b/lib/python3.12/site-packages/click/__pycache__/_termui_impl.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/click/__pycache__/_textwrap.cpython-312.pyc b/lib/python3.12/site-packages/click/__pycache__/_textwrap.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..34c6d908b49f6039b41f24fdb1740d5cb35dbcf2 Binary files /dev/null and b/lib/python3.12/site-packages/click/__pycache__/_textwrap.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/click/__pycache__/_winconsole.cpython-312.pyc b/lib/python3.12/site-packages/click/__pycache__/_winconsole.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..21bd539fd87679e0fe628a87bbbd38384d65dcf5 Binary files /dev/null and b/lib/python3.12/site-packages/click/__pycache__/_winconsole.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/click/__pycache__/decorators.cpython-312.pyc b/lib/python3.12/site-packages/click/__pycache__/decorators.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9ab4e00ed80c61e04e5d4cc6d7c0dda038457427 Binary files /dev/null and b/lib/python3.12/site-packages/click/__pycache__/decorators.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/click/__pycache__/exceptions.cpython-312.pyc b/lib/python3.12/site-packages/click/__pycache__/exceptions.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c8602c3f25ecd8b1fe11b9c94bc0e307ea769651 Binary files /dev/null and b/lib/python3.12/site-packages/click/__pycache__/exceptions.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/click/__pycache__/formatting.cpython-312.pyc b/lib/python3.12/site-packages/click/__pycache__/formatting.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9b97b44c8872bf472eabd6a9b17b950b818b3bf1 Binary files /dev/null and b/lib/python3.12/site-packages/click/__pycache__/formatting.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/click/__pycache__/globals.cpython-312.pyc b/lib/python3.12/site-packages/click/__pycache__/globals.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e17c079a24cb05721405c34111579b3b4c3dc62b Binary files /dev/null and b/lib/python3.12/site-packages/click/__pycache__/globals.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/click/__pycache__/parser.cpython-312.pyc b/lib/python3.12/site-packages/click/__pycache__/parser.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bc72a77774977fa8705e794b61c7f78d161aa7b7 Binary files /dev/null and b/lib/python3.12/site-packages/click/__pycache__/parser.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/click/__pycache__/shell_completion.cpython-312.pyc b/lib/python3.12/site-packages/click/__pycache__/shell_completion.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..33e012e4eb764c6358932d7d92580b78cf5ad704 Binary files /dev/null and b/lib/python3.12/site-packages/click/__pycache__/shell_completion.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/click/__pycache__/termui.cpython-312.pyc b/lib/python3.12/site-packages/click/__pycache__/termui.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d853d549cd0e7a98db9bd9c949e395a85fbd5552 Binary files /dev/null and b/lib/python3.12/site-packages/click/__pycache__/termui.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/click/__pycache__/testing.cpython-312.pyc b/lib/python3.12/site-packages/click/__pycache__/testing.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..86ce83287c763eae8ed3a50f920eae9c9c21171d Binary files /dev/null and b/lib/python3.12/site-packages/click/__pycache__/testing.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/click/__pycache__/types.cpython-312.pyc b/lib/python3.12/site-packages/click/__pycache__/types.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ced2d1248cecc866a481e62c07f16767e2743739 Binary files /dev/null and b/lib/python3.12/site-packages/click/__pycache__/types.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/click/__pycache__/utils.cpython-312.pyc b/lib/python3.12/site-packages/click/__pycache__/utils.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b16f675a300dad74b3df62c1febee603716194b1 Binary files /dev/null and b/lib/python3.12/site-packages/click/__pycache__/utils.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/click/_compat.py b/lib/python3.12/site-packages/click/_compat.py new file mode 100644 index 0000000000000000000000000000000000000000..f2726b93afe2ed69ba20d8e709c9cbd22b9d2339 --- /dev/null +++ b/lib/python3.12/site-packages/click/_compat.py @@ -0,0 +1,622 @@ +from __future__ import annotations + +import codecs +import collections.abc as cabc +import io +import os +import re +import sys +import typing as t +from types import TracebackType +from weakref import WeakKeyDictionary + +CYGWIN = sys.platform.startswith("cygwin") +WIN = sys.platform.startswith("win") +auto_wrap_for_ansi: t.Callable[[t.TextIO], t.TextIO] | None = None +_ansi_re = re.compile(r"\033\[[;?0-9]*[a-zA-Z]") + + +def _make_text_stream( + stream: t.BinaryIO, + encoding: str | None, + errors: str | None, + force_readable: bool = False, + force_writable: bool = False, +) -> t.TextIO: + if encoding is None: + encoding = get_best_encoding(stream) + if errors is None: + errors = "replace" + return _NonClosingTextIOWrapper( + stream, + encoding, + errors, + line_buffering=True, + force_readable=force_readable, + force_writable=force_writable, + ) + + +def is_ascii_encoding(encoding: str) -> bool: + """Checks if a given encoding is ascii.""" + try: + return codecs.lookup(encoding).name == "ascii" + except LookupError: + return False + + +def get_best_encoding(stream: t.IO[t.Any]) -> str: + """Returns the default stream encoding if not found.""" + rv = getattr(stream, "encoding", None) or sys.getdefaultencoding() + if is_ascii_encoding(rv): + return "utf-8" + return rv + + +class _NonClosingTextIOWrapper(io.TextIOWrapper): + def __init__( + self, + stream: t.BinaryIO, + encoding: str | None, + errors: str | None, + force_readable: bool = False, + force_writable: bool = False, + **extra: t.Any, + ) -> None: + self._stream = stream = t.cast( + t.BinaryIO, _FixupStream(stream, force_readable, force_writable) + ) + super().__init__(stream, encoding, errors, **extra) + + def __del__(self) -> None: + try: + self.detach() + except Exception: + pass + + def isatty(self) -> bool: + # https://bitbucket.org/pypy/pypy/issue/1803 + return self._stream.isatty() + + +class _FixupStream: + """The new io interface needs more from streams than streams + traditionally implement. As such, this fix-up code is necessary in + some circumstances. + + The forcing of readable and writable flags are there because some tools + put badly patched objects on sys (one such offender are certain version + of jupyter notebook). + """ + + def __init__( + self, + stream: t.BinaryIO, + force_readable: bool = False, + force_writable: bool = False, + ): + self._stream = stream + self._force_readable = force_readable + self._force_writable = force_writable + + def __getattr__(self, name: str) -> t.Any: + return getattr(self._stream, name) + + def read1(self, size: int) -> bytes: + f = getattr(self._stream, "read1", None) + + if f is not None: + return t.cast(bytes, f(size)) + + return self._stream.read(size) + + def readable(self) -> bool: + if self._force_readable: + return True + x = getattr(self._stream, "readable", None) + if x is not None: + return t.cast(bool, x()) + try: + self._stream.read(0) + except Exception: + return False + return True + + def writable(self) -> bool: + if self._force_writable: + return True + x = getattr(self._stream, "writable", None) + if x is not None: + return t.cast(bool, x()) + try: + self._stream.write(b"") + except Exception: + try: + self._stream.write(b"") + except Exception: + return False + return True + + def seekable(self) -> bool: + x = getattr(self._stream, "seekable", None) + if x is not None: + return t.cast(bool, x()) + try: + self._stream.seek(self._stream.tell()) + except Exception: + return False + return True + + +def _is_binary_reader(stream: t.IO[t.Any], default: bool = False) -> bool: + try: + return isinstance(stream.read(0), bytes) + except Exception: + return default + # This happens in some cases where the stream was already + # closed. In this case, we assume the default. + + +def _is_binary_writer(stream: t.IO[t.Any], default: bool = False) -> bool: + try: + stream.write(b"") + except Exception: + try: + stream.write("") + return False + except Exception: + pass + return default + return True + + +def _find_binary_reader(stream: t.IO[t.Any]) -> t.BinaryIO | None: + # We need to figure out if the given stream is already binary. + # This can happen because the official docs recommend detaching + # the streams to get binary streams. Some code might do this, so + # we need to deal with this case explicitly. + if _is_binary_reader(stream, False): + return t.cast(t.BinaryIO, stream) + + buf = getattr(stream, "buffer", None) + + # Same situation here; this time we assume that the buffer is + # actually binary in case it's closed. + if buf is not None and _is_binary_reader(buf, True): + return t.cast(t.BinaryIO, buf) + + return None + + +def _find_binary_writer(stream: t.IO[t.Any]) -> t.BinaryIO | None: + # We need to figure out if the given stream is already binary. + # This can happen because the official docs recommend detaching + # the streams to get binary streams. Some code might do this, so + # we need to deal with this case explicitly. + if _is_binary_writer(stream, False): + return t.cast(t.BinaryIO, stream) + + buf = getattr(stream, "buffer", None) + + # Same situation here; this time we assume that the buffer is + # actually binary in case it's closed. + if buf is not None and _is_binary_writer(buf, True): + return t.cast(t.BinaryIO, buf) + + return None + + +def _stream_is_misconfigured(stream: t.TextIO) -> bool: + """A stream is misconfigured if its encoding is ASCII.""" + # If the stream does not have an encoding set, we assume it's set + # to ASCII. This appears to happen in certain unittest + # environments. It's not quite clear what the correct behavior is + # but this at least will force Click to recover somehow. + return is_ascii_encoding(getattr(stream, "encoding", None) or "ascii") + + +def _is_compat_stream_attr(stream: t.TextIO, attr: str, value: str | None) -> bool: + """A stream attribute is compatible if it is equal to the + desired value or the desired value is unset and the attribute + has a value. + """ + stream_value = getattr(stream, attr, None) + return stream_value == value or (value is None and stream_value is not None) + + +def _is_compatible_text_stream( + stream: t.TextIO, encoding: str | None, errors: str | None +) -> bool: + """Check if a stream's encoding and errors attributes are + compatible with the desired values. + """ + return _is_compat_stream_attr( + stream, "encoding", encoding + ) and _is_compat_stream_attr(stream, "errors", errors) + + +def _force_correct_text_stream( + text_stream: t.IO[t.Any], + encoding: str | None, + errors: str | None, + is_binary: t.Callable[[t.IO[t.Any], bool], bool], + find_binary: t.Callable[[t.IO[t.Any]], t.BinaryIO | None], + force_readable: bool = False, + force_writable: bool = False, +) -> t.TextIO: + if is_binary(text_stream, False): + binary_reader = t.cast(t.BinaryIO, text_stream) + else: + text_stream = t.cast(t.TextIO, text_stream) + # If the stream looks compatible, and won't default to a + # misconfigured ascii encoding, return it as-is. + if _is_compatible_text_stream(text_stream, encoding, errors) and not ( + encoding is None and _stream_is_misconfigured(text_stream) + ): + return text_stream + + # Otherwise, get the underlying binary reader. + possible_binary_reader = find_binary(text_stream) + + # If that's not possible, silently use the original reader + # and get mojibake instead of exceptions. + if possible_binary_reader is None: + return text_stream + + binary_reader = possible_binary_reader + + # Default errors to replace instead of strict in order to get + # something that works. + if errors is None: + errors = "replace" + + # Wrap the binary stream in a text stream with the correct + # encoding parameters. + return _make_text_stream( + binary_reader, + encoding, + errors, + force_readable=force_readable, + force_writable=force_writable, + ) + + +def _force_correct_text_reader( + text_reader: t.IO[t.Any], + encoding: str | None, + errors: str | None, + force_readable: bool = False, +) -> t.TextIO: + return _force_correct_text_stream( + text_reader, + encoding, + errors, + _is_binary_reader, + _find_binary_reader, + force_readable=force_readable, + ) + + +def _force_correct_text_writer( + text_writer: t.IO[t.Any], + encoding: str | None, + errors: str | None, + force_writable: bool = False, +) -> t.TextIO: + return _force_correct_text_stream( + text_writer, + encoding, + errors, + _is_binary_writer, + _find_binary_writer, + force_writable=force_writable, + ) + + +def get_binary_stdin() -> t.BinaryIO: + reader = _find_binary_reader(sys.stdin) + if reader is None: + raise RuntimeError("Was not able to determine binary stream for sys.stdin.") + return reader + + +def get_binary_stdout() -> t.BinaryIO: + writer = _find_binary_writer(sys.stdout) + if writer is None: + raise RuntimeError("Was not able to determine binary stream for sys.stdout.") + return writer + + +def get_binary_stderr() -> t.BinaryIO: + writer = _find_binary_writer(sys.stderr) + if writer is None: + raise RuntimeError("Was not able to determine binary stream for sys.stderr.") + return writer + + +def get_text_stdin(encoding: str | None = None, errors: str | None = None) -> t.TextIO: + rv = _get_windows_console_stream(sys.stdin, encoding, errors) + if rv is not None: + return rv + return _force_correct_text_reader(sys.stdin, encoding, errors, force_readable=True) + + +def get_text_stdout(encoding: str | None = None, errors: str | None = None) -> t.TextIO: + rv = _get_windows_console_stream(sys.stdout, encoding, errors) + if rv is not None: + return rv + return _force_correct_text_writer(sys.stdout, encoding, errors, force_writable=True) + + +def get_text_stderr(encoding: str | None = None, errors: str | None = None) -> t.TextIO: + rv = _get_windows_console_stream(sys.stderr, encoding, errors) + if rv is not None: + return rv + return _force_correct_text_writer(sys.stderr, encoding, errors, force_writable=True) + + +def _wrap_io_open( + file: str | os.PathLike[str] | int, + mode: str, + encoding: str | None, + errors: str | None, +) -> t.IO[t.Any]: + """Handles not passing ``encoding`` and ``errors`` in binary mode.""" + if "b" in mode: + return open(file, mode) + + return open(file, mode, encoding=encoding, errors=errors) + + +def open_stream( + filename: str | os.PathLike[str], + mode: str = "r", + encoding: str | None = None, + errors: str | None = "strict", + atomic: bool = False, +) -> tuple[t.IO[t.Any], bool]: + binary = "b" in mode + filename = os.fspath(filename) + + # Standard streams first. These are simple because they ignore the + # atomic flag. Use fsdecode to handle Path("-"). + if os.fsdecode(filename) == "-": + if any(m in mode for m in ["w", "a", "x"]): + if binary: + return get_binary_stdout(), False + return get_text_stdout(encoding=encoding, errors=errors), False + if binary: + return get_binary_stdin(), False + return get_text_stdin(encoding=encoding, errors=errors), False + + # Non-atomic writes directly go out through the regular open functions. + if not atomic: + return _wrap_io_open(filename, mode, encoding, errors), True + + # Some usability stuff for atomic writes + if "a" in mode: + raise ValueError( + "Appending to an existing file is not supported, because that" + " would involve an expensive `copy`-operation to a temporary" + " file. Open the file in normal `w`-mode and copy explicitly" + " if that's what you're after." + ) + if "x" in mode: + raise ValueError("Use the `overwrite`-parameter instead.") + if "w" not in mode: + raise ValueError("Atomic writes only make sense with `w`-mode.") + + # Atomic writes are more complicated. They work by opening a file + # as a proxy in the same folder and then using the fdopen + # functionality to wrap it in a Python file. Then we wrap it in an + # atomic file that moves the file over on close. + import errno + import random + + try: + perm: int | None = os.stat(filename).st_mode + except OSError: + perm = None + + flags = os.O_RDWR | os.O_CREAT | os.O_EXCL + + if binary: + flags |= getattr(os, "O_BINARY", 0) + + while True: + tmp_filename = os.path.join( + os.path.dirname(filename), + f".__atomic-write{random.randrange(1 << 32):08x}", + ) + try: + fd = os.open(tmp_filename, flags, 0o666 if perm is None else perm) + break + except OSError as e: + if e.errno == errno.EEXIST or ( + os.name == "nt" + and e.errno == errno.EACCES + and os.path.isdir(e.filename) + and os.access(e.filename, os.W_OK) + ): + continue + raise + + if perm is not None: + os.chmod(tmp_filename, perm) # in case perm includes bits in umask + + f = _wrap_io_open(fd, mode, encoding, errors) + af = _AtomicFile(f, tmp_filename, os.path.realpath(filename)) + return t.cast(t.IO[t.Any], af), True + + +class _AtomicFile: + def __init__(self, f: t.IO[t.Any], tmp_filename: str, real_filename: str) -> None: + self._f = f + self._tmp_filename = tmp_filename + self._real_filename = real_filename + self.closed = False + + @property + def name(self) -> str: + return self._real_filename + + def close(self, delete: bool = False) -> None: + if self.closed: + return + self._f.close() + os.replace(self._tmp_filename, self._real_filename) + self.closed = True + + def __getattr__(self, name: str) -> t.Any: + return getattr(self._f, name) + + def __enter__(self) -> _AtomicFile: + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_value: BaseException | None, + tb: TracebackType | None, + ) -> None: + self.close(delete=exc_type is not None) + + def __repr__(self) -> str: + return repr(self._f) + + +def strip_ansi(value: str) -> str: + return _ansi_re.sub("", value) + + +def _is_jupyter_kernel_output(stream: t.IO[t.Any]) -> bool: + while isinstance(stream, (_FixupStream, _NonClosingTextIOWrapper)): + stream = stream._stream + + return stream.__class__.__module__.startswith("ipykernel.") + + +def should_strip_ansi( + stream: t.IO[t.Any] | None = None, color: bool | None = None +) -> bool: + if color is None: + if stream is None: + stream = sys.stdin + return not isatty(stream) and not _is_jupyter_kernel_output(stream) + return not color + + +# On Windows, wrap the output streams with colorama to support ANSI +# color codes. +# NOTE: double check is needed so mypy does not analyze this on Linux +if sys.platform.startswith("win") and WIN: + from ._winconsole import _get_windows_console_stream + + def _get_argv_encoding() -> str: + import locale + + return locale.getpreferredencoding() + + _ansi_stream_wrappers: cabc.MutableMapping[t.TextIO, t.TextIO] = WeakKeyDictionary() + + def auto_wrap_for_ansi(stream: t.TextIO, color: bool | None = None) -> t.TextIO: + """Support ANSI color and style codes on Windows by wrapping a + stream with colorama. + """ + try: + cached = _ansi_stream_wrappers.get(stream) + except Exception: + cached = None + + if cached is not None: + return cached + + import colorama + + strip = should_strip_ansi(stream, color) + ansi_wrapper = colorama.AnsiToWin32(stream, strip=strip) + rv = t.cast(t.TextIO, ansi_wrapper.stream) + _write = rv.write + + def _safe_write(s: str) -> int: + try: + return _write(s) + except BaseException: + ansi_wrapper.reset_all() + raise + + rv.write = _safe_write # type: ignore[method-assign] + + try: + _ansi_stream_wrappers[stream] = rv + except Exception: + pass + + return rv + +else: + + def _get_argv_encoding() -> str: + return getattr(sys.stdin, "encoding", None) or sys.getfilesystemencoding() + + def _get_windows_console_stream( + f: t.TextIO, encoding: str | None, errors: str | None + ) -> t.TextIO | None: + return None + + +def term_len(x: str) -> int: + return len(strip_ansi(x)) + + +def isatty(stream: t.IO[t.Any]) -> bool: + try: + return stream.isatty() + except Exception: + return False + + +def _make_cached_stream_func( + src_func: t.Callable[[], t.TextIO | None], + wrapper_func: t.Callable[[], t.TextIO], +) -> t.Callable[[], t.TextIO | None]: + cache: cabc.MutableMapping[t.TextIO, t.TextIO] = WeakKeyDictionary() + + def func() -> t.TextIO | None: + stream = src_func() + + if stream is None: + return None + + try: + rv = cache.get(stream) + except Exception: + rv = None + if rv is not None: + return rv + rv = wrapper_func() + try: + cache[stream] = rv + except Exception: + pass + return rv + + return func + + +_default_text_stdin = _make_cached_stream_func(lambda: sys.stdin, get_text_stdin) +_default_text_stdout = _make_cached_stream_func(lambda: sys.stdout, get_text_stdout) +_default_text_stderr = _make_cached_stream_func(lambda: sys.stderr, get_text_stderr) + + +binary_streams: cabc.Mapping[str, t.Callable[[], t.BinaryIO]] = { + "stdin": get_binary_stdin, + "stdout": get_binary_stdout, + "stderr": get_binary_stderr, +} + +text_streams: cabc.Mapping[str, t.Callable[[str | None, str | None], t.TextIO]] = { + "stdin": get_text_stdin, + "stdout": get_text_stdout, + "stderr": get_text_stderr, +} diff --git a/lib/python3.12/site-packages/click/_termui_impl.py b/lib/python3.12/site-packages/click/_termui_impl.py new file mode 100644 index 0000000000000000000000000000000000000000..51fd9bf358f0ee8dedceb88efa8c6a60dc40d4a8 --- /dev/null +++ b/lib/python3.12/site-packages/click/_termui_impl.py @@ -0,0 +1,839 @@ +""" +This module contains implementations for the termui module. To keep the +import time of Click down, some infrequently used functionality is +placed in this module and only imported as needed. +""" + +from __future__ import annotations + +import collections.abc as cabc +import contextlib +import math +import os +import shlex +import sys +import time +import typing as t +from gettext import gettext as _ +from io import StringIO +from pathlib import Path +from shutil import which +from types import TracebackType + +from ._compat import _default_text_stdout +from ._compat import CYGWIN +from ._compat import get_best_encoding +from ._compat import isatty +from ._compat import open_stream +from ._compat import strip_ansi +from ._compat import term_len +from ._compat import WIN +from .exceptions import ClickException +from .utils import echo + +V = t.TypeVar("V") + +if os.name == "nt": + BEFORE_BAR = "\r" + AFTER_BAR = "\n" +else: + BEFORE_BAR = "\r\033[?25l" + AFTER_BAR = "\033[?25h\n" + + +class ProgressBar(t.Generic[V]): + def __init__( + self, + iterable: cabc.Iterable[V] | None, + length: int | None = None, + fill_char: str = "#", + empty_char: str = " ", + bar_template: str = "%(bar)s", + info_sep: str = " ", + hidden: bool = False, + show_eta: bool = True, + show_percent: bool | None = None, + show_pos: bool = False, + item_show_func: t.Callable[[V | None], str | None] | None = None, + label: str | None = None, + file: t.TextIO | None = None, + color: bool | None = None, + update_min_steps: int = 1, + width: int = 30, + ) -> None: + self.fill_char = fill_char + self.empty_char = empty_char + self.bar_template = bar_template + self.info_sep = info_sep + self.hidden = hidden + self.show_eta = show_eta + self.show_percent = show_percent + self.show_pos = show_pos + self.item_show_func = item_show_func + self.label: str = label or "" + + if file is None: + file = _default_text_stdout() + + # There are no standard streams attached to write to. For example, + # pythonw on Windows. + if file is None: + file = StringIO() + + self.file = file + self.color = color + self.update_min_steps = update_min_steps + self._completed_intervals = 0 + self.width: int = width + self.autowidth: bool = width == 0 + + if length is None: + from operator import length_hint + + length = length_hint(iterable, -1) + + if length == -1: + length = None + if iterable is None: + if length is None: + raise TypeError("iterable or length is required") + iterable = t.cast("cabc.Iterable[V]", range(length)) + self.iter: cabc.Iterable[V] = iter(iterable) + self.length = length + self.pos: int = 0 + self.avg: list[float] = [] + self.last_eta: float + self.start: float + self.start = self.last_eta = time.time() + self.eta_known: bool = False + self.finished: bool = False + self.max_width: int | None = None + self.entered: bool = False + self.current_item: V | None = None + self._is_atty = isatty(self.file) + self._last_line: str | None = None + + def __enter__(self) -> ProgressBar[V]: + self.entered = True + self.render_progress() + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_value: BaseException | None, + tb: TracebackType | None, + ) -> None: + self.render_finish() + + def __iter__(self) -> cabc.Iterator[V]: + if not self.entered: + raise RuntimeError("You need to use progress bars in a with block.") + self.render_progress() + return self.generator() + + def __next__(self) -> V: + # Iteration is defined in terms of a generator function, + # returned by iter(self); use that to define next(). This works + # because `self.iter` is an iterable consumed by that generator, + # so it is re-entry safe. Calling `next(self.generator())` + # twice works and does "what you want". + return next(iter(self)) + + def render_finish(self) -> None: + if self.hidden or not self._is_atty: + return + self.file.write(AFTER_BAR) + self.file.flush() + + @property + def pct(self) -> float: + if self.finished: + return 1.0 + return min(self.pos / (float(self.length or 1) or 1), 1.0) + + @property + def time_per_iteration(self) -> float: + if not self.avg: + return 0.0 + return sum(self.avg) / float(len(self.avg)) + + @property + def eta(self) -> float: + if self.length is not None and not self.finished: + return self.time_per_iteration * (self.length - self.pos) + return 0.0 + + def format_eta(self) -> str: + if self.eta_known: + t = int(self.eta) + seconds = t % 60 + t //= 60 + minutes = t % 60 + t //= 60 + hours = t % 24 + t //= 24 + if t > 0: + return f"{t}d {hours:02}:{minutes:02}:{seconds:02}" + else: + return f"{hours:02}:{minutes:02}:{seconds:02}" + return "" + + def format_pos(self) -> str: + pos = str(self.pos) + if self.length is not None: + pos += f"/{self.length}" + return pos + + def format_pct(self) -> str: + return f"{int(self.pct * 100): 4}%"[1:] + + def format_bar(self) -> str: + if self.length is not None: + bar_length = int(self.pct * self.width) + bar = self.fill_char * bar_length + bar += self.empty_char * (self.width - bar_length) + elif self.finished: + bar = self.fill_char * self.width + else: + chars = list(self.empty_char * (self.width or 1)) + if self.time_per_iteration != 0: + chars[ + int( + (math.cos(self.pos * self.time_per_iteration) / 2.0 + 0.5) + * self.width + ) + ] = self.fill_char + bar = "".join(chars) + return bar + + def format_progress_line(self) -> str: + show_percent = self.show_percent + + info_bits = [] + if self.length is not None and show_percent is None: + show_percent = not self.show_pos + + if self.show_pos: + info_bits.append(self.format_pos()) + if show_percent: + info_bits.append(self.format_pct()) + if self.show_eta and self.eta_known and not self.finished: + info_bits.append(self.format_eta()) + if self.item_show_func is not None: + item_info = self.item_show_func(self.current_item) + if item_info is not None: + info_bits.append(item_info) + + return ( + self.bar_template + % { + "label": self.label, + "bar": self.format_bar(), + "info": self.info_sep.join(info_bits), + } + ).rstrip() + + def render_progress(self) -> None: + import shutil + + if self.hidden: + return + + if not self._is_atty: + # Only output the label once if the output is not a TTY. + if self._last_line != self.label: + self._last_line = self.label + echo(self.label, file=self.file, color=self.color) + return + + buf = [] + # Update width in case the terminal has been resized + if self.autowidth: + old_width = self.width + self.width = 0 + clutter_length = term_len(self.format_progress_line()) + new_width = max(0, shutil.get_terminal_size().columns - clutter_length) + if new_width < old_width and self.max_width is not None: + buf.append(BEFORE_BAR) + buf.append(" " * self.max_width) + self.max_width = new_width + self.width = new_width + + clear_width = self.width + if self.max_width is not None: + clear_width = self.max_width + + buf.append(BEFORE_BAR) + line = self.format_progress_line() + line_len = term_len(line) + if self.max_width is None or self.max_width < line_len: + self.max_width = line_len + + buf.append(line) + buf.append(" " * (clear_width - line_len)) + line = "".join(buf) + # Render the line only if it changed. + + if line != self._last_line: + self._last_line = line + echo(line, file=self.file, color=self.color, nl=False) + self.file.flush() + + def make_step(self, n_steps: int) -> None: + self.pos += n_steps + if self.length is not None and self.pos >= self.length: + self.finished = True + + if (time.time() - self.last_eta) < 1.0: + return + + self.last_eta = time.time() + + # self.avg is a rolling list of length <= 7 of steps where steps are + # defined as time elapsed divided by the total progress through + # self.length. + if self.pos: + step = (time.time() - self.start) / self.pos + else: + step = time.time() - self.start + + self.avg = self.avg[-6:] + [step] + + self.eta_known = self.length is not None + + def update(self, n_steps: int, current_item: V | None = None) -> None: + """Update the progress bar by advancing a specified number of + steps, and optionally set the ``current_item`` for this new + position. + + :param n_steps: Number of steps to advance. + :param current_item: Optional item to set as ``current_item`` + for the updated position. + + .. versionchanged:: 8.0 + Added the ``current_item`` optional parameter. + + .. versionchanged:: 8.0 + Only render when the number of steps meets the + ``update_min_steps`` threshold. + """ + if current_item is not None: + self.current_item = current_item + + self._completed_intervals += n_steps + + if self._completed_intervals >= self.update_min_steps: + self.make_step(self._completed_intervals) + self.render_progress() + self._completed_intervals = 0 + + def finish(self) -> None: + self.eta_known = False + self.current_item = None + self.finished = True + + def generator(self) -> cabc.Iterator[V]: + """Return a generator which yields the items added to the bar + during construction, and updates the progress bar *after* the + yielded block returns. + """ + # WARNING: the iterator interface for `ProgressBar` relies on + # this and only works because this is a simple generator which + # doesn't create or manage additional state. If this function + # changes, the impact should be evaluated both against + # `iter(bar)` and `next(bar)`. `next()` in particular may call + # `self.generator()` repeatedly, and this must remain safe in + # order for that interface to work. + if not self.entered: + raise RuntimeError("You need to use progress bars in a with block.") + + if not self._is_atty: + yield from self.iter + else: + for rv in self.iter: + self.current_item = rv + + # This allows show_item_func to be updated before the + # item is processed. Only trigger at the beginning of + # the update interval. + if self._completed_intervals == 0: + self.render_progress() + + yield rv + self.update(1) + + self.finish() + self.render_progress() + + +def pager(generator: cabc.Iterable[str], color: bool | None = None) -> None: + """Decide what method to use for paging through text.""" + stdout = _default_text_stdout() + + # There are no standard streams attached to write to. For example, + # pythonw on Windows. + if stdout is None: + stdout = StringIO() + + if not isatty(sys.stdin) or not isatty(stdout): + return _nullpager(stdout, generator, color) + + # Split and normalize the pager command into parts. + pager_cmd_parts = shlex.split(os.environ.get("PAGER", ""), posix=False) + if pager_cmd_parts: + if WIN: + if _tempfilepager(generator, pager_cmd_parts, color): + return + elif _pipepager(generator, pager_cmd_parts, color): + return + + if os.environ.get("TERM") in ("dumb", "emacs"): + return _nullpager(stdout, generator, color) + if (WIN or sys.platform.startswith("os2")) and _tempfilepager( + generator, ["more"], color + ): + return + if _pipepager(generator, ["less"], color): + return + + import tempfile + + fd, filename = tempfile.mkstemp() + os.close(fd) + try: + if _pipepager(generator, ["more"], color): + return + return _nullpager(stdout, generator, color) + finally: + os.unlink(filename) + + +def _pipepager( + generator: cabc.Iterable[str], cmd_parts: list[str], color: bool | None +) -> bool: + """Page through text by feeding it to another program. Invoking a + pager through this might support colors. + + Returns `True` if the command was found, `False` otherwise and thus another + pager should be attempted. + """ + # Split the command into the invoked CLI and its parameters. + if not cmd_parts: + return False + cmd = cmd_parts[0] + cmd_params = cmd_parts[1:] + + cmd_filepath = which(cmd) + if not cmd_filepath: + return False + # Resolves symlinks and produces a normalized absolute path string. + cmd_path = Path(cmd_filepath).resolve() + cmd_name = cmd_path.name + + import subprocess + + # Make a local copy of the environment to not affect the global one. + env = dict(os.environ) + + # If we're piping to less and the user hasn't decided on colors, we enable + # them by default we find the -R flag in the command line arguments. + if color is None and cmd_name == "less": + less_flags = f"{os.environ.get('LESS', '')}{' '.join(cmd_params)}" + if not less_flags: + env["LESS"] = "-R" + color = True + elif "r" in less_flags or "R" in less_flags: + color = True + + c = subprocess.Popen( + [str(cmd_path)] + cmd_params, + shell=True, + stdin=subprocess.PIPE, + env=env, + errors="replace", + text=True, + ) + assert c.stdin is not None + try: + for text in generator: + if not color: + text = strip_ansi(text) + + c.stdin.write(text) + except BrokenPipeError: + # In case the pager exited unexpectedly, ignore the broken pipe error. + pass + except Exception as e: + # In case there is an exception we want to close the pager immediately + # and let the caller handle it. + # Otherwise the pager will keep running, and the user may not notice + # the error message, or worse yet it may leave the terminal in a broken state. + c.terminate() + raise e + finally: + # We must close stdin and wait for the pager to exit before we continue + try: + c.stdin.close() + # Close implies flush, so it might throw a BrokenPipeError if the pager + # process exited already. + except BrokenPipeError: + pass + + # Less doesn't respect ^C, but catches it for its own UI purposes (aborting + # search or other commands inside less). + # + # That means when the user hits ^C, the parent process (click) terminates, + # but less is still alive, paging the output and messing up the terminal. + # + # If the user wants to make the pager exit on ^C, they should set + # `LESS='-K'`. It's not our decision to make. + while True: + try: + c.wait() + except KeyboardInterrupt: + pass + else: + break + + return True + + +def _tempfilepager( + generator: cabc.Iterable[str], cmd_parts: list[str], color: bool | None +) -> bool: + """Page through text by invoking a program on a temporary file. + + Returns `True` if the command was found, `False` otherwise and thus another + pager should be attempted. + """ + # Split the command into the invoked CLI and its parameters. + if not cmd_parts: + return False + cmd = cmd_parts[0] + + cmd_filepath = which(cmd) + if not cmd_filepath: + return False + # Resolves symlinks and produces a normalized absolute path string. + cmd_path = Path(cmd_filepath).resolve() + + import subprocess + import tempfile + + fd, filename = tempfile.mkstemp() + # TODO: This never terminates if the passed generator never terminates. + text = "".join(generator) + if not color: + text = strip_ansi(text) + encoding = get_best_encoding(sys.stdout) + with open_stream(filename, "wb")[0] as f: + f.write(text.encode(encoding)) + try: + subprocess.call([str(cmd_path), filename]) + except OSError: + # Command not found + pass + finally: + os.close(fd) + os.unlink(filename) + + return True + + +def _nullpager( + stream: t.TextIO, generator: cabc.Iterable[str], color: bool | None +) -> None: + """Simply print unformatted text. This is the ultimate fallback.""" + for text in generator: + if not color: + text = strip_ansi(text) + stream.write(text) + + +class Editor: + def __init__( + self, + editor: str | None = None, + env: cabc.Mapping[str, str] | None = None, + require_save: bool = True, + extension: str = ".txt", + ) -> None: + self.editor = editor + self.env = env + self.require_save = require_save + self.extension = extension + + def get_editor(self) -> str: + if self.editor is not None: + return self.editor + for key in "VISUAL", "EDITOR": + rv = os.environ.get(key) + if rv: + return rv + if WIN: + return "notepad" + for editor in "sensible-editor", "vim", "nano": + if which(editor) is not None: + return editor + return "vi" + + def edit_files(self, filenames: cabc.Iterable[str]) -> None: + import subprocess + + editor = self.get_editor() + environ: dict[str, str] | None = None + + if self.env: + environ = os.environ.copy() + environ.update(self.env) + + exc_filename = " ".join(f'"{filename}"' for filename in filenames) + + try: + c = subprocess.Popen( + args=f"{editor} {exc_filename}", env=environ, shell=True + ) + exit_code = c.wait() + if exit_code != 0: + raise ClickException( + _("{editor}: Editing failed").format(editor=editor) + ) + except OSError as e: + raise ClickException( + _("{editor}: Editing failed: {e}").format(editor=editor, e=e) + ) from e + + @t.overload + def edit(self, text: bytes | bytearray) -> bytes | None: ... + + # We cannot know whether or not the type expected is str or bytes when None + # is passed, so str is returned as that was what was done before. + @t.overload + def edit(self, text: str | None) -> str | None: ... + + def edit(self, text: str | bytes | bytearray | None) -> str | bytes | None: + import tempfile + + if text is None: + data = b"" + elif isinstance(text, (bytes, bytearray)): + data = text + else: + if text and not text.endswith("\n"): + text += "\n" + + if WIN: + data = text.replace("\n", "\r\n").encode("utf-8-sig") + else: + data = text.encode("utf-8") + + fd, name = tempfile.mkstemp(prefix="editor-", suffix=self.extension) + f: t.BinaryIO + + try: + with os.fdopen(fd, "wb") as f: + f.write(data) + + # If the filesystem resolution is 1 second, like Mac OS + # 10.12 Extended, or 2 seconds, like FAT32, and the editor + # closes very fast, require_save can fail. Set the modified + # time to be 2 seconds in the past to work around this. + os.utime(name, (os.path.getatime(name), os.path.getmtime(name) - 2)) + # Depending on the resolution, the exact value might not be + # recorded, so get the new recorded value. + timestamp = os.path.getmtime(name) + + self.edit_files((name,)) + + if self.require_save and os.path.getmtime(name) == timestamp: + return None + + with open(name, "rb") as f: + rv = f.read() + + if isinstance(text, (bytes, bytearray)): + return rv + + return rv.decode("utf-8-sig").replace("\r\n", "\n") + finally: + os.unlink(name) + + +def open_url(url: str, wait: bool = False, locate: bool = False) -> int: + import subprocess + + def _unquote_file(url: str) -> str: + from urllib.parse import unquote + + if url.startswith("file://"): + url = unquote(url[7:]) + + return url + + if sys.platform == "darwin": + args = ["open"] + if wait: + args.append("-W") + if locate: + args.append("-R") + args.append(_unquote_file(url)) + null = open("/dev/null", "w") + try: + return subprocess.Popen(args, stderr=null).wait() + finally: + null.close() + elif WIN: + if locate: + url = _unquote_file(url) + args = ["explorer", f"/select,{url}"] + else: + args = ["start"] + if wait: + args.append("/WAIT") + args.append("") + args.append(url) + try: + return subprocess.call(args) + except OSError: + # Command not found + return 127 + elif CYGWIN: + if locate: + url = _unquote_file(url) + args = ["cygstart", os.path.dirname(url)] + else: + args = ["cygstart"] + if wait: + args.append("-w") + args.append(url) + try: + return subprocess.call(args) + except OSError: + # Command not found + return 127 + + try: + if locate: + url = os.path.dirname(_unquote_file(url)) or "." + else: + url = _unquote_file(url) + c = subprocess.Popen(["xdg-open", url]) + if wait: + return c.wait() + return 0 + except OSError: + if url.startswith(("http://", "https://")) and not locate and not wait: + import webbrowser + + webbrowser.open(url) + return 0 + return 1 + + +def _translate_ch_to_exc(ch: str) -> None: + if ch == "\x03": + raise KeyboardInterrupt() + + if ch == "\x04" and not WIN: # Unix-like, Ctrl+D + raise EOFError() + + if ch == "\x1a" and WIN: # Windows, Ctrl+Z + raise EOFError() + + return None + + +if sys.platform == "win32": + import msvcrt + + @contextlib.contextmanager + def raw_terminal() -> cabc.Iterator[int]: + yield -1 + + def getchar(echo: bool) -> str: + # The function `getch` will return a bytes object corresponding to + # the pressed character. Since Windows 10 build 1803, it will also + # return \x00 when called a second time after pressing a regular key. + # + # `getwch` does not share this probably-bugged behavior. Moreover, it + # returns a Unicode object by default, which is what we want. + # + # Either of these functions will return \x00 or \xe0 to indicate + # a special key, and you need to call the same function again to get + # the "rest" of the code. The fun part is that \u00e0 is + # "latin small letter a with grave", so if you type that on a French + # keyboard, you _also_ get a \xe0. + # E.g., consider the Up arrow. This returns \xe0 and then \x48. The + # resulting Unicode string reads as "a with grave" + "capital H". + # This is indistinguishable from when the user actually types + # "a with grave" and then "capital H". + # + # When \xe0 is returned, we assume it's part of a special-key sequence + # and call `getwch` again, but that means that when the user types + # the \u00e0 character, `getchar` doesn't return until a second + # character is typed. + # The alternative is returning immediately, but that would mess up + # cross-platform handling of arrow keys and others that start with + # \xe0. Another option is using `getch`, but then we can't reliably + # read non-ASCII characters, because return values of `getch` are + # limited to the current 8-bit codepage. + # + # Anyway, Click doesn't claim to do this Right(tm), and using `getwch` + # is doing the right thing in more situations than with `getch`. + + if echo: + func = t.cast(t.Callable[[], str], msvcrt.getwche) + else: + func = t.cast(t.Callable[[], str], msvcrt.getwch) + + rv = func() + + if rv in ("\x00", "\xe0"): + # \x00 and \xe0 are control characters that indicate special key, + # see above. + rv += func() + + _translate_ch_to_exc(rv) + return rv + +else: + import termios + import tty + + @contextlib.contextmanager + def raw_terminal() -> cabc.Iterator[int]: + f: t.TextIO | None + fd: int + + if not isatty(sys.stdin): + f = open("/dev/tty") + fd = f.fileno() + else: + fd = sys.stdin.fileno() + f = None + + try: + old_settings = termios.tcgetattr(fd) + + try: + tty.setraw(fd) + yield fd + finally: + termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) + sys.stdout.flush() + + if f is not None: + f.close() + except termios.error: + pass + + def getchar(echo: bool) -> str: + with raw_terminal() as fd: + ch = os.read(fd, 32).decode(get_best_encoding(sys.stdin), "replace") + + if echo and isatty(sys.stdout): + sys.stdout.write(ch) + + _translate_ch_to_exc(ch) + return ch diff --git a/lib/python3.12/site-packages/click/_textwrap.py b/lib/python3.12/site-packages/click/_textwrap.py new file mode 100644 index 0000000000000000000000000000000000000000..97fbee3dc6ce372600e702d38d9139bcf6a40bec --- /dev/null +++ b/lib/python3.12/site-packages/click/_textwrap.py @@ -0,0 +1,51 @@ +from __future__ import annotations + +import collections.abc as cabc +import textwrap +from contextlib import contextmanager + + +class TextWrapper(textwrap.TextWrapper): + def _handle_long_word( + self, + reversed_chunks: list[str], + cur_line: list[str], + cur_len: int, + width: int, + ) -> None: + space_left = max(width - cur_len, 1) + + if self.break_long_words: + last = reversed_chunks[-1] + cut = last[:space_left] + res = last[space_left:] + cur_line.append(cut) + reversed_chunks[-1] = res + elif not cur_line: + cur_line.append(reversed_chunks.pop()) + + @contextmanager + def extra_indent(self, indent: str) -> cabc.Iterator[None]: + old_initial_indent = self.initial_indent + old_subsequent_indent = self.subsequent_indent + self.initial_indent += indent + self.subsequent_indent += indent + + try: + yield + finally: + self.initial_indent = old_initial_indent + self.subsequent_indent = old_subsequent_indent + + def indent_only(self, text: str) -> str: + rv = [] + + for idx, line in enumerate(text.splitlines()): + indent = self.initial_indent + + if idx > 0: + indent = self.subsequent_indent + + rv.append(f"{indent}{line}") + + return "\n".join(rv) diff --git a/lib/python3.12/site-packages/click/_winconsole.py b/lib/python3.12/site-packages/click/_winconsole.py new file mode 100644 index 0000000000000000000000000000000000000000..e56c7c6ae7ee7e577db6ace659cd58c0c652074c --- /dev/null +++ b/lib/python3.12/site-packages/click/_winconsole.py @@ -0,0 +1,296 @@ +# This module is based on the excellent work by Adam Bartoš who +# provided a lot of what went into the implementation here in +# the discussion to issue1602 in the Python bug tracker. +# +# There are some general differences in regards to how this works +# compared to the original patches as we do not need to patch +# the entire interpreter but just work in our little world of +# echo and prompt. +from __future__ import annotations + +import collections.abc as cabc +import io +import sys +import time +import typing as t +from ctypes import Array +from ctypes import byref +from ctypes import c_char +from ctypes import c_char_p +from ctypes import c_int +from ctypes import c_ssize_t +from ctypes import c_ulong +from ctypes import c_void_p +from ctypes import POINTER +from ctypes import py_object +from ctypes import Structure +from ctypes.wintypes import DWORD +from ctypes.wintypes import HANDLE +from ctypes.wintypes import LPCWSTR +from ctypes.wintypes import LPWSTR + +from ._compat import _NonClosingTextIOWrapper + +assert sys.platform == "win32" +import msvcrt # noqa: E402 +from ctypes import windll # noqa: E402 +from ctypes import WINFUNCTYPE # noqa: E402 + +c_ssize_p = POINTER(c_ssize_t) + +kernel32 = windll.kernel32 +GetStdHandle = kernel32.GetStdHandle +ReadConsoleW = kernel32.ReadConsoleW +WriteConsoleW = kernel32.WriteConsoleW +GetConsoleMode = kernel32.GetConsoleMode +GetLastError = kernel32.GetLastError +GetCommandLineW = WINFUNCTYPE(LPWSTR)(("GetCommandLineW", windll.kernel32)) +CommandLineToArgvW = WINFUNCTYPE(POINTER(LPWSTR), LPCWSTR, POINTER(c_int))( + ("CommandLineToArgvW", windll.shell32) +) +LocalFree = WINFUNCTYPE(c_void_p, c_void_p)(("LocalFree", windll.kernel32)) + +STDIN_HANDLE = GetStdHandle(-10) +STDOUT_HANDLE = GetStdHandle(-11) +STDERR_HANDLE = GetStdHandle(-12) + +PyBUF_SIMPLE = 0 +PyBUF_WRITABLE = 1 + +ERROR_SUCCESS = 0 +ERROR_NOT_ENOUGH_MEMORY = 8 +ERROR_OPERATION_ABORTED = 995 + +STDIN_FILENO = 0 +STDOUT_FILENO = 1 +STDERR_FILENO = 2 + +EOF = b"\x1a" +MAX_BYTES_WRITTEN = 32767 + +if t.TYPE_CHECKING: + try: + # Using `typing_extensions.Buffer` instead of `collections.abc` + # on Windows for some reason does not have `Sized` implemented. + from collections.abc import Buffer # type: ignore + except ImportError: + from typing_extensions import Buffer + +try: + from ctypes import pythonapi +except ImportError: + # On PyPy we cannot get buffers so our ability to operate here is + # severely limited. + get_buffer = None +else: + + class Py_buffer(Structure): + _fields_ = [ # noqa: RUF012 + ("buf", c_void_p), + ("obj", py_object), + ("len", c_ssize_t), + ("itemsize", c_ssize_t), + ("readonly", c_int), + ("ndim", c_int), + ("format", c_char_p), + ("shape", c_ssize_p), + ("strides", c_ssize_p), + ("suboffsets", c_ssize_p), + ("internal", c_void_p), + ] + + PyObject_GetBuffer = pythonapi.PyObject_GetBuffer + PyBuffer_Release = pythonapi.PyBuffer_Release + + def get_buffer(obj: Buffer, writable: bool = False) -> Array[c_char]: + buf = Py_buffer() + flags: int = PyBUF_WRITABLE if writable else PyBUF_SIMPLE + PyObject_GetBuffer(py_object(obj), byref(buf), flags) + + try: + buffer_type = c_char * buf.len + out: Array[c_char] = buffer_type.from_address(buf.buf) + return out + finally: + PyBuffer_Release(byref(buf)) + + +class _WindowsConsoleRawIOBase(io.RawIOBase): + def __init__(self, handle: int | None) -> None: + self.handle = handle + + def isatty(self) -> t.Literal[True]: + super().isatty() + return True + + +class _WindowsConsoleReader(_WindowsConsoleRawIOBase): + def readable(self) -> t.Literal[True]: + return True + + def readinto(self, b: Buffer) -> int: + bytes_to_be_read = len(b) + if not bytes_to_be_read: + return 0 + elif bytes_to_be_read % 2: + raise ValueError( + "cannot read odd number of bytes from UTF-16-LE encoded console" + ) + + buffer = get_buffer(b, writable=True) + code_units_to_be_read = bytes_to_be_read // 2 + code_units_read = c_ulong() + + rv = ReadConsoleW( + HANDLE(self.handle), + buffer, + code_units_to_be_read, + byref(code_units_read), + None, + ) + if GetLastError() == ERROR_OPERATION_ABORTED: + # wait for KeyboardInterrupt + time.sleep(0.1) + if not rv: + raise OSError(f"Windows error: {GetLastError()}") + + if buffer[0] == EOF: + return 0 + return 2 * code_units_read.value + + +class _WindowsConsoleWriter(_WindowsConsoleRawIOBase): + def writable(self) -> t.Literal[True]: + return True + + @staticmethod + def _get_error_message(errno: int) -> str: + if errno == ERROR_SUCCESS: + return "ERROR_SUCCESS" + elif errno == ERROR_NOT_ENOUGH_MEMORY: + return "ERROR_NOT_ENOUGH_MEMORY" + return f"Windows error {errno}" + + def write(self, b: Buffer) -> int: + bytes_to_be_written = len(b) + buf = get_buffer(b) + code_units_to_be_written = min(bytes_to_be_written, MAX_BYTES_WRITTEN) // 2 + code_units_written = c_ulong() + + WriteConsoleW( + HANDLE(self.handle), + buf, + code_units_to_be_written, + byref(code_units_written), + None, + ) + bytes_written = 2 * code_units_written.value + + if bytes_written == 0 and bytes_to_be_written > 0: + raise OSError(self._get_error_message(GetLastError())) + return bytes_written + + +class ConsoleStream: + def __init__(self, text_stream: t.TextIO, byte_stream: t.BinaryIO) -> None: + self._text_stream = text_stream + self.buffer = byte_stream + + @property + def name(self) -> str: + return self.buffer.name + + def write(self, x: t.AnyStr) -> int: + if isinstance(x, str): + return self._text_stream.write(x) + try: + self.flush() + except Exception: + pass + return self.buffer.write(x) + + def writelines(self, lines: cabc.Iterable[t.AnyStr]) -> None: + for line in lines: + self.write(line) + + def __getattr__(self, name: str) -> t.Any: + return getattr(self._text_stream, name) + + def isatty(self) -> bool: + return self.buffer.isatty() + + def __repr__(self) -> str: + return f"" + + +def _get_text_stdin(buffer_stream: t.BinaryIO) -> t.TextIO: + text_stream = _NonClosingTextIOWrapper( + io.BufferedReader(_WindowsConsoleReader(STDIN_HANDLE)), + "utf-16-le", + "strict", + line_buffering=True, + ) + return t.cast(t.TextIO, ConsoleStream(text_stream, buffer_stream)) + + +def _get_text_stdout(buffer_stream: t.BinaryIO) -> t.TextIO: + text_stream = _NonClosingTextIOWrapper( + io.BufferedWriter(_WindowsConsoleWriter(STDOUT_HANDLE)), + "utf-16-le", + "strict", + line_buffering=True, + ) + return t.cast(t.TextIO, ConsoleStream(text_stream, buffer_stream)) + + +def _get_text_stderr(buffer_stream: t.BinaryIO) -> t.TextIO: + text_stream = _NonClosingTextIOWrapper( + io.BufferedWriter(_WindowsConsoleWriter(STDERR_HANDLE)), + "utf-16-le", + "strict", + line_buffering=True, + ) + return t.cast(t.TextIO, ConsoleStream(text_stream, buffer_stream)) + + +_stream_factories: cabc.Mapping[int, t.Callable[[t.BinaryIO], t.TextIO]] = { + 0: _get_text_stdin, + 1: _get_text_stdout, + 2: _get_text_stderr, +} + + +def _is_console(f: t.TextIO) -> bool: + if not hasattr(f, "fileno"): + return False + + try: + fileno = f.fileno() + except (OSError, io.UnsupportedOperation): + return False + + handle = msvcrt.get_osfhandle(fileno) + return bool(GetConsoleMode(handle, byref(DWORD()))) + + +def _get_windows_console_stream( + f: t.TextIO, encoding: str | None, errors: str | None +) -> t.TextIO | None: + if ( + get_buffer is None + or encoding not in {"utf-16-le", None} + or errors not in {"strict", None} + or not _is_console(f) + ): + return None + + func = _stream_factories.get(f.fileno()) + if func is None: + return None + + b = getattr(f, "buffer", None) + + if b is None: + return None + + return func(b) diff --git a/lib/python3.12/site-packages/click/core.py b/lib/python3.12/site-packages/click/core.py new file mode 100644 index 0000000000000000000000000000000000000000..f57ada62b195fa533387b5d06b366b17edb7d2f4 --- /dev/null +++ b/lib/python3.12/site-packages/click/core.py @@ -0,0 +1,3135 @@ +from __future__ import annotations + +import collections.abc as cabc +import enum +import errno +import inspect +import os +import sys +import typing as t +from collections import abc +from collections import Counter +from contextlib import AbstractContextManager +from contextlib import contextmanager +from contextlib import ExitStack +from functools import update_wrapper +from gettext import gettext as _ +from gettext import ngettext +from itertools import repeat +from types import TracebackType + +from . import types +from .exceptions import Abort +from .exceptions import BadParameter +from .exceptions import ClickException +from .exceptions import Exit +from .exceptions import MissingParameter +from .exceptions import NoArgsIsHelpError +from .exceptions import UsageError +from .formatting import HelpFormatter +from .formatting import join_options +from .globals import pop_context +from .globals import push_context +from .parser import _flag_needs_value +from .parser import _OptionParser +from .parser import _split_opt +from .termui import confirm +from .termui import prompt +from .termui import style +from .utils import _detect_program_name +from .utils import _expand_args +from .utils import echo +from .utils import make_default_short_help +from .utils import make_str +from .utils import PacifyFlushWrapper + +if t.TYPE_CHECKING: + from .shell_completion import CompletionItem + +F = t.TypeVar("F", bound="t.Callable[..., t.Any]") +V = t.TypeVar("V") + + +def _complete_visible_commands( + ctx: Context, incomplete: str +) -> cabc.Iterator[tuple[str, Command]]: + """List all the subcommands of a group that start with the + incomplete value and aren't hidden. + + :param ctx: Invocation context for the group. + :param incomplete: Value being completed. May be empty. + """ + multi = t.cast(Group, ctx.command) + + for name in multi.list_commands(ctx): + if name.startswith(incomplete): + command = multi.get_command(ctx, name) + + if command is not None and not command.hidden: + yield name, command + + +def _check_nested_chain( + base_command: Group, cmd_name: str, cmd: Command, register: bool = False +) -> None: + if not base_command.chain or not isinstance(cmd, Group): + return + + if register: + message = ( + f"It is not possible to add the group {cmd_name!r} to another" + f" group {base_command.name!r} that is in chain mode." + ) + else: + message = ( + f"Found the group {cmd_name!r} as subcommand to another group " + f" {base_command.name!r} that is in chain mode. This is not supported." + ) + + raise RuntimeError(message) + + +def batch(iterable: cabc.Iterable[V], batch_size: int) -> list[tuple[V, ...]]: + return list(zip(*repeat(iter(iterable), batch_size), strict=False)) + + +@contextmanager +def augment_usage_errors( + ctx: Context, param: Parameter | None = None +) -> cabc.Iterator[None]: + """Context manager that attaches extra information to exceptions.""" + try: + yield + except BadParameter as e: + if e.ctx is None: + e.ctx = ctx + if param is not None and e.param is None: + e.param = param + raise + except UsageError as e: + if e.ctx is None: + e.ctx = ctx + raise + + +def iter_params_for_processing( + invocation_order: cabc.Sequence[Parameter], + declaration_order: cabc.Sequence[Parameter], +) -> list[Parameter]: + """Returns all declared parameters in the order they should be processed. + + The declared parameters are re-shuffled depending on the order in which + they were invoked, as well as the eagerness of each parameters. + + The invocation order takes precedence over the declaration order. I.e. the + order in which the user provided them to the CLI is respected. + + This behavior and its effect on callback evaluation is detailed at: + https://click.palletsprojects.com/en/stable/advanced/#callback-evaluation-order + """ + + def sort_key(item: Parameter) -> tuple[bool, float]: + try: + idx: float = invocation_order.index(item) + except ValueError: + idx = float("inf") + + return not item.is_eager, idx + + return sorted(declaration_order, key=sort_key) + + +class ParameterSource(enum.Enum): + """This is an :class:`~enum.Enum` that indicates the source of a + parameter's value. + + Use :meth:`click.Context.get_parameter_source` to get the + source for a parameter by name. + + .. versionchanged:: 8.0 + Use :class:`~enum.Enum` and drop the ``validate`` method. + + .. versionchanged:: 8.0 + Added the ``PROMPT`` value. + """ + + COMMANDLINE = enum.auto() + """The value was provided by the command line args.""" + ENVIRONMENT = enum.auto() + """The value was provided with an environment variable.""" + DEFAULT = enum.auto() + """Used the default specified by the parameter.""" + DEFAULT_MAP = enum.auto() + """Used a default provided by :attr:`Context.default_map`.""" + PROMPT = enum.auto() + """Used a prompt to confirm a default or provide a value.""" + + +class Context: + """The context is a special internal object that holds state relevant + for the script execution at every single level. It's normally invisible + to commands unless they opt-in to getting access to it. + + The context is useful as it can pass internal objects around and can + control special execution features such as reading data from + environment variables. + + A context can be used as context manager in which case it will call + :meth:`close` on teardown. + + :param command: the command class for this context. + :param parent: the parent context. + :param info_name: the info name for this invocation. Generally this + is the most descriptive name for the script or + command. For the toplevel script it is usually + the name of the script, for commands below it it's + the name of the script. + :param obj: an arbitrary object of user data. + :param auto_envvar_prefix: the prefix to use for automatic environment + variables. If this is `None` then reading + from environment variables is disabled. This + does not affect manually set environment + variables which are always read. + :param default_map: a dictionary (like object) with default values + for parameters. + :param terminal_width: the width of the terminal. The default is + inherit from parent context. If no context + defines the terminal width then auto + detection will be applied. + :param max_content_width: the maximum width for content rendered by + Click (this currently only affects help + pages). This defaults to 80 characters if + not overridden. In other words: even if the + terminal is larger than that, Click will not + format things wider than 80 characters by + default. In addition to that, formatters might + add some safety mapping on the right. + :param resilient_parsing: if this flag is enabled then Click will + parse without any interactivity or callback + invocation. Default values will also be + ignored. This is useful for implementing + things such as completion support. + :param allow_extra_args: if this is set to `True` then extra arguments + at the end will not raise an error and will be + kept on the context. The default is to inherit + from the command. + :param allow_interspersed_args: if this is set to `False` then options + and arguments cannot be mixed. The + default is to inherit from the command. + :param ignore_unknown_options: instructs click to ignore options it does + not know and keeps them for later + processing. + :param help_option_names: optionally a list of strings that define how + the default help parameter is named. The + default is ``['--help']``. + :param token_normalize_func: an optional function that is used to + normalize tokens (options, choices, + etc.). This for instance can be used to + implement case insensitive behavior. + :param color: controls if the terminal supports ANSI colors or not. The + default is autodetection. This is only needed if ANSI + codes are used in texts that Click prints which is by + default not the case. This for instance would affect + help output. + :param show_default: Show the default value for commands. If this + value is not set, it defaults to the value from the parent + context. ``Command.show_default`` overrides this default for the + specific command. + + .. versionchanged:: 8.2 + The ``protected_args`` attribute is deprecated and will be removed in + Click 9.0. ``args`` will contain remaining unparsed tokens. + + .. versionchanged:: 8.1 + The ``show_default`` parameter is overridden by + ``Command.show_default``, instead of the other way around. + + .. versionchanged:: 8.0 + The ``show_default`` parameter defaults to the value from the + parent context. + + .. versionchanged:: 7.1 + Added the ``show_default`` parameter. + + .. versionchanged:: 4.0 + Added the ``color``, ``ignore_unknown_options``, and + ``max_content_width`` parameters. + + .. versionchanged:: 3.0 + Added the ``allow_extra_args`` and ``allow_interspersed_args`` + parameters. + + .. versionchanged:: 2.0 + Added the ``resilient_parsing``, ``help_option_names``, and + ``token_normalize_func`` parameters. + """ + + #: The formatter class to create with :meth:`make_formatter`. + #: + #: .. versionadded:: 8.0 + formatter_class: type[HelpFormatter] = HelpFormatter + + def __init__( + self, + command: Command, + parent: Context | None = None, + info_name: str | None = None, + obj: t.Any | None = None, + auto_envvar_prefix: str | None = None, + default_map: cabc.MutableMapping[str, t.Any] | None = None, + terminal_width: int | None = None, + max_content_width: int | None = None, + resilient_parsing: bool = False, + allow_extra_args: bool | None = None, + allow_interspersed_args: bool | None = None, + ignore_unknown_options: bool | None = None, + help_option_names: list[str] | None = None, + token_normalize_func: t.Callable[[str], str] | None = None, + color: bool | None = None, + show_default: bool | None = None, + ) -> None: + #: the parent context or `None` if none exists. + self.parent = parent + #: the :class:`Command` for this context. + self.command = command + #: the descriptive information name + self.info_name = info_name + #: Map of parameter names to their parsed values. Parameters + #: with ``expose_value=False`` are not stored. + self.params: dict[str, t.Any] = {} + #: the leftover arguments. + self.args: list[str] = [] + #: protected arguments. These are arguments that are prepended + #: to `args` when certain parsing scenarios are encountered but + #: must be never propagated to another arguments. This is used + #: to implement nested parsing. + self._protected_args: list[str] = [] + #: the collected prefixes of the command's options. + self._opt_prefixes: set[str] = set(parent._opt_prefixes) if parent else set() + + if obj is None and parent is not None: + obj = parent.obj + + #: the user object stored. + self.obj: t.Any = obj + self._meta: dict[str, t.Any] = getattr(parent, "meta", {}) + + #: A dictionary (-like object) with defaults for parameters. + if ( + default_map is None + and info_name is not None + and parent is not None + and parent.default_map is not None + ): + default_map = parent.default_map.get(info_name) + + self.default_map: cabc.MutableMapping[str, t.Any] | None = default_map + + #: This flag indicates if a subcommand is going to be executed. A + #: group callback can use this information to figure out if it's + #: being executed directly or because the execution flow passes + #: onwards to a subcommand. By default it's None, but it can be + #: the name of the subcommand to execute. + #: + #: If chaining is enabled this will be set to ``'*'`` in case + #: any commands are executed. It is however not possible to + #: figure out which ones. If you require this knowledge you + #: should use a :func:`result_callback`. + self.invoked_subcommand: str | None = None + + if terminal_width is None and parent is not None: + terminal_width = parent.terminal_width + + #: The width of the terminal (None is autodetection). + self.terminal_width: int | None = terminal_width + + if max_content_width is None and parent is not None: + max_content_width = parent.max_content_width + + #: The maximum width of formatted content (None implies a sensible + #: default which is 80 for most things). + self.max_content_width: int | None = max_content_width + + if allow_extra_args is None: + allow_extra_args = command.allow_extra_args + + #: Indicates if the context allows extra args or if it should + #: fail on parsing. + #: + #: .. versionadded:: 3.0 + self.allow_extra_args = allow_extra_args + + if allow_interspersed_args is None: + allow_interspersed_args = command.allow_interspersed_args + + #: Indicates if the context allows mixing of arguments and + #: options or not. + #: + #: .. versionadded:: 3.0 + self.allow_interspersed_args: bool = allow_interspersed_args + + if ignore_unknown_options is None: + ignore_unknown_options = command.ignore_unknown_options + + #: Instructs click to ignore options that a command does not + #: understand and will store it on the context for later + #: processing. This is primarily useful for situations where you + #: want to call into external programs. Generally this pattern is + #: strongly discouraged because it's not possibly to losslessly + #: forward all arguments. + #: + #: .. versionadded:: 4.0 + self.ignore_unknown_options: bool = ignore_unknown_options + + if help_option_names is None: + if parent is not None: + help_option_names = parent.help_option_names + else: + help_option_names = ["--help"] + + #: The names for the help options. + self.help_option_names: list[str] = help_option_names + + if token_normalize_func is None and parent is not None: + token_normalize_func = parent.token_normalize_func + + #: An optional normalization function for tokens. This is + #: options, choices, commands etc. + self.token_normalize_func: t.Callable[[str], str] | None = token_normalize_func + + #: Indicates if resilient parsing is enabled. In that case Click + #: will do its best to not cause any failures and default values + #: will be ignored. Useful for completion. + self.resilient_parsing: bool = resilient_parsing + + # If there is no envvar prefix yet, but the parent has one and + # the command on this level has a name, we can expand the envvar + # prefix automatically. + if auto_envvar_prefix is None: + if ( + parent is not None + and parent.auto_envvar_prefix is not None + and self.info_name is not None + ): + auto_envvar_prefix = ( + f"{parent.auto_envvar_prefix}_{self.info_name.upper()}" + ) + else: + auto_envvar_prefix = auto_envvar_prefix.upper() + + if auto_envvar_prefix is not None: + auto_envvar_prefix = auto_envvar_prefix.replace("-", "_") + + self.auto_envvar_prefix: str | None = auto_envvar_prefix + + if color is None and parent is not None: + color = parent.color + + #: Controls if styling output is wanted or not. + self.color: bool | None = color + + if show_default is None and parent is not None: + show_default = parent.show_default + + #: Show option default values when formatting help text. + self.show_default: bool | None = show_default + + self._close_callbacks: list[t.Callable[[], t.Any]] = [] + self._depth = 0 + self._parameter_source: dict[str, ParameterSource] = {} + self._exit_stack = ExitStack() + + @property + def protected_args(self) -> list[str]: + import warnings + + warnings.warn( + "'protected_args' is deprecated and will be removed in Click 9.0." + " 'args' will contain remaining unparsed tokens.", + DeprecationWarning, + stacklevel=2, + ) + return self._protected_args + + def to_info_dict(self) -> dict[str, t.Any]: + """Gather information that could be useful for a tool generating + user-facing documentation. This traverses the entire CLI + structure. + + .. code-block:: python + + with Context(cli) as ctx: + info = ctx.to_info_dict() + + .. versionadded:: 8.0 + """ + return { + "command": self.command.to_info_dict(self), + "info_name": self.info_name, + "allow_extra_args": self.allow_extra_args, + "allow_interspersed_args": self.allow_interspersed_args, + "ignore_unknown_options": self.ignore_unknown_options, + "auto_envvar_prefix": self.auto_envvar_prefix, + } + + def __enter__(self) -> Context: + self._depth += 1 + push_context(self) + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_value: BaseException | None, + tb: TracebackType | None, + ) -> None: + self._depth -= 1 + if self._depth == 0: + self.close() + pop_context() + + @contextmanager + def scope(self, cleanup: bool = True) -> cabc.Iterator[Context]: + """This helper method can be used with the context object to promote + it to the current thread local (see :func:`get_current_context`). + The default behavior of this is to invoke the cleanup functions which + can be disabled by setting `cleanup` to `False`. The cleanup + functions are typically used for things such as closing file handles. + + If the cleanup is intended the context object can also be directly + used as a context manager. + + Example usage:: + + with ctx.scope(): + assert get_current_context() is ctx + + This is equivalent:: + + with ctx: + assert get_current_context() is ctx + + .. versionadded:: 5.0 + + :param cleanup: controls if the cleanup functions should be run or + not. The default is to run these functions. In + some situations the context only wants to be + temporarily pushed in which case this can be disabled. + Nested pushes automatically defer the cleanup. + """ + if not cleanup: + self._depth += 1 + try: + with self as rv: + yield rv + finally: + if not cleanup: + self._depth -= 1 + + @property + def meta(self) -> dict[str, t.Any]: + """This is a dictionary which is shared with all the contexts + that are nested. It exists so that click utilities can store some + state here if they need to. It is however the responsibility of + that code to manage this dictionary well. + + The keys are supposed to be unique dotted strings. For instance + module paths are a good choice for it. What is stored in there is + irrelevant for the operation of click. However what is important is + that code that places data here adheres to the general semantics of + the system. + + Example usage:: + + LANG_KEY = f'{__name__}.lang' + + def set_language(value): + ctx = get_current_context() + ctx.meta[LANG_KEY] = value + + def get_language(): + return get_current_context().meta.get(LANG_KEY, 'en_US') + + .. versionadded:: 5.0 + """ + return self._meta + + def make_formatter(self) -> HelpFormatter: + """Creates the :class:`~click.HelpFormatter` for the help and + usage output. + + To quickly customize the formatter class used without overriding + this method, set the :attr:`formatter_class` attribute. + + .. versionchanged:: 8.0 + Added the :attr:`formatter_class` attribute. + """ + return self.formatter_class( + width=self.terminal_width, max_width=self.max_content_width + ) + + def with_resource(self, context_manager: AbstractContextManager[V]) -> V: + """Register a resource as if it were used in a ``with`` + statement. The resource will be cleaned up when the context is + popped. + + Uses :meth:`contextlib.ExitStack.enter_context`. It calls the + resource's ``__enter__()`` method and returns the result. When + the context is popped, it closes the stack, which calls the + resource's ``__exit__()`` method. + + To register a cleanup function for something that isn't a + context manager, use :meth:`call_on_close`. Or use something + from :mod:`contextlib` to turn it into a context manager first. + + .. code-block:: python + + @click.group() + @click.option("--name") + @click.pass_context + def cli(ctx): + ctx.obj = ctx.with_resource(connect_db(name)) + + :param context_manager: The context manager to enter. + :return: Whatever ``context_manager.__enter__()`` returns. + + .. versionadded:: 8.0 + """ + return self._exit_stack.enter_context(context_manager) + + def call_on_close(self, f: t.Callable[..., t.Any]) -> t.Callable[..., t.Any]: + """Register a function to be called when the context tears down. + + This can be used to close resources opened during the script + execution. Resources that support Python's context manager + protocol which would be used in a ``with`` statement should be + registered with :meth:`with_resource` instead. + + :param f: The function to execute on teardown. + """ + return self._exit_stack.callback(f) + + def close(self) -> None: + """Invoke all close callbacks registered with + :meth:`call_on_close`, and exit all context managers entered + with :meth:`with_resource`. + """ + self._exit_stack.close() + # In case the context is reused, create a new exit stack. + self._exit_stack = ExitStack() + + @property + def command_path(self) -> str: + """The computed command path. This is used for the ``usage`` + information on the help page. It's automatically created by + combining the info names of the chain of contexts to the root. + """ + rv = "" + if self.info_name is not None: + rv = self.info_name + if self.parent is not None: + parent_command_path = [self.parent.command_path] + + if isinstance(self.parent.command, Command): + for param in self.parent.command.get_params(self): + parent_command_path.extend(param.get_usage_pieces(self)) + + rv = f"{' '.join(parent_command_path)} {rv}" + return rv.lstrip() + + def find_root(self) -> Context: + """Finds the outermost context.""" + node = self + while node.parent is not None: + node = node.parent + return node + + def find_object(self, object_type: type[V]) -> V | None: + """Finds the closest object of a given type.""" + node: Context | None = self + + while node is not None: + if isinstance(node.obj, object_type): + return node.obj + + node = node.parent + + return None + + def ensure_object(self, object_type: type[V]) -> V: + """Like :meth:`find_object` but sets the innermost object to a + new instance of `object_type` if it does not exist. + """ + rv = self.find_object(object_type) + if rv is None: + self.obj = rv = object_type() + return rv + + @t.overload + def lookup_default( + self, name: str, call: t.Literal[True] = True + ) -> t.Any | None: ... + + @t.overload + def lookup_default( + self, name: str, call: t.Literal[False] = ... + ) -> t.Any | t.Callable[[], t.Any] | None: ... + + def lookup_default(self, name: str, call: bool = True) -> t.Any | None: + """Get the default for a parameter from :attr:`default_map`. + + :param name: Name of the parameter. + :param call: If the default is a callable, call it. Disable to + return the callable instead. + + .. versionchanged:: 8.0 + Added the ``call`` parameter. + """ + if self.default_map is not None: + value = self.default_map.get(name) + + if call and callable(value): + return value() + + return value + + return None + + def fail(self, message: str) -> t.NoReturn: + """Aborts the execution of the program with a specific error + message. + + :param message: the error message to fail with. + """ + raise UsageError(message, self) + + def abort(self) -> t.NoReturn: + """Aborts the script.""" + raise Abort() + + def exit(self, code: int = 0) -> t.NoReturn: + """Exits the application with a given exit code. + + .. versionchanged:: 8.2 + Callbacks and context managers registered with :meth:`call_on_close` + and :meth:`with_resource` are closed before exiting. + """ + self.close() + raise Exit(code) + + def get_usage(self) -> str: + """Helper method to get formatted usage string for the current + context and command. + """ + return self.command.get_usage(self) + + def get_help(self) -> str: + """Helper method to get formatted help page for the current + context and command. + """ + return self.command.get_help(self) + + def _make_sub_context(self, command: Command) -> Context: + """Create a new context of the same type as this context, but + for a new command. + + :meta private: + """ + return type(self)(command, info_name=command.name, parent=self) + + @t.overload + def invoke( + self, callback: t.Callable[..., V], /, *args: t.Any, **kwargs: t.Any + ) -> V: ... + + @t.overload + def invoke(self, callback: Command, /, *args: t.Any, **kwargs: t.Any) -> t.Any: ... + + def invoke( + self, callback: Command | t.Callable[..., V], /, *args: t.Any, **kwargs: t.Any + ) -> t.Any | V: + """Invokes a command callback in exactly the way it expects. There + are two ways to invoke this method: + + 1. the first argument can be a callback and all other arguments and + keyword arguments are forwarded directly to the function. + 2. the first argument is a click command object. In that case all + arguments are forwarded as well but proper click parameters + (options and click arguments) must be keyword arguments and Click + will fill in defaults. + + .. versionchanged:: 8.0 + All ``kwargs`` are tracked in :attr:`params` so they will be + passed if :meth:`forward` is called at multiple levels. + + .. versionchanged:: 3.2 + A new context is created, and missing arguments use default values. + """ + if isinstance(callback, Command): + other_cmd = callback + + if other_cmd.callback is None: + raise TypeError( + "The given command does not have a callback that can be invoked." + ) + else: + callback = t.cast("t.Callable[..., V]", other_cmd.callback) + + ctx = self._make_sub_context(other_cmd) + + for param in other_cmd.params: + if param.name not in kwargs and param.expose_value: + kwargs[param.name] = param.type_cast_value( # type: ignore + ctx, param.get_default(ctx) + ) + + # Track all kwargs as params, so that forward() will pass + # them on in subsequent calls. + ctx.params.update(kwargs) + else: + ctx = self + + with augment_usage_errors(self): + with ctx: + return callback(*args, **kwargs) + + def forward(self, cmd: Command, /, *args: t.Any, **kwargs: t.Any) -> t.Any: + """Similar to :meth:`invoke` but fills in default keyword + arguments from the current context if the other command expects + it. This cannot invoke callbacks directly, only other commands. + + .. versionchanged:: 8.0 + All ``kwargs`` are tracked in :attr:`params` so they will be + passed if ``forward`` is called at multiple levels. + """ + # Can only forward to other commands, not direct callbacks. + if not isinstance(cmd, Command): + raise TypeError("Callback is not a command.") + + for param in self.params: + if param not in kwargs: + kwargs[param] = self.params[param] + + return self.invoke(cmd, *args, **kwargs) + + def set_parameter_source(self, name: str, source: ParameterSource) -> None: + """Set the source of a parameter. This indicates the location + from which the value of the parameter was obtained. + + :param name: The name of the parameter. + :param source: A member of :class:`~click.core.ParameterSource`. + """ + self._parameter_source[name] = source + + def get_parameter_source(self, name: str) -> ParameterSource | None: + """Get the source of a parameter. This indicates the location + from which the value of the parameter was obtained. + + This can be useful for determining when a user specified a value + on the command line that is the same as the default value. It + will be :attr:`~click.core.ParameterSource.DEFAULT` only if the + value was actually taken from the default. + + :param name: The name of the parameter. + :rtype: ParameterSource + + .. versionchanged:: 8.0 + Returns ``None`` if the parameter was not provided from any + source. + """ + return self._parameter_source.get(name) + + +class Command: + """Commands are the basic building block of command line interfaces in + Click. A basic command handles command line parsing and might dispatch + more parsing to commands nested below it. + + :param name: the name of the command to use unless a group overrides it. + :param context_settings: an optional dictionary with defaults that are + passed to the context object. + :param callback: the callback to invoke. This is optional. + :param params: the parameters to register with this command. This can + be either :class:`Option` or :class:`Argument` objects. + :param help: the help string to use for this command. + :param epilog: like the help string but it's printed at the end of the + help page after everything else. + :param short_help: the short help to use for this command. This is + shown on the command listing of the parent command. + :param add_help_option: by default each command registers a ``--help`` + option. This can be disabled by this parameter. + :param no_args_is_help: this controls what happens if no arguments are + provided. This option is disabled by default. + If enabled this will add ``--help`` as argument + if no arguments are passed + :param hidden: hide this command from help outputs. + :param deprecated: If ``True`` or non-empty string, issues a message + indicating that the command is deprecated and highlights + its deprecation in --help. The message can be customized + by using a string as the value. + + .. versionchanged:: 8.2 + This is the base class for all commands, not ``BaseCommand``. + ``deprecated`` can be set to a string as well to customize the + deprecation message. + + .. versionchanged:: 8.1 + ``help``, ``epilog``, and ``short_help`` are stored unprocessed, + all formatting is done when outputting help text, not at init, + and is done even if not using the ``@command`` decorator. + + .. versionchanged:: 8.0 + Added a ``repr`` showing the command name. + + .. versionchanged:: 7.1 + Added the ``no_args_is_help`` parameter. + + .. versionchanged:: 2.0 + Added the ``context_settings`` parameter. + """ + + #: The context class to create with :meth:`make_context`. + #: + #: .. versionadded:: 8.0 + context_class: type[Context] = Context + + #: the default for the :attr:`Context.allow_extra_args` flag. + allow_extra_args = False + + #: the default for the :attr:`Context.allow_interspersed_args` flag. + allow_interspersed_args = True + + #: the default for the :attr:`Context.ignore_unknown_options` flag. + ignore_unknown_options = False + + def __init__( + self, + name: str | None, + context_settings: cabc.MutableMapping[str, t.Any] | None = None, + callback: t.Callable[..., t.Any] | None = None, + params: list[Parameter] | None = None, + help: str | None = None, + epilog: str | None = None, + short_help: str | None = None, + options_metavar: str | None = "[OPTIONS]", + add_help_option: bool = True, + no_args_is_help: bool = False, + hidden: bool = False, + deprecated: bool | str = False, + ) -> None: + #: the name the command thinks it has. Upon registering a command + #: on a :class:`Group` the group will default the command name + #: with this information. You should instead use the + #: :class:`Context`\'s :attr:`~Context.info_name` attribute. + self.name = name + + if context_settings is None: + context_settings = {} + + #: an optional dictionary with defaults passed to the context. + self.context_settings: cabc.MutableMapping[str, t.Any] = context_settings + + #: the callback to execute when the command fires. This might be + #: `None` in which case nothing happens. + self.callback = callback + #: the list of parameters for this command in the order they + #: should show up in the help page and execute. Eager parameters + #: will automatically be handled before non eager ones. + self.params: list[Parameter] = params or [] + self.help = help + self.epilog = epilog + self.options_metavar = options_metavar + self.short_help = short_help + self.add_help_option = add_help_option + self._help_option = None + self.no_args_is_help = no_args_is_help + self.hidden = hidden + self.deprecated = deprecated + + def to_info_dict(self, ctx: Context) -> dict[str, t.Any]: + return { + "name": self.name, + "params": [param.to_info_dict() for param in self.get_params(ctx)], + "help": self.help, + "epilog": self.epilog, + "short_help": self.short_help, + "hidden": self.hidden, + "deprecated": self.deprecated, + } + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} {self.name}>" + + def get_usage(self, ctx: Context) -> str: + """Formats the usage line into a string and returns it. + + Calls :meth:`format_usage` internally. + """ + formatter = ctx.make_formatter() + self.format_usage(ctx, formatter) + return formatter.getvalue().rstrip("\n") + + def get_params(self, ctx: Context) -> list[Parameter]: + params = self.params + help_option = self.get_help_option(ctx) + + if help_option is not None: + params = [*params, help_option] + + if __debug__: + import warnings + + opts = [opt for param in params for opt in param.opts] + opts_counter = Counter(opts) + duplicate_opts = (opt for opt, count in opts_counter.items() if count > 1) + + for duplicate_opt in duplicate_opts: + warnings.warn( + ( + f"The parameter {duplicate_opt} is used more than once. " + "Remove its duplicate as parameters should be unique." + ), + stacklevel=3, + ) + + return params + + def format_usage(self, ctx: Context, formatter: HelpFormatter) -> None: + """Writes the usage line into the formatter. + + This is a low-level method called by :meth:`get_usage`. + """ + pieces = self.collect_usage_pieces(ctx) + formatter.write_usage(ctx.command_path, " ".join(pieces)) + + def collect_usage_pieces(self, ctx: Context) -> list[str]: + """Returns all the pieces that go into the usage line and returns + it as a list of strings. + """ + rv = [self.options_metavar] if self.options_metavar else [] + + for param in self.get_params(ctx): + rv.extend(param.get_usage_pieces(ctx)) + + return rv + + def get_help_option_names(self, ctx: Context) -> list[str]: + """Returns the names for the help option.""" + all_names = set(ctx.help_option_names) + for param in self.params: + all_names.difference_update(param.opts) + all_names.difference_update(param.secondary_opts) + return list(all_names) + + def get_help_option(self, ctx: Context) -> Option | None: + """Returns the help option object. + + Skipped if :attr:`add_help_option` is ``False``. + + .. versionchanged:: 8.1.8 + The help option is now cached to avoid creating it multiple times. + """ + help_option_names = self.get_help_option_names(ctx) + + if not help_option_names or not self.add_help_option: + return None + + # Cache the help option object in private _help_option attribute to + # avoid creating it multiple times. Not doing this will break the + # callback odering by iter_params_for_processing(), which relies on + # object comparison. + if self._help_option is None: + # Avoid circular import. + from .decorators import help_option + + # Apply help_option decorator and pop resulting option + help_option(*help_option_names)(self) + self._help_option = self.params.pop() # type: ignore[assignment] + + return self._help_option + + def make_parser(self, ctx: Context) -> _OptionParser: + """Creates the underlying option parser for this command.""" + parser = _OptionParser(ctx) + for param in self.get_params(ctx): + param.add_to_parser(parser, ctx) + return parser + + def get_help(self, ctx: Context) -> str: + """Formats the help into a string and returns it. + + Calls :meth:`format_help` internally. + """ + formatter = ctx.make_formatter() + self.format_help(ctx, formatter) + return formatter.getvalue().rstrip("\n") + + def get_short_help_str(self, limit: int = 45) -> str: + """Gets short help for the command or makes it by shortening the + long help string. + """ + if self.short_help: + text = inspect.cleandoc(self.short_help) + elif self.help: + text = make_default_short_help(self.help, limit) + else: + text = "" + + if self.deprecated: + deprecated_message = ( + f"(DEPRECATED: {self.deprecated})" + if isinstance(self.deprecated, str) + else "(DEPRECATED)" + ) + text = _("{text} {deprecated_message}").format( + text=text, deprecated_message=deprecated_message + ) + + return text.strip() + + def format_help(self, ctx: Context, formatter: HelpFormatter) -> None: + """Writes the help into the formatter if it exists. + + This is a low-level method called by :meth:`get_help`. + + This calls the following methods: + + - :meth:`format_usage` + - :meth:`format_help_text` + - :meth:`format_options` + - :meth:`format_epilog` + """ + self.format_usage(ctx, formatter) + self.format_help_text(ctx, formatter) + self.format_options(ctx, formatter) + self.format_epilog(ctx, formatter) + + def format_help_text(self, ctx: Context, formatter: HelpFormatter) -> None: + """Writes the help text to the formatter if it exists.""" + if self.help is not None: + # truncate the help text to the first form feed + text = inspect.cleandoc(self.help).partition("\f")[0] + else: + text = "" + + if self.deprecated: + deprecated_message = ( + f"(DEPRECATED: {self.deprecated})" + if isinstance(self.deprecated, str) + else "(DEPRECATED)" + ) + text = _("{text} {deprecated_message}").format( + text=text, deprecated_message=deprecated_message + ) + + if text: + formatter.write_paragraph() + + with formatter.indentation(): + formatter.write_text(text) + + def format_options(self, ctx: Context, formatter: HelpFormatter) -> None: + """Writes all the options into the formatter if they exist.""" + opts = [] + for param in self.get_params(ctx): + rv = param.get_help_record(ctx) + if rv is not None: + opts.append(rv) + + if opts: + with formatter.section(_("Options")): + formatter.write_dl(opts) + + def format_epilog(self, ctx: Context, formatter: HelpFormatter) -> None: + """Writes the epilog into the formatter if it exists.""" + if self.epilog: + epilog = inspect.cleandoc(self.epilog) + formatter.write_paragraph() + + with formatter.indentation(): + formatter.write_text(epilog) + + def make_context( + self, + info_name: str | None, + args: list[str], + parent: Context | None = None, + **extra: t.Any, + ) -> Context: + """This function when given an info name and arguments will kick + off the parsing and create a new :class:`Context`. It does not + invoke the actual command callback though. + + To quickly customize the context class used without overriding + this method, set the :attr:`context_class` attribute. + + :param info_name: the info name for this invocation. Generally this + is the most descriptive name for the script or + command. For the toplevel script it's usually + the name of the script, for commands below it's + the name of the command. + :param args: the arguments to parse as list of strings. + :param parent: the parent context if available. + :param extra: extra keyword arguments forwarded to the context + constructor. + + .. versionchanged:: 8.0 + Added the :attr:`context_class` attribute. + """ + for key, value in self.context_settings.items(): + if key not in extra: + extra[key] = value + + ctx = self.context_class(self, info_name=info_name, parent=parent, **extra) + + with ctx.scope(cleanup=False): + self.parse_args(ctx, args) + return ctx + + def parse_args(self, ctx: Context, args: list[str]) -> list[str]: + if not args and self.no_args_is_help and not ctx.resilient_parsing: + raise NoArgsIsHelpError(ctx) + + parser = self.make_parser(ctx) + opts, args, param_order = parser.parse_args(args=args) + + for param in iter_params_for_processing(param_order, self.get_params(ctx)): + value, args = param.handle_parse_result(ctx, opts, args) + + if args and not ctx.allow_extra_args and not ctx.resilient_parsing: + ctx.fail( + ngettext( + "Got unexpected extra argument ({args})", + "Got unexpected extra arguments ({args})", + len(args), + ).format(args=" ".join(map(str, args))) + ) + + ctx.args = args + ctx._opt_prefixes.update(parser._opt_prefixes) + return args + + def invoke(self, ctx: Context) -> t.Any: + """Given a context, this invokes the attached callback (if it exists) + in the right way. + """ + if self.deprecated: + extra_message = ( + f" {self.deprecated}" if isinstance(self.deprecated, str) else "" + ) + message = _( + "DeprecationWarning: The command {name!r} is deprecated.{extra_message}" + ).format(name=self.name, extra_message=extra_message) + echo(style(message, fg="red"), err=True) + + if self.callback is not None: + return ctx.invoke(self.callback, **ctx.params) + + def shell_complete(self, ctx: Context, incomplete: str) -> list[CompletionItem]: + """Return a list of completions for the incomplete value. Looks + at the names of options and chained multi-commands. + + Any command could be part of a chained multi-command, so sibling + commands are valid at any point during command completion. + + :param ctx: Invocation context for this command. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + from click.shell_completion import CompletionItem + + results: list[CompletionItem] = [] + + if incomplete and not incomplete[0].isalnum(): + for param in self.get_params(ctx): + if ( + not isinstance(param, Option) + or param.hidden + or ( + not param.multiple + and ctx.get_parameter_source(param.name) # type: ignore + is ParameterSource.COMMANDLINE + ) + ): + continue + + results.extend( + CompletionItem(name, help=param.help) + for name in [*param.opts, *param.secondary_opts] + if name.startswith(incomplete) + ) + + while ctx.parent is not None: + ctx = ctx.parent + + if isinstance(ctx.command, Group) and ctx.command.chain: + results.extend( + CompletionItem(name, help=command.get_short_help_str()) + for name, command in _complete_visible_commands(ctx, incomplete) + if name not in ctx._protected_args + ) + + return results + + @t.overload + def main( + self, + args: cabc.Sequence[str] | None = None, + prog_name: str | None = None, + complete_var: str | None = None, + standalone_mode: t.Literal[True] = True, + **extra: t.Any, + ) -> t.NoReturn: ... + + @t.overload + def main( + self, + args: cabc.Sequence[str] | None = None, + prog_name: str | None = None, + complete_var: str | None = None, + standalone_mode: bool = ..., + **extra: t.Any, + ) -> t.Any: ... + + def main( + self, + args: cabc.Sequence[str] | None = None, + prog_name: str | None = None, + complete_var: str | None = None, + standalone_mode: bool = True, + windows_expand_args: bool = True, + **extra: t.Any, + ) -> t.Any: + """This is the way to invoke a script with all the bells and + whistles as a command line application. This will always terminate + the application after a call. If this is not wanted, ``SystemExit`` + needs to be caught. + + This method is also available by directly calling the instance of + a :class:`Command`. + + :param args: the arguments that should be used for parsing. If not + provided, ``sys.argv[1:]`` is used. + :param prog_name: the program name that should be used. By default + the program name is constructed by taking the file + name from ``sys.argv[0]``. + :param complete_var: the environment variable that controls the + bash completion support. The default is + ``"__COMPLETE"`` with prog_name in + uppercase. + :param standalone_mode: the default behavior is to invoke the script + in standalone mode. Click will then + handle exceptions and convert them into + error messages and the function will never + return but shut down the interpreter. If + this is set to `False` they will be + propagated to the caller and the return + value of this function is the return value + of :meth:`invoke`. + :param windows_expand_args: Expand glob patterns, user dir, and + env vars in command line args on Windows. + :param extra: extra keyword arguments are forwarded to the context + constructor. See :class:`Context` for more information. + + .. versionchanged:: 8.0.1 + Added the ``windows_expand_args`` parameter to allow + disabling command line arg expansion on Windows. + + .. versionchanged:: 8.0 + When taking arguments from ``sys.argv`` on Windows, glob + patterns, user dir, and env vars are expanded. + + .. versionchanged:: 3.0 + Added the ``standalone_mode`` parameter. + """ + if args is None: + args = sys.argv[1:] + + if os.name == "nt" and windows_expand_args: + args = _expand_args(args) + else: + args = list(args) + + if prog_name is None: + prog_name = _detect_program_name() + + # Process shell completion requests and exit early. + self._main_shell_completion(extra, prog_name, complete_var) + + try: + try: + with self.make_context(prog_name, args, **extra) as ctx: + rv = self.invoke(ctx) + if not standalone_mode: + return rv + # it's not safe to `ctx.exit(rv)` here! + # note that `rv` may actually contain data like "1" which + # has obvious effects + # more subtle case: `rv=[None, None]` can come out of + # chained commands which all returned `None` -- so it's not + # even always obvious that `rv` indicates success/failure + # by its truthiness/falsiness + ctx.exit() + except (EOFError, KeyboardInterrupt) as e: + echo(file=sys.stderr) + raise Abort() from e + except ClickException as e: + if not standalone_mode: + raise + e.show() + sys.exit(e.exit_code) + except OSError as e: + if e.errno == errno.EPIPE: + sys.stdout = t.cast(t.TextIO, PacifyFlushWrapper(sys.stdout)) + sys.stderr = t.cast(t.TextIO, PacifyFlushWrapper(sys.stderr)) + sys.exit(1) + else: + raise + except Exit as e: + if standalone_mode: + sys.exit(e.exit_code) + else: + # in non-standalone mode, return the exit code + # note that this is only reached if `self.invoke` above raises + # an Exit explicitly -- thus bypassing the check there which + # would return its result + # the results of non-standalone execution may therefore be + # somewhat ambiguous: if there are codepaths which lead to + # `ctx.exit(1)` and to `return 1`, the caller won't be able to + # tell the difference between the two + return e.exit_code + except Abort: + if not standalone_mode: + raise + echo(_("Aborted!"), file=sys.stderr) + sys.exit(1) + + def _main_shell_completion( + self, + ctx_args: cabc.MutableMapping[str, t.Any], + prog_name: str, + complete_var: str | None = None, + ) -> None: + """Check if the shell is asking for tab completion, process + that, then exit early. Called from :meth:`main` before the + program is invoked. + + :param prog_name: Name of the executable in the shell. + :param complete_var: Name of the environment variable that holds + the completion instruction. Defaults to + ``_{PROG_NAME}_COMPLETE``. + + .. versionchanged:: 8.2.0 + Dots (``.``) in ``prog_name`` are replaced with underscores (``_``). + """ + if complete_var is None: + complete_name = prog_name.replace("-", "_").replace(".", "_") + complete_var = f"_{complete_name}_COMPLETE".upper() + + instruction = os.environ.get(complete_var) + + if not instruction: + return + + from .shell_completion import shell_complete + + rv = shell_complete(self, ctx_args, prog_name, complete_var, instruction) + sys.exit(rv) + + def __call__(self, *args: t.Any, **kwargs: t.Any) -> t.Any: + """Alias for :meth:`main`.""" + return self.main(*args, **kwargs) + + +class _FakeSubclassCheck(type): + def __subclasscheck__(cls, subclass: type) -> bool: + return issubclass(subclass, cls.__bases__[0]) + + def __instancecheck__(cls, instance: t.Any) -> bool: + return isinstance(instance, cls.__bases__[0]) + + +class _BaseCommand(Command, metaclass=_FakeSubclassCheck): + """ + .. deprecated:: 8.2 + Will be removed in Click 9.0. Use ``Command`` instead. + """ + + +class Group(Command): + """A group is a command that nests other commands (or more groups). + + :param name: The name of the group command. + :param commands: Map names to :class:`Command` objects. Can be a list, which + will use :attr:`Command.name` as the keys. + :param invoke_without_command: Invoke the group's callback even if a + subcommand is not given. + :param no_args_is_help: If no arguments are given, show the group's help and + exit. Defaults to the opposite of ``invoke_without_command``. + :param subcommand_metavar: How to represent the subcommand argument in help. + The default will represent whether ``chain`` is set or not. + :param chain: Allow passing more than one subcommand argument. After parsing + a command's arguments, if any arguments remain another command will be + matched, and so on. + :param result_callback: A function to call after the group's and + subcommand's callbacks. The value returned by the subcommand is passed. + If ``chain`` is enabled, the value will be a list of values returned by + all the commands. If ``invoke_without_command`` is enabled, the value + will be the value returned by the group's callback, or an empty list if + ``chain`` is enabled. + :param kwargs: Other arguments passed to :class:`Command`. + + .. versionchanged:: 8.0 + The ``commands`` argument can be a list of command objects. + + .. versionchanged:: 8.2 + Merged with and replaces the ``MultiCommand`` base class. + """ + + allow_extra_args = True + allow_interspersed_args = False + + #: If set, this is used by the group's :meth:`command` decorator + #: as the default :class:`Command` class. This is useful to make all + #: subcommands use a custom command class. + #: + #: .. versionadded:: 8.0 + command_class: type[Command] | None = None + + #: If set, this is used by the group's :meth:`group` decorator + #: as the default :class:`Group` class. This is useful to make all + #: subgroups use a custom group class. + #: + #: If set to the special value :class:`type` (literally + #: ``group_class = type``), this group's class will be used as the + #: default class. This makes a custom group class continue to make + #: custom groups. + #: + #: .. versionadded:: 8.0 + group_class: type[Group] | type[type] | None = None + # Literal[type] isn't valid, so use Type[type] + + def __init__( + self, + name: str | None = None, + commands: cabc.MutableMapping[str, Command] + | cabc.Sequence[Command] + | None = None, + invoke_without_command: bool = False, + no_args_is_help: bool | None = None, + subcommand_metavar: str | None = None, + chain: bool = False, + result_callback: t.Callable[..., t.Any] | None = None, + **kwargs: t.Any, + ) -> None: + super().__init__(name, **kwargs) + + if commands is None: + commands = {} + elif isinstance(commands, abc.Sequence): + commands = {c.name: c for c in commands if c.name is not None} + + #: The registered subcommands by their exported names. + self.commands: cabc.MutableMapping[str, Command] = commands + + if no_args_is_help is None: + no_args_is_help = not invoke_without_command + + self.no_args_is_help = no_args_is_help + self.invoke_without_command = invoke_without_command + + if subcommand_metavar is None: + if chain: + subcommand_metavar = "COMMAND1 [ARGS]... [COMMAND2 [ARGS]...]..." + else: + subcommand_metavar = "COMMAND [ARGS]..." + + self.subcommand_metavar = subcommand_metavar + self.chain = chain + # The result callback that is stored. This can be set or + # overridden with the :func:`result_callback` decorator. + self._result_callback = result_callback + + if self.chain: + for param in self.params: + if isinstance(param, Argument) and not param.required: + raise RuntimeError( + "A group in chain mode cannot have optional arguments." + ) + + def to_info_dict(self, ctx: Context) -> dict[str, t.Any]: + info_dict = super().to_info_dict(ctx) + commands = {} + + for name in self.list_commands(ctx): + command = self.get_command(ctx, name) + + if command is None: + continue + + sub_ctx = ctx._make_sub_context(command) + + with sub_ctx.scope(cleanup=False): + commands[name] = command.to_info_dict(sub_ctx) + + info_dict.update(commands=commands, chain=self.chain) + return info_dict + + def add_command(self, cmd: Command, name: str | None = None) -> None: + """Registers another :class:`Command` with this group. If the name + is not provided, the name of the command is used. + """ + name = name or cmd.name + if name is None: + raise TypeError("Command has no name.") + _check_nested_chain(self, name, cmd, register=True) + self.commands[name] = cmd + + @t.overload + def command(self, __func: t.Callable[..., t.Any]) -> Command: ... + + @t.overload + def command( + self, *args: t.Any, **kwargs: t.Any + ) -> t.Callable[[t.Callable[..., t.Any]], Command]: ... + + def command( + self, *args: t.Any, **kwargs: t.Any + ) -> t.Callable[[t.Callable[..., t.Any]], Command] | Command: + """A shortcut decorator for declaring and attaching a command to + the group. This takes the same arguments as :func:`command` and + immediately registers the created command with this group by + calling :meth:`add_command`. + + To customize the command class used, set the + :attr:`command_class` attribute. + + .. versionchanged:: 8.1 + This decorator can be applied without parentheses. + + .. versionchanged:: 8.0 + Added the :attr:`command_class` attribute. + """ + from .decorators import command + + func: t.Callable[..., t.Any] | None = None + + if args and callable(args[0]): + assert len(args) == 1 and not kwargs, ( + "Use 'command(**kwargs)(callable)' to provide arguments." + ) + (func,) = args + args = () + + if self.command_class and kwargs.get("cls") is None: + kwargs["cls"] = self.command_class + + def decorator(f: t.Callable[..., t.Any]) -> Command: + cmd: Command = command(*args, **kwargs)(f) + self.add_command(cmd) + return cmd + + if func is not None: + return decorator(func) + + return decorator + + @t.overload + def group(self, __func: t.Callable[..., t.Any]) -> Group: ... + + @t.overload + def group( + self, *args: t.Any, **kwargs: t.Any + ) -> t.Callable[[t.Callable[..., t.Any]], Group]: ... + + def group( + self, *args: t.Any, **kwargs: t.Any + ) -> t.Callable[[t.Callable[..., t.Any]], Group] | Group: + """A shortcut decorator for declaring and attaching a group to + the group. This takes the same arguments as :func:`group` and + immediately registers the created group with this group by + calling :meth:`add_command`. + + To customize the group class used, set the :attr:`group_class` + attribute. + + .. versionchanged:: 8.1 + This decorator can be applied without parentheses. + + .. versionchanged:: 8.0 + Added the :attr:`group_class` attribute. + """ + from .decorators import group + + func: t.Callable[..., t.Any] | None = None + + if args and callable(args[0]): + assert len(args) == 1 and not kwargs, ( + "Use 'group(**kwargs)(callable)' to provide arguments." + ) + (func,) = args + args = () + + if self.group_class is not None and kwargs.get("cls") is None: + if self.group_class is type: + kwargs["cls"] = type(self) + else: + kwargs["cls"] = self.group_class + + def decorator(f: t.Callable[..., t.Any]) -> Group: + cmd: Group = group(*args, **kwargs)(f) + self.add_command(cmd) + return cmd + + if func is not None: + return decorator(func) + + return decorator + + def result_callback(self, replace: bool = False) -> t.Callable[[F], F]: + """Adds a result callback to the command. By default if a + result callback is already registered this will chain them but + this can be disabled with the `replace` parameter. The result + callback is invoked with the return value of the subcommand + (or the list of return values from all subcommands if chaining + is enabled) as well as the parameters as they would be passed + to the main callback. + + Example:: + + @click.group() + @click.option('-i', '--input', default=23) + def cli(input): + return 42 + + @cli.result_callback() + def process_result(result, input): + return result + input + + :param replace: if set to `True` an already existing result + callback will be removed. + + .. versionchanged:: 8.0 + Renamed from ``resultcallback``. + + .. versionadded:: 3.0 + """ + + def decorator(f: F) -> F: + old_callback = self._result_callback + + if old_callback is None or replace: + self._result_callback = f + return f + + def function(value: t.Any, /, *args: t.Any, **kwargs: t.Any) -> t.Any: + inner = old_callback(value, *args, **kwargs) + return f(inner, *args, **kwargs) + + self._result_callback = rv = update_wrapper(t.cast(F, function), f) + return rv # type: ignore[return-value] + + return decorator + + def get_command(self, ctx: Context, cmd_name: str) -> Command | None: + """Given a context and a command name, this returns a :class:`Command` + object if it exists or returns ``None``. + """ + return self.commands.get(cmd_name) + + def list_commands(self, ctx: Context) -> list[str]: + """Returns a list of subcommand names in the order they should appear.""" + return sorted(self.commands) + + def collect_usage_pieces(self, ctx: Context) -> list[str]: + rv = super().collect_usage_pieces(ctx) + rv.append(self.subcommand_metavar) + return rv + + def format_options(self, ctx: Context, formatter: HelpFormatter) -> None: + super().format_options(ctx, formatter) + self.format_commands(ctx, formatter) + + def format_commands(self, ctx: Context, formatter: HelpFormatter) -> None: + """Extra format methods for multi methods that adds all the commands + after the options. + """ + commands = [] + for subcommand in self.list_commands(ctx): + cmd = self.get_command(ctx, subcommand) + # What is this, the tool lied about a command. Ignore it + if cmd is None: + continue + if cmd.hidden: + continue + + commands.append((subcommand, cmd)) + + # allow for 3 times the default spacing + if len(commands): + limit = formatter.width - 6 - max(len(cmd[0]) for cmd in commands) + + rows = [] + for subcommand, cmd in commands: + help = cmd.get_short_help_str(limit) + rows.append((subcommand, help)) + + if rows: + with formatter.section(_("Commands")): + formatter.write_dl(rows) + + def parse_args(self, ctx: Context, args: list[str]) -> list[str]: + if not args and self.no_args_is_help and not ctx.resilient_parsing: + raise NoArgsIsHelpError(ctx) + + rest = super().parse_args(ctx, args) + + if self.chain: + ctx._protected_args = rest + ctx.args = [] + elif rest: + ctx._protected_args, ctx.args = rest[:1], rest[1:] + + return ctx.args + + def invoke(self, ctx: Context) -> t.Any: + def _process_result(value: t.Any) -> t.Any: + if self._result_callback is not None: + value = ctx.invoke(self._result_callback, value, **ctx.params) + return value + + if not ctx._protected_args: + if self.invoke_without_command: + # No subcommand was invoked, so the result callback is + # invoked with the group return value for regular + # groups, or an empty list for chained groups. + with ctx: + rv = super().invoke(ctx) + return _process_result([] if self.chain else rv) + ctx.fail(_("Missing command.")) + + # Fetch args back out + args = [*ctx._protected_args, *ctx.args] + ctx.args = [] + ctx._protected_args = [] + + # If we're not in chain mode, we only allow the invocation of a + # single command but we also inform the current context about the + # name of the command to invoke. + if not self.chain: + # Make sure the context is entered so we do not clean up + # resources until the result processor has worked. + with ctx: + cmd_name, cmd, args = self.resolve_command(ctx, args) + assert cmd is not None + ctx.invoked_subcommand = cmd_name + super().invoke(ctx) + sub_ctx = cmd.make_context(cmd_name, args, parent=ctx) + with sub_ctx: + return _process_result(sub_ctx.command.invoke(sub_ctx)) + + # In chain mode we create the contexts step by step, but after the + # base command has been invoked. Because at that point we do not + # know the subcommands yet, the invoked subcommand attribute is + # set to ``*`` to inform the command that subcommands are executed + # but nothing else. + with ctx: + ctx.invoked_subcommand = "*" if args else None + super().invoke(ctx) + + # Otherwise we make every single context and invoke them in a + # chain. In that case the return value to the result processor + # is the list of all invoked subcommand's results. + contexts = [] + while args: + cmd_name, cmd, args = self.resolve_command(ctx, args) + assert cmd is not None + sub_ctx = cmd.make_context( + cmd_name, + args, + parent=ctx, + allow_extra_args=True, + allow_interspersed_args=False, + ) + contexts.append(sub_ctx) + args, sub_ctx.args = sub_ctx.args, [] + + rv = [] + for sub_ctx in contexts: + with sub_ctx: + rv.append(sub_ctx.command.invoke(sub_ctx)) + return _process_result(rv) + + def resolve_command( + self, ctx: Context, args: list[str] + ) -> tuple[str | None, Command | None, list[str]]: + cmd_name = make_str(args[0]) + original_cmd_name = cmd_name + + # Get the command + cmd = self.get_command(ctx, cmd_name) + + # If we can't find the command but there is a normalization + # function available, we try with that one. + if cmd is None and ctx.token_normalize_func is not None: + cmd_name = ctx.token_normalize_func(cmd_name) + cmd = self.get_command(ctx, cmd_name) + + # If we don't find the command we want to show an error message + # to the user that it was not provided. However, there is + # something else we should do: if the first argument looks like + # an option we want to kick off parsing again for arguments to + # resolve things like --help which now should go to the main + # place. + if cmd is None and not ctx.resilient_parsing: + if _split_opt(cmd_name)[0]: + self.parse_args(ctx, args) + ctx.fail(_("No such command {name!r}.").format(name=original_cmd_name)) + return cmd_name if cmd else None, cmd, args[1:] + + def shell_complete(self, ctx: Context, incomplete: str) -> list[CompletionItem]: + """Return a list of completions for the incomplete value. Looks + at the names of options, subcommands, and chained + multi-commands. + + :param ctx: Invocation context for this command. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + from click.shell_completion import CompletionItem + + results = [ + CompletionItem(name, help=command.get_short_help_str()) + for name, command in _complete_visible_commands(ctx, incomplete) + ] + results.extend(super().shell_complete(ctx, incomplete)) + return results + + +class _MultiCommand(Group, metaclass=_FakeSubclassCheck): + """ + .. deprecated:: 8.2 + Will be removed in Click 9.0. Use ``Group`` instead. + """ + + +class CommandCollection(Group): + """A :class:`Group` that looks up subcommands on other groups. If a command + is not found on this group, each registered source is checked in order. + Parameters on a source are not added to this group, and a source's callback + is not invoked when invoking its commands. In other words, this "flattens" + commands in many groups into this one group. + + :param name: The name of the group command. + :param sources: A list of :class:`Group` objects to look up commands from. + :param kwargs: Other arguments passed to :class:`Group`. + + .. versionchanged:: 8.2 + This is a subclass of ``Group``. Commands are looked up first on this + group, then each of its sources. + """ + + def __init__( + self, + name: str | None = None, + sources: list[Group] | None = None, + **kwargs: t.Any, + ) -> None: + super().__init__(name, **kwargs) + #: The list of registered groups. + self.sources: list[Group] = sources or [] + + def add_source(self, group: Group) -> None: + """Add a group as a source of commands.""" + self.sources.append(group) + + def get_command(self, ctx: Context, cmd_name: str) -> Command | None: + rv = super().get_command(ctx, cmd_name) + + if rv is not None: + return rv + + for source in self.sources: + rv = source.get_command(ctx, cmd_name) + + if rv is not None: + if self.chain: + _check_nested_chain(self, cmd_name, rv) + + return rv + + return None + + def list_commands(self, ctx: Context) -> list[str]: + rv: set[str] = set(super().list_commands(ctx)) + + for source in self.sources: + rv.update(source.list_commands(ctx)) + + return sorted(rv) + + +def _check_iter(value: t.Any) -> cabc.Iterator[t.Any]: + """Check if the value is iterable but not a string. Raises a type + error, or return an iterator over the value. + """ + if isinstance(value, str): + raise TypeError + + return iter(value) + + +class Parameter: + r"""A parameter to a command comes in two versions: they are either + :class:`Option`\s or :class:`Argument`\s. Other subclasses are currently + not supported by design as some of the internals for parsing are + intentionally not finalized. + + Some settings are supported by both options and arguments. + + :param param_decls: the parameter declarations for this option or + argument. This is a list of flags or argument + names. + :param type: the type that should be used. Either a :class:`ParamType` + or a Python type. The latter is converted into the former + automatically if supported. + :param required: controls if this is optional or not. + :param default: the default value if omitted. This can also be a callable, + in which case it's invoked when the default is needed + without any arguments. + :param callback: A function to further process or validate the value + after type conversion. It is called as ``f(ctx, param, value)`` + and must return the value. It is called for all sources, + including prompts. + :param nargs: the number of arguments to match. If not ``1`` the return + value is a tuple instead of single value. The default for + nargs is ``1`` (except if the type is a tuple, then it's + the arity of the tuple). If ``nargs=-1``, all remaining + parameters are collected. + :param metavar: how the value is represented in the help page. + :param expose_value: if this is `True` then the value is passed onwards + to the command callback and stored on the context, + otherwise it's skipped. + :param is_eager: eager values are processed before non eager ones. This + should not be set for arguments or it will inverse the + order of processing. + :param envvar: a string or list of strings that are environment variables + that should be checked. + :param shell_complete: A function that returns custom shell + completions. Used instead of the param's type completion if + given. Takes ``ctx, param, incomplete`` and must return a list + of :class:`~click.shell_completion.CompletionItem` or a list of + strings. + :param deprecated: If ``True`` or non-empty string, issues a message + indicating that the argument is deprecated and highlights + its deprecation in --help. The message can be customized + by using a string as the value. A deprecated parameter + cannot be required, a ValueError will be raised otherwise. + + .. versionchanged:: 8.2.0 + Introduction of ``deprecated``. + + .. versionchanged:: 8.2 + Adding duplicate parameter names to a :class:`~click.core.Command` will + result in a ``UserWarning`` being shown. + + .. versionchanged:: 8.2 + Adding duplicate parameter names to a :class:`~click.core.Command` will + result in a ``UserWarning`` being shown. + + .. versionchanged:: 8.0 + ``process_value`` validates required parameters and bounded + ``nargs``, and invokes the parameter callback before returning + the value. This allows the callback to validate prompts. + ``full_process_value`` is removed. + + .. versionchanged:: 8.0 + ``autocompletion`` is renamed to ``shell_complete`` and has new + semantics described above. The old name is deprecated and will + be removed in 8.1, until then it will be wrapped to match the + new requirements. + + .. versionchanged:: 8.0 + For ``multiple=True, nargs>1``, the default must be a list of + tuples. + + .. versionchanged:: 8.0 + Setting a default is no longer required for ``nargs>1``, it will + default to ``None``. ``multiple=True`` or ``nargs=-1`` will + default to ``()``. + + .. versionchanged:: 7.1 + Empty environment variables are ignored rather than taking the + empty string value. This makes it possible for scripts to clear + variables if they can't unset them. + + .. versionchanged:: 2.0 + Changed signature for parameter callback to also be passed the + parameter. The old callback format will still work, but it will + raise a warning to give you a chance to migrate the code easier. + """ + + param_type_name = "parameter" + + def __init__( + self, + param_decls: cabc.Sequence[str] | None = None, + type: types.ParamType | t.Any | None = None, + required: bool = False, + default: t.Any | t.Callable[[], t.Any] | None = None, + callback: t.Callable[[Context, Parameter, t.Any], t.Any] | None = None, + nargs: int | None = None, + multiple: bool = False, + metavar: str | None = None, + expose_value: bool = True, + is_eager: bool = False, + envvar: str | cabc.Sequence[str] | None = None, + shell_complete: t.Callable[ + [Context, Parameter, str], list[CompletionItem] | list[str] + ] + | None = None, + deprecated: bool | str = False, + ) -> None: + self.name: str | None + self.opts: list[str] + self.secondary_opts: list[str] + self.name, self.opts, self.secondary_opts = self._parse_decls( + param_decls or (), expose_value + ) + self.type: types.ParamType = types.convert_type(type, default) + + # Default nargs to what the type tells us if we have that + # information available. + if nargs is None: + if self.type.is_composite: + nargs = self.type.arity + else: + nargs = 1 + + self.required = required + self.callback = callback + self.nargs = nargs + self.multiple = multiple + self.expose_value = expose_value + self.default = default + self.is_eager = is_eager + self.metavar = metavar + self.envvar = envvar + self._custom_shell_complete = shell_complete + self.deprecated = deprecated + + if __debug__: + if self.type.is_composite and nargs != self.type.arity: + raise ValueError( + f"'nargs' must be {self.type.arity} (or None) for" + f" type {self.type!r}, but it was {nargs}." + ) + + # Skip no default or callable default. + check_default = default if not callable(default) else None + + if check_default is not None: + if multiple: + try: + # Only check the first value against nargs. + check_default = next(_check_iter(check_default), None) + except TypeError: + raise ValueError( + "'default' must be a list when 'multiple' is true." + ) from None + + # Can be None for multiple with empty default. + if nargs != 1 and check_default is not None: + try: + _check_iter(check_default) + except TypeError: + if multiple: + message = ( + "'default' must be a list of lists when 'multiple' is" + " true and 'nargs' != 1." + ) + else: + message = "'default' must be a list when 'nargs' != 1." + + raise ValueError(message) from None + + if nargs > 1 and len(check_default) != nargs: + subject = "item length" if multiple else "length" + raise ValueError( + f"'default' {subject} must match nargs={nargs}." + ) + + if required and deprecated: + raise ValueError( + f"The {self.param_type_name} '{self.human_readable_name}' " + "is deprecated and still required. A deprecated " + f"{self.param_type_name} cannot be required." + ) + + def to_info_dict(self) -> dict[str, t.Any]: + """Gather information that could be useful for a tool generating + user-facing documentation. + + Use :meth:`click.Context.to_info_dict` to traverse the entire + CLI structure. + + .. versionadded:: 8.0 + """ + return { + "name": self.name, + "param_type_name": self.param_type_name, + "opts": self.opts, + "secondary_opts": self.secondary_opts, + "type": self.type.to_info_dict(), + "required": self.required, + "nargs": self.nargs, + "multiple": self.multiple, + "default": self.default, + "envvar": self.envvar, + } + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} {self.name}>" + + def _parse_decls( + self, decls: cabc.Sequence[str], expose_value: bool + ) -> tuple[str | None, list[str], list[str]]: + raise NotImplementedError() + + @property + def human_readable_name(self) -> str: + """Returns the human readable name of this parameter. This is the + same as the name for options, but the metavar for arguments. + """ + return self.name # type: ignore + + def make_metavar(self, ctx: Context) -> str: + if self.metavar is not None: + return self.metavar + + metavar = self.type.get_metavar(param=self, ctx=ctx) + + if metavar is None: + metavar = self.type.name.upper() + + if self.nargs != 1: + metavar += "..." + + return metavar + + @t.overload + def get_default( + self, ctx: Context, call: t.Literal[True] = True + ) -> t.Any | None: ... + + @t.overload + def get_default( + self, ctx: Context, call: bool = ... + ) -> t.Any | t.Callable[[], t.Any] | None: ... + + def get_default( + self, ctx: Context, call: bool = True + ) -> t.Any | t.Callable[[], t.Any] | None: + """Get the default for the parameter. Tries + :meth:`Context.lookup_default` first, then the local default. + + :param ctx: Current context. + :param call: If the default is a callable, call it. Disable to + return the callable instead. + + .. versionchanged:: 8.0.2 + Type casting is no longer performed when getting a default. + + .. versionchanged:: 8.0.1 + Type casting can fail in resilient parsing mode. Invalid + defaults will not prevent showing help text. + + .. versionchanged:: 8.0 + Looks at ``ctx.default_map`` first. + + .. versionchanged:: 8.0 + Added the ``call`` parameter. + """ + value = ctx.lookup_default(self.name, call=False) # type: ignore + + if value is None: + value = self.default + + if call and callable(value): + value = value() + + return value + + def add_to_parser(self, parser: _OptionParser, ctx: Context) -> None: + raise NotImplementedError() + + def consume_value( + self, ctx: Context, opts: cabc.Mapping[str, t.Any] + ) -> tuple[t.Any, ParameterSource]: + value = opts.get(self.name) # type: ignore + source = ParameterSource.COMMANDLINE + + if value is None: + value = self.value_from_envvar(ctx) + source = ParameterSource.ENVIRONMENT + + if value is None: + value = ctx.lookup_default(self.name) # type: ignore + source = ParameterSource.DEFAULT_MAP + + if value is None: + value = self.get_default(ctx) + source = ParameterSource.DEFAULT + + return value, source + + def type_cast_value(self, ctx: Context, value: t.Any) -> t.Any: + """Convert and validate a value against the option's + :attr:`type`, :attr:`multiple`, and :attr:`nargs`. + """ + if value is None: + return () if self.multiple or self.nargs == -1 else None + + def check_iter(value: t.Any) -> cabc.Iterator[t.Any]: + try: + return _check_iter(value) + except TypeError: + # This should only happen when passing in args manually, + # the parser should construct an iterable when parsing + # the command line. + raise BadParameter( + _("Value must be an iterable."), ctx=ctx, param=self + ) from None + + if self.nargs == 1 or self.type.is_composite: + + def convert(value: t.Any) -> t.Any: + return self.type(value, param=self, ctx=ctx) + + elif self.nargs == -1: + + def convert(value: t.Any) -> t.Any: # tuple[t.Any, ...] + return tuple(self.type(x, self, ctx) for x in check_iter(value)) + + else: # nargs > 1 + + def convert(value: t.Any) -> t.Any: # tuple[t.Any, ...] + value = tuple(check_iter(value)) + + if len(value) != self.nargs: + raise BadParameter( + ngettext( + "Takes {nargs} values but 1 was given.", + "Takes {nargs} values but {len} were given.", + len(value), + ).format(nargs=self.nargs, len=len(value)), + ctx=ctx, + param=self, + ) + + return tuple(self.type(x, self, ctx) for x in value) + + if self.multiple: + return tuple(convert(x) for x in check_iter(value)) + + return convert(value) + + def value_is_missing(self, value: t.Any) -> bool: + if value is None: + return True + + if (self.nargs != 1 or self.multiple) and value == (): + return True + + return False + + def process_value(self, ctx: Context, value: t.Any) -> t.Any: + value = self.type_cast_value(ctx, value) + + if self.required and self.value_is_missing(value): + raise MissingParameter(ctx=ctx, param=self) + + if self.callback is not None: + value = self.callback(ctx, self, value) + + return value + + def resolve_envvar_value(self, ctx: Context) -> str | None: + if self.envvar is None: + return None + + if isinstance(self.envvar, str): + rv = os.environ.get(self.envvar) + + if rv: + return rv + else: + for envvar in self.envvar: + rv = os.environ.get(envvar) + + if rv: + return rv + + return None + + def value_from_envvar(self, ctx: Context) -> t.Any | None: + rv: t.Any | None = self.resolve_envvar_value(ctx) + + if rv is not None and self.nargs != 1: + rv = self.type.split_envvar_value(rv) + + return rv + + def handle_parse_result( + self, ctx: Context, opts: cabc.Mapping[str, t.Any], args: list[str] + ) -> tuple[t.Any, list[str]]: + with augment_usage_errors(ctx, param=self): + value, source = self.consume_value(ctx, opts) + + if ( + self.deprecated + and value is not None + and source + not in ( + ParameterSource.DEFAULT, + ParameterSource.DEFAULT_MAP, + ) + ): + extra_message = ( + f" {self.deprecated}" if isinstance(self.deprecated, str) else "" + ) + message = _( + "DeprecationWarning: The {param_type} {name!r} is deprecated." + "{extra_message}" + ).format( + param_type=self.param_type_name, + name=self.human_readable_name, + extra_message=extra_message, + ) + echo(style(message, fg="red"), err=True) + + ctx.set_parameter_source(self.name, source) # type: ignore + + try: + value = self.process_value(ctx, value) + except Exception: + if not ctx.resilient_parsing: + raise + + value = None + + if self.expose_value: + ctx.params[self.name] = value # type: ignore + + return value, args + + def get_help_record(self, ctx: Context) -> tuple[str, str] | None: + pass + + def get_usage_pieces(self, ctx: Context) -> list[str]: + return [] + + def get_error_hint(self, ctx: Context) -> str: + """Get a stringified version of the param for use in error messages to + indicate which param caused the error. + """ + hint_list = self.opts or [self.human_readable_name] + return " / ".join(f"'{x}'" for x in hint_list) + + def shell_complete(self, ctx: Context, incomplete: str) -> list[CompletionItem]: + """Return a list of completions for the incomplete value. If a + ``shell_complete`` function was given during init, it is used. + Otherwise, the :attr:`type` + :meth:`~click.types.ParamType.shell_complete` function is used. + + :param ctx: Invocation context for this command. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + if self._custom_shell_complete is not None: + results = self._custom_shell_complete(ctx, self, incomplete) + + if results and isinstance(results[0], str): + from click.shell_completion import CompletionItem + + results = [CompletionItem(c) for c in results] + + return t.cast("list[CompletionItem]", results) + + return self.type.shell_complete(ctx, self, incomplete) + + +class Option(Parameter): + """Options are usually optional values on the command line and + have some extra features that arguments don't have. + + All other parameters are passed onwards to the parameter constructor. + + :param show_default: Show the default value for this option in its + help text. Values are not shown by default, unless + :attr:`Context.show_default` is ``True``. If this value is a + string, it shows that string in parentheses instead of the + actual value. This is particularly useful for dynamic options. + For single option boolean flags, the default remains hidden if + its value is ``False``. + :param show_envvar: Controls if an environment variable should be + shown on the help page and error messages. + Normally, environment variables are not shown. + :param prompt: If set to ``True`` or a non empty string then the + user will be prompted for input. If set to ``True`` the prompt + will be the option name capitalized. A deprecated option cannot be + prompted. + :param confirmation_prompt: Prompt a second time to confirm the + value if it was prompted for. Can be set to a string instead of + ``True`` to customize the message. + :param prompt_required: If set to ``False``, the user will be + prompted for input only when the option was specified as a flag + without a value. + :param hide_input: If this is ``True`` then the input on the prompt + will be hidden from the user. This is useful for password input. + :param is_flag: forces this option to act as a flag. The default is + auto detection. + :param flag_value: which value should be used for this flag if it's + enabled. This is set to a boolean automatically if + the option string contains a slash to mark two options. + :param multiple: if this is set to `True` then the argument is accepted + multiple times and recorded. This is similar to ``nargs`` + in how it works but supports arbitrary number of + arguments. + :param count: this flag makes an option increment an integer. + :param allow_from_autoenv: if this is enabled then the value of this + parameter will be pulled from an environment + variable in case a prefix is defined on the + context. + :param help: the help string. + :param hidden: hide this option from help outputs. + :param attrs: Other command arguments described in :class:`Parameter`. + + .. versionchanged:: 8.2 + ``envvar`` used with ``flag_value`` will always use the ``flag_value``, + previously it would use the value of the environment variable. + + .. versionchanged:: 8.1 + Help text indentation is cleaned here instead of only in the + ``@option`` decorator. + + .. versionchanged:: 8.1 + The ``show_default`` parameter overrides + ``Context.show_default``. + + .. versionchanged:: 8.1 + The default of a single option boolean flag is not shown if the + default value is ``False``. + + .. versionchanged:: 8.0.1 + ``type`` is detected from ``flag_value`` if given. + """ + + param_type_name = "option" + + def __init__( + self, + param_decls: cabc.Sequence[str] | None = None, + show_default: bool | str | None = None, + prompt: bool | str = False, + confirmation_prompt: bool | str = False, + prompt_required: bool = True, + hide_input: bool = False, + is_flag: bool | None = None, + flag_value: t.Any | None = None, + multiple: bool = False, + count: bool = False, + allow_from_autoenv: bool = True, + type: types.ParamType | t.Any | None = None, + help: str | None = None, + hidden: bool = False, + show_choices: bool = True, + show_envvar: bool = False, + deprecated: bool | str = False, + **attrs: t.Any, + ) -> None: + if help: + help = inspect.cleandoc(help) + + default_is_missing = "default" not in attrs + super().__init__( + param_decls, type=type, multiple=multiple, deprecated=deprecated, **attrs + ) + + if prompt is True: + if self.name is None: + raise TypeError("'name' is required with 'prompt=True'.") + + prompt_text: str | None = self.name.replace("_", " ").capitalize() + elif prompt is False: + prompt_text = None + else: + prompt_text = prompt + + if deprecated: + deprecated_message = ( + f"(DEPRECATED: {deprecated})" + if isinstance(deprecated, str) + else "(DEPRECATED)" + ) + help = help + deprecated_message if help is not None else deprecated_message + + self.prompt = prompt_text + self.confirmation_prompt = confirmation_prompt + self.prompt_required = prompt_required + self.hide_input = hide_input + self.hidden = hidden + + # If prompt is enabled but not required, then the option can be + # used as a flag to indicate using prompt or flag_value. + self._flag_needs_value = self.prompt is not None and not self.prompt_required + + if is_flag is None: + if flag_value is not None: + # Implicitly a flag because flag_value was set. + is_flag = True + elif self._flag_needs_value: + # Not a flag, but when used as a flag it shows a prompt. + is_flag = False + else: + # Implicitly a flag because flag options were given. + is_flag = bool(self.secondary_opts) + elif is_flag is False and not self._flag_needs_value: + # Not a flag, and prompt is not enabled, can be used as a + # flag if flag_value is set. + self._flag_needs_value = flag_value is not None + + self.default: t.Any | t.Callable[[], t.Any] + + if is_flag and default_is_missing and not self.required: + if multiple: + self.default = () + else: + self.default = False + + if is_flag and flag_value is None: + flag_value = not self.default + + self.type: types.ParamType + if is_flag and type is None: + # Re-guess the type from the flag value instead of the + # default. + self.type = types.convert_type(None, flag_value) + + self.is_flag: bool = is_flag + self.is_bool_flag: bool = is_flag and isinstance(self.type, types.BoolParamType) + self.flag_value: t.Any = flag_value + + # Counting + self.count = count + if count: + if type is None: + self.type = types.IntRange(min=0) + if default_is_missing: + self.default = 0 + + self.allow_from_autoenv = allow_from_autoenv + self.help = help + self.show_default = show_default + self.show_choices = show_choices + self.show_envvar = show_envvar + + if __debug__: + if deprecated and prompt: + raise ValueError("`deprecated` options cannot use `prompt`.") + + if self.nargs == -1: + raise TypeError("nargs=-1 is not supported for options.") + + if self.prompt and self.is_flag and not self.is_bool_flag: + raise TypeError("'prompt' is not valid for non-boolean flag.") + + if not self.is_bool_flag and self.secondary_opts: + raise TypeError("Secondary flag is not valid for non-boolean flag.") + + if self.is_bool_flag and self.hide_input and self.prompt is not None: + raise TypeError( + "'prompt' with 'hide_input' is not valid for boolean flag." + ) + + if self.count: + if self.multiple: + raise TypeError("'count' is not valid with 'multiple'.") + + if self.is_flag: + raise TypeError("'count' is not valid with 'is_flag'.") + + def to_info_dict(self) -> dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict.update( + help=self.help, + prompt=self.prompt, + is_flag=self.is_flag, + flag_value=self.flag_value, + count=self.count, + hidden=self.hidden, + ) + return info_dict + + def get_error_hint(self, ctx: Context) -> str: + result = super().get_error_hint(ctx) + if self.show_envvar: + result += f" (env var: '{self.envvar}')" + return result + + def _parse_decls( + self, decls: cabc.Sequence[str], expose_value: bool + ) -> tuple[str | None, list[str], list[str]]: + opts = [] + secondary_opts = [] + name = None + possible_names = [] + + for decl in decls: + if decl.isidentifier(): + if name is not None: + raise TypeError(f"Name '{name}' defined twice") + name = decl + else: + split_char = ";" if decl[:1] == "/" else "/" + if split_char in decl: + first, second = decl.split(split_char, 1) + first = first.rstrip() + if first: + possible_names.append(_split_opt(first)) + opts.append(first) + second = second.lstrip() + if second: + secondary_opts.append(second.lstrip()) + if first == second: + raise ValueError( + f"Boolean option {decl!r} cannot use the" + " same flag for true/false." + ) + else: + possible_names.append(_split_opt(decl)) + opts.append(decl) + + if name is None and possible_names: + possible_names.sort(key=lambda x: -len(x[0])) # group long options first + name = possible_names[0][1].replace("-", "_").lower() + if not name.isidentifier(): + name = None + + if name is None: + if not expose_value: + return None, opts, secondary_opts + raise TypeError( + f"Could not determine name for option with declarations {decls!r}" + ) + + if not opts and not secondary_opts: + raise TypeError( + f"No options defined but a name was passed ({name})." + " Did you mean to declare an argument instead? Did" + f" you mean to pass '--{name}'?" + ) + + return name, opts, secondary_opts + + def add_to_parser(self, parser: _OptionParser, ctx: Context) -> None: + if self.multiple: + action = "append" + elif self.count: + action = "count" + else: + action = "store" + + if self.is_flag: + action = f"{action}_const" + + if self.is_bool_flag and self.secondary_opts: + parser.add_option( + obj=self, opts=self.opts, dest=self.name, action=action, const=True + ) + parser.add_option( + obj=self, + opts=self.secondary_opts, + dest=self.name, + action=action, + const=False, + ) + else: + parser.add_option( + obj=self, + opts=self.opts, + dest=self.name, + action=action, + const=self.flag_value, + ) + else: + parser.add_option( + obj=self, + opts=self.opts, + dest=self.name, + action=action, + nargs=self.nargs, + ) + + def get_help_record(self, ctx: Context) -> tuple[str, str] | None: + if self.hidden: + return None + + any_prefix_is_slash = False + + def _write_opts(opts: cabc.Sequence[str]) -> str: + nonlocal any_prefix_is_slash + + rv, any_slashes = join_options(opts) + + if any_slashes: + any_prefix_is_slash = True + + if not self.is_flag and not self.count: + rv += f" {self.make_metavar(ctx=ctx)}" + + return rv + + rv = [_write_opts(self.opts)] + + if self.secondary_opts: + rv.append(_write_opts(self.secondary_opts)) + + help = self.help or "" + + extra = self.get_help_extra(ctx) + extra_items = [] + if "envvars" in extra: + extra_items.append( + _("env var: {var}").format(var=", ".join(extra["envvars"])) + ) + if "default" in extra: + extra_items.append(_("default: {default}").format(default=extra["default"])) + if "range" in extra: + extra_items.append(extra["range"]) + if "required" in extra: + extra_items.append(_(extra["required"])) + + if extra_items: + extra_str = "; ".join(extra_items) + help = f"{help} [{extra_str}]" if help else f"[{extra_str}]" + + return ("; " if any_prefix_is_slash else " / ").join(rv), help + + def get_help_extra(self, ctx: Context) -> types.OptionHelpExtra: + extra: types.OptionHelpExtra = {} + + if self.show_envvar: + envvar = self.envvar + + if envvar is None: + if ( + self.allow_from_autoenv + and ctx.auto_envvar_prefix is not None + and self.name is not None + ): + envvar = f"{ctx.auto_envvar_prefix}_{self.name.upper()}" + + if envvar is not None: + if isinstance(envvar, str): + extra["envvars"] = (envvar,) + else: + extra["envvars"] = tuple(str(d) for d in envvar) + + # Temporarily enable resilient parsing to avoid type casting + # failing for the default. Might be possible to extend this to + # help formatting in general. + resilient = ctx.resilient_parsing + ctx.resilient_parsing = True + + try: + default_value = self.get_default(ctx, call=False) + finally: + ctx.resilient_parsing = resilient + + show_default = False + show_default_is_str = False + + if self.show_default is not None: + if isinstance(self.show_default, str): + show_default_is_str = show_default = True + else: + show_default = self.show_default + elif ctx.show_default is not None: + show_default = ctx.show_default + + if show_default_is_str or (show_default and (default_value is not None)): + if show_default_is_str: + default_string = f"({self.show_default})" + elif isinstance(default_value, (list, tuple)): + default_string = ", ".join(str(d) for d in default_value) + elif inspect.isfunction(default_value): + default_string = _("(dynamic)") + elif self.is_bool_flag and self.secondary_opts: + # For boolean flags that have distinct True/False opts, + # use the opt without prefix instead of the value. + default_string = _split_opt( + (self.opts if default_value else self.secondary_opts)[0] + )[1] + elif self.is_bool_flag and not self.secondary_opts and not default_value: + default_string = "" + elif default_value == "": + default_string = '""' + else: + default_string = str(default_value) + + if default_string: + extra["default"] = default_string + + if ( + isinstance(self.type, types._NumberRangeBase) + # skip count with default range type + and not (self.count and self.type.min == 0 and self.type.max is None) + ): + range_str = self.type._describe_range() + + if range_str: + extra["range"] = range_str + + if self.required: + extra["required"] = "required" + + return extra + + @t.overload + def get_default( + self, ctx: Context, call: t.Literal[True] = True + ) -> t.Any | None: ... + + @t.overload + def get_default( + self, ctx: Context, call: bool = ... + ) -> t.Any | t.Callable[[], t.Any] | None: ... + + def get_default( + self, ctx: Context, call: bool = True + ) -> t.Any | t.Callable[[], t.Any] | None: + # If we're a non boolean flag our default is more complex because + # we need to look at all flags in the same group to figure out + # if we're the default one in which case we return the flag + # value as default. + if self.is_flag and not self.is_bool_flag: + for param in ctx.command.params: + if param.name == self.name and param.default: + return t.cast(Option, param).flag_value + + return None + + return super().get_default(ctx, call=call) + + def prompt_for_value(self, ctx: Context) -> t.Any: + """This is an alternative flow that can be activated in the full + value processing if a value does not exist. It will prompt the + user until a valid value exists and then returns the processed + value as result. + """ + assert self.prompt is not None + + # Calculate the default before prompting anything to be stable. + default = self.get_default(ctx) + + # If this is a prompt for a flag we need to handle this + # differently. + if self.is_bool_flag: + return confirm(self.prompt, default) + + # If show_default is set to True/False, provide this to `prompt` as well. For + # non-bool values of `show_default`, we use `prompt`'s default behavior + prompt_kwargs: t.Any = {} + if isinstance(self.show_default, bool): + prompt_kwargs["show_default"] = self.show_default + + return prompt( + self.prompt, + default=default, + type=self.type, + hide_input=self.hide_input, + show_choices=self.show_choices, + confirmation_prompt=self.confirmation_prompt, + value_proc=lambda x: self.process_value(ctx, x), + **prompt_kwargs, + ) + + def resolve_envvar_value(self, ctx: Context) -> str | None: + rv = super().resolve_envvar_value(ctx) + + if rv is not None: + if self.is_flag and self.flag_value: + return str(self.flag_value) + return rv + + if ( + self.allow_from_autoenv + and ctx.auto_envvar_prefix is not None + and self.name is not None + ): + envvar = f"{ctx.auto_envvar_prefix}_{self.name.upper()}" + rv = os.environ.get(envvar) + + if rv: + return rv + + return None + + def value_from_envvar(self, ctx: Context) -> t.Any | None: + rv: t.Any | None = self.resolve_envvar_value(ctx) + + if rv is None: + return None + + value_depth = (self.nargs != 1) + bool(self.multiple) + + if value_depth > 0: + rv = self.type.split_envvar_value(rv) + + if self.multiple and self.nargs != 1: + rv = batch(rv, self.nargs) + + return rv + + def consume_value( + self, ctx: Context, opts: cabc.Mapping[str, Parameter] + ) -> tuple[t.Any, ParameterSource]: + value, source = super().consume_value(ctx, opts) + + # The parser will emit a sentinel value if the option can be + # given as a flag without a value. This is different from None + # to distinguish from the flag not being given at all. + if value is _flag_needs_value: + if self.prompt is not None and not ctx.resilient_parsing: + value = self.prompt_for_value(ctx) + source = ParameterSource.PROMPT + else: + value = self.flag_value + source = ParameterSource.COMMANDLINE + + elif ( + self.multiple + and value is not None + and any(v is _flag_needs_value for v in value) + ): + value = [self.flag_value if v is _flag_needs_value else v for v in value] + source = ParameterSource.COMMANDLINE + + # The value wasn't set, or used the param's default, prompt if + # prompting is enabled. + elif ( + source in {None, ParameterSource.DEFAULT} + and self.prompt is not None + and (self.required or self.prompt_required) + and not ctx.resilient_parsing + ): + value = self.prompt_for_value(ctx) + source = ParameterSource.PROMPT + + return value, source + + +class Argument(Parameter): + """Arguments are positional parameters to a command. They generally + provide fewer features than options but can have infinite ``nargs`` + and are required by default. + + All parameters are passed onwards to the constructor of :class:`Parameter`. + """ + + param_type_name = "argument" + + def __init__( + self, + param_decls: cabc.Sequence[str], + required: bool | None = None, + **attrs: t.Any, + ) -> None: + if required is None: + if attrs.get("default") is not None: + required = False + else: + required = attrs.get("nargs", 1) > 0 + + if "multiple" in attrs: + raise TypeError("__init__() got an unexpected keyword argument 'multiple'.") + + super().__init__(param_decls, required=required, **attrs) + + if __debug__: + if self.default is not None and self.nargs == -1: + raise TypeError("'default' is not supported for nargs=-1.") + + @property + def human_readable_name(self) -> str: + if self.metavar is not None: + return self.metavar + return self.name.upper() # type: ignore + + def make_metavar(self, ctx: Context) -> str: + if self.metavar is not None: + return self.metavar + var = self.type.get_metavar(param=self, ctx=ctx) + if not var: + var = self.name.upper() # type: ignore + if self.deprecated: + var += "!" + if not self.required: + var = f"[{var}]" + if self.nargs != 1: + var += "..." + return var + + def _parse_decls( + self, decls: cabc.Sequence[str], expose_value: bool + ) -> tuple[str | None, list[str], list[str]]: + if not decls: + if not expose_value: + return None, [], [] + raise TypeError("Argument is marked as exposed, but does not have a name.") + if len(decls) == 1: + name = arg = decls[0] + name = name.replace("-", "_").lower() + else: + raise TypeError( + "Arguments take exactly one parameter declaration, got" + f" {len(decls)}: {decls}." + ) + return name, [arg], [] + + def get_usage_pieces(self, ctx: Context) -> list[str]: + return [self.make_metavar(ctx)] + + def get_error_hint(self, ctx: Context) -> str: + return f"'{self.make_metavar(ctx)}'" + + def add_to_parser(self, parser: _OptionParser, ctx: Context) -> None: + parser.add_argument(dest=self.name, nargs=self.nargs, obj=self) + + +def __getattr__(name: str) -> object: + import warnings + + if name == "BaseCommand": + warnings.warn( + "'BaseCommand' is deprecated and will be removed in Click 9.0. Use" + " 'Command' instead.", + DeprecationWarning, + stacklevel=2, + ) + return _BaseCommand + + if name == "MultiCommand": + warnings.warn( + "'MultiCommand' is deprecated and will be removed in Click 9.0. Use" + " 'Group' instead.", + DeprecationWarning, + stacklevel=2, + ) + return _MultiCommand + + raise AttributeError(name) diff --git a/lib/python3.12/site-packages/click/decorators.py b/lib/python3.12/site-packages/click/decorators.py new file mode 100644 index 0000000000000000000000000000000000000000..21f4c342245fa93a7946b17d7661e0d2fa6a0c29 --- /dev/null +++ b/lib/python3.12/site-packages/click/decorators.py @@ -0,0 +1,551 @@ +from __future__ import annotations + +import inspect +import typing as t +from functools import update_wrapper +from gettext import gettext as _ + +from .core import Argument +from .core import Command +from .core import Context +from .core import Group +from .core import Option +from .core import Parameter +from .globals import get_current_context +from .utils import echo + +if t.TYPE_CHECKING: + import typing_extensions as te + + P = te.ParamSpec("P") + +R = t.TypeVar("R") +T = t.TypeVar("T") +_AnyCallable = t.Callable[..., t.Any] +FC = t.TypeVar("FC", bound="_AnyCallable | Command") + + +def pass_context(f: t.Callable[te.Concatenate[Context, P], R]) -> t.Callable[P, R]: + """Marks a callback as wanting to receive the current context + object as first argument. + """ + + def new_func(*args: P.args, **kwargs: P.kwargs) -> R: + return f(get_current_context(), *args, **kwargs) + + return update_wrapper(new_func, f) + + +def pass_obj(f: t.Callable[te.Concatenate[T, P], R]) -> t.Callable[P, R]: + """Similar to :func:`pass_context`, but only pass the object on the + context onwards (:attr:`Context.obj`). This is useful if that object + represents the state of a nested system. + """ + + def new_func(*args: P.args, **kwargs: P.kwargs) -> R: + return f(get_current_context().obj, *args, **kwargs) + + return update_wrapper(new_func, f) + + +def make_pass_decorator( + object_type: type[T], ensure: bool = False +) -> t.Callable[[t.Callable[te.Concatenate[T, P], R]], t.Callable[P, R]]: + """Given an object type this creates a decorator that will work + similar to :func:`pass_obj` but instead of passing the object of the + current context, it will find the innermost context of type + :func:`object_type`. + + This generates a decorator that works roughly like this:: + + from functools import update_wrapper + + def decorator(f): + @pass_context + def new_func(ctx, *args, **kwargs): + obj = ctx.find_object(object_type) + return ctx.invoke(f, obj, *args, **kwargs) + return update_wrapper(new_func, f) + return decorator + + :param object_type: the type of the object to pass. + :param ensure: if set to `True`, a new object will be created and + remembered on the context if it's not there yet. + """ + + def decorator(f: t.Callable[te.Concatenate[T, P], R]) -> t.Callable[P, R]: + def new_func(*args: P.args, **kwargs: P.kwargs) -> R: + ctx = get_current_context() + + obj: T | None + if ensure: + obj = ctx.ensure_object(object_type) + else: + obj = ctx.find_object(object_type) + + if obj is None: + raise RuntimeError( + "Managed to invoke callback without a context" + f" object of type {object_type.__name__!r}" + " existing." + ) + + return ctx.invoke(f, obj, *args, **kwargs) + + return update_wrapper(new_func, f) + + return decorator + + +def pass_meta_key( + key: str, *, doc_description: str | None = None +) -> t.Callable[[t.Callable[te.Concatenate[T, P], R]], t.Callable[P, R]]: + """Create a decorator that passes a key from + :attr:`click.Context.meta` as the first argument to the decorated + function. + + :param key: Key in ``Context.meta`` to pass. + :param doc_description: Description of the object being passed, + inserted into the decorator's docstring. Defaults to "the 'key' + key from Context.meta". + + .. versionadded:: 8.0 + """ + + def decorator(f: t.Callable[te.Concatenate[T, P], R]) -> t.Callable[P, R]: + def new_func(*args: P.args, **kwargs: P.kwargs) -> R: + ctx = get_current_context() + obj = ctx.meta[key] + return ctx.invoke(f, obj, *args, **kwargs) + + return update_wrapper(new_func, f) + + if doc_description is None: + doc_description = f"the {key!r} key from :attr:`click.Context.meta`" + + decorator.__doc__ = ( + f"Decorator that passes {doc_description} as the first argument" + " to the decorated function." + ) + return decorator + + +CmdType = t.TypeVar("CmdType", bound=Command) + + +# variant: no call, directly as decorator for a function. +@t.overload +def command(name: _AnyCallable) -> Command: ... + + +# variant: with positional name and with positional or keyword cls argument: +# @command(namearg, CommandCls, ...) or @command(namearg, cls=CommandCls, ...) +@t.overload +def command( + name: str | None, + cls: type[CmdType], + **attrs: t.Any, +) -> t.Callable[[_AnyCallable], CmdType]: ... + + +# variant: name omitted, cls _must_ be a keyword argument, @command(cls=CommandCls, ...) +@t.overload +def command( + name: None = None, + *, + cls: type[CmdType], + **attrs: t.Any, +) -> t.Callable[[_AnyCallable], CmdType]: ... + + +# variant: with optional string name, no cls argument provided. +@t.overload +def command( + name: str | None = ..., cls: None = None, **attrs: t.Any +) -> t.Callable[[_AnyCallable], Command]: ... + + +def command( + name: str | _AnyCallable | None = None, + cls: type[CmdType] | None = None, + **attrs: t.Any, +) -> Command | t.Callable[[_AnyCallable], Command | CmdType]: + r"""Creates a new :class:`Command` and uses the decorated function as + callback. This will also automatically attach all decorated + :func:`option`\s and :func:`argument`\s as parameters to the command. + + The name of the command defaults to the name of the function, converted to + lowercase, with underscores ``_`` replaced by dashes ``-``, and the suffixes + ``_command``, ``_cmd``, ``_group``, and ``_grp`` are removed. For example, + ``init_data_command`` becomes ``init-data``. + + All keyword arguments are forwarded to the underlying command class. + For the ``params`` argument, any decorated params are appended to + the end of the list. + + Once decorated the function turns into a :class:`Command` instance + that can be invoked as a command line utility or be attached to a + command :class:`Group`. + + :param name: The name of the command. Defaults to modifying the function's + name as described above. + :param cls: The command class to create. Defaults to :class:`Command`. + + .. versionchanged:: 8.2 + The suffixes ``_command``, ``_cmd``, ``_group``, and ``_grp`` are + removed when generating the name. + + .. versionchanged:: 8.1 + This decorator can be applied without parentheses. + + .. versionchanged:: 8.1 + The ``params`` argument can be used. Decorated params are + appended to the end of the list. + """ + + func: t.Callable[[_AnyCallable], t.Any] | None = None + + if callable(name): + func = name + name = None + assert cls is None, "Use 'command(cls=cls)(callable)' to specify a class." + assert not attrs, "Use 'command(**kwargs)(callable)' to provide arguments." + + if cls is None: + cls = t.cast("type[CmdType]", Command) + + def decorator(f: _AnyCallable) -> CmdType: + if isinstance(f, Command): + raise TypeError("Attempted to convert a callback into a command twice.") + + attr_params = attrs.pop("params", None) + params = attr_params if attr_params is not None else [] + + try: + decorator_params = f.__click_params__ # type: ignore + except AttributeError: + pass + else: + del f.__click_params__ # type: ignore + params.extend(reversed(decorator_params)) + + if attrs.get("help") is None: + attrs["help"] = f.__doc__ + + if t.TYPE_CHECKING: + assert cls is not None + assert not callable(name) + + if name is not None: + cmd_name = name + else: + cmd_name = f.__name__.lower().replace("_", "-") + cmd_left, sep, suffix = cmd_name.rpartition("-") + + if sep and suffix in {"command", "cmd", "group", "grp"}: + cmd_name = cmd_left + + cmd = cls(name=cmd_name, callback=f, params=params, **attrs) + cmd.__doc__ = f.__doc__ + return cmd + + if func is not None: + return decorator(func) + + return decorator + + +GrpType = t.TypeVar("GrpType", bound=Group) + + +# variant: no call, directly as decorator for a function. +@t.overload +def group(name: _AnyCallable) -> Group: ... + + +# variant: with positional name and with positional or keyword cls argument: +# @group(namearg, GroupCls, ...) or @group(namearg, cls=GroupCls, ...) +@t.overload +def group( + name: str | None, + cls: type[GrpType], + **attrs: t.Any, +) -> t.Callable[[_AnyCallable], GrpType]: ... + + +# variant: name omitted, cls _must_ be a keyword argument, @group(cmd=GroupCls, ...) +@t.overload +def group( + name: None = None, + *, + cls: type[GrpType], + **attrs: t.Any, +) -> t.Callable[[_AnyCallable], GrpType]: ... + + +# variant: with optional string name, no cls argument provided. +@t.overload +def group( + name: str | None = ..., cls: None = None, **attrs: t.Any +) -> t.Callable[[_AnyCallable], Group]: ... + + +def group( + name: str | _AnyCallable | None = None, + cls: type[GrpType] | None = None, + **attrs: t.Any, +) -> Group | t.Callable[[_AnyCallable], Group | GrpType]: + """Creates a new :class:`Group` with a function as callback. This + works otherwise the same as :func:`command` just that the `cls` + parameter is set to :class:`Group`. + + .. versionchanged:: 8.1 + This decorator can be applied without parentheses. + """ + if cls is None: + cls = t.cast("type[GrpType]", Group) + + if callable(name): + return command(cls=cls, **attrs)(name) + + return command(name, cls, **attrs) + + +def _param_memo(f: t.Callable[..., t.Any], param: Parameter) -> None: + if isinstance(f, Command): + f.params.append(param) + else: + if not hasattr(f, "__click_params__"): + f.__click_params__ = [] # type: ignore + + f.__click_params__.append(param) # type: ignore + + +def argument( + *param_decls: str, cls: type[Argument] | None = None, **attrs: t.Any +) -> t.Callable[[FC], FC]: + """Attaches an argument to the command. All positional arguments are + passed as parameter declarations to :class:`Argument`; all keyword + arguments are forwarded unchanged (except ``cls``). + This is equivalent to creating an :class:`Argument` instance manually + and attaching it to the :attr:`Command.params` list. + + For the default argument class, refer to :class:`Argument` and + :class:`Parameter` for descriptions of parameters. + + :param cls: the argument class to instantiate. This defaults to + :class:`Argument`. + :param param_decls: Passed as positional arguments to the constructor of + ``cls``. + :param attrs: Passed as keyword arguments to the constructor of ``cls``. + """ + if cls is None: + cls = Argument + + def decorator(f: FC) -> FC: + _param_memo(f, cls(param_decls, **attrs)) + return f + + return decorator + + +def option( + *param_decls: str, cls: type[Option] | None = None, **attrs: t.Any +) -> t.Callable[[FC], FC]: + """Attaches an option to the command. All positional arguments are + passed as parameter declarations to :class:`Option`; all keyword + arguments are forwarded unchanged (except ``cls``). + This is equivalent to creating an :class:`Option` instance manually + and attaching it to the :attr:`Command.params` list. + + For the default option class, refer to :class:`Option` and + :class:`Parameter` for descriptions of parameters. + + :param cls: the option class to instantiate. This defaults to + :class:`Option`. + :param param_decls: Passed as positional arguments to the constructor of + ``cls``. + :param attrs: Passed as keyword arguments to the constructor of ``cls``. + """ + if cls is None: + cls = Option + + def decorator(f: FC) -> FC: + _param_memo(f, cls(param_decls, **attrs)) + return f + + return decorator + + +def confirmation_option(*param_decls: str, **kwargs: t.Any) -> t.Callable[[FC], FC]: + """Add a ``--yes`` option which shows a prompt before continuing if + not passed. If the prompt is declined, the program will exit. + + :param param_decls: One or more option names. Defaults to the single + value ``"--yes"``. + :param kwargs: Extra arguments are passed to :func:`option`. + """ + + def callback(ctx: Context, param: Parameter, value: bool) -> None: + if not value: + ctx.abort() + + if not param_decls: + param_decls = ("--yes",) + + kwargs.setdefault("is_flag", True) + kwargs.setdefault("callback", callback) + kwargs.setdefault("expose_value", False) + kwargs.setdefault("prompt", "Do you want to continue?") + kwargs.setdefault("help", "Confirm the action without prompting.") + return option(*param_decls, **kwargs) + + +def password_option(*param_decls: str, **kwargs: t.Any) -> t.Callable[[FC], FC]: + """Add a ``--password`` option which prompts for a password, hiding + input and asking to enter the value again for confirmation. + + :param param_decls: One or more option names. Defaults to the single + value ``"--password"``. + :param kwargs: Extra arguments are passed to :func:`option`. + """ + if not param_decls: + param_decls = ("--password",) + + kwargs.setdefault("prompt", True) + kwargs.setdefault("confirmation_prompt", True) + kwargs.setdefault("hide_input", True) + return option(*param_decls, **kwargs) + + +def version_option( + version: str | None = None, + *param_decls: str, + package_name: str | None = None, + prog_name: str | None = None, + message: str | None = None, + **kwargs: t.Any, +) -> t.Callable[[FC], FC]: + """Add a ``--version`` option which immediately prints the version + number and exits the program. + + If ``version`` is not provided, Click will try to detect it using + :func:`importlib.metadata.version` to get the version for the + ``package_name``. + + If ``package_name`` is not provided, Click will try to detect it by + inspecting the stack frames. This will be used to detect the + version, so it must match the name of the installed package. + + :param version: The version number to show. If not provided, Click + will try to detect it. + :param param_decls: One or more option names. Defaults to the single + value ``"--version"``. + :param package_name: The package name to detect the version from. If + not provided, Click will try to detect it. + :param prog_name: The name of the CLI to show in the message. If not + provided, it will be detected from the command. + :param message: The message to show. The values ``%(prog)s``, + ``%(package)s``, and ``%(version)s`` are available. Defaults to + ``"%(prog)s, version %(version)s"``. + :param kwargs: Extra arguments are passed to :func:`option`. + :raise RuntimeError: ``version`` could not be detected. + + .. versionchanged:: 8.0 + Add the ``package_name`` parameter, and the ``%(package)s`` + value for messages. + + .. versionchanged:: 8.0 + Use :mod:`importlib.metadata` instead of ``pkg_resources``. The + version is detected based on the package name, not the entry + point name. The Python package name must match the installed + package name, or be passed with ``package_name=``. + """ + if message is None: + message = _("%(prog)s, version %(version)s") + + if version is None and package_name is None: + frame = inspect.currentframe() + f_back = frame.f_back if frame is not None else None + f_globals = f_back.f_globals if f_back is not None else None + # break reference cycle + # https://docs.python.org/3/library/inspect.html#the-interpreter-stack + del frame + + if f_globals is not None: + package_name = f_globals.get("__name__") + + if package_name == "__main__": + package_name = f_globals.get("__package__") + + if package_name: + package_name = package_name.partition(".")[0] + + def callback(ctx: Context, param: Parameter, value: bool) -> None: + if not value or ctx.resilient_parsing: + return + + nonlocal prog_name + nonlocal version + + if prog_name is None: + prog_name = ctx.find_root().info_name + + if version is None and package_name is not None: + import importlib.metadata + + try: + version = importlib.metadata.version(package_name) + except importlib.metadata.PackageNotFoundError: + raise RuntimeError( + f"{package_name!r} is not installed. Try passing" + " 'package_name' instead." + ) from None + + if version is None: + raise RuntimeError( + f"Could not determine the version for {package_name!r} automatically." + ) + + echo( + message % {"prog": prog_name, "package": package_name, "version": version}, + color=ctx.color, + ) + ctx.exit() + + if not param_decls: + param_decls = ("--version",) + + kwargs.setdefault("is_flag", True) + kwargs.setdefault("expose_value", False) + kwargs.setdefault("is_eager", True) + kwargs.setdefault("help", _("Show the version and exit.")) + kwargs["callback"] = callback + return option(*param_decls, **kwargs) + + +def help_option(*param_decls: str, **kwargs: t.Any) -> t.Callable[[FC], FC]: + """Pre-configured ``--help`` option which immediately prints the help page + and exits the program. + + :param param_decls: One or more option names. Defaults to the single + value ``"--help"``. + :param kwargs: Extra arguments are passed to :func:`option`. + """ + + def show_help(ctx: Context, param: Parameter, value: bool) -> None: + """Callback that print the help page on ```` and exits.""" + if value and not ctx.resilient_parsing: + echo(ctx.get_help(), color=ctx.color) + ctx.exit() + + if not param_decls: + param_decls = ("--help",) + + kwargs.setdefault("is_flag", True) + kwargs.setdefault("expose_value", False) + kwargs.setdefault("is_eager", True) + kwargs.setdefault("help", _("Show this message and exit.")) + kwargs.setdefault("callback", show_help) + + return option(*param_decls, **kwargs) diff --git a/lib/python3.12/site-packages/click/exceptions.py b/lib/python3.12/site-packages/click/exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..f141a832e1ab678709998c3bbafcdea6561b9661 --- /dev/null +++ b/lib/python3.12/site-packages/click/exceptions.py @@ -0,0 +1,308 @@ +from __future__ import annotations + +import collections.abc as cabc +import typing as t +from gettext import gettext as _ +from gettext import ngettext + +from ._compat import get_text_stderr +from .globals import resolve_color_default +from .utils import echo +from .utils import format_filename + +if t.TYPE_CHECKING: + from .core import Command + from .core import Context + from .core import Parameter + + +def _join_param_hints(param_hint: cabc.Sequence[str] | str | None) -> str | None: + if param_hint is not None and not isinstance(param_hint, str): + return " / ".join(repr(x) for x in param_hint) + + return param_hint + + +class ClickException(Exception): + """An exception that Click can handle and show to the user.""" + + #: The exit code for this exception. + exit_code = 1 + + def __init__(self, message: str) -> None: + super().__init__(message) + # The context will be removed by the time we print the message, so cache + # the color settings here to be used later on (in `show`) + self.show_color: bool | None = resolve_color_default() + self.message = message + + def format_message(self) -> str: + return self.message + + def __str__(self) -> str: + return self.message + + def show(self, file: t.IO[t.Any] | None = None) -> None: + if file is None: + file = get_text_stderr() + + echo( + _("Error: {message}").format(message=self.format_message()), + file=file, + color=self.show_color, + ) + + +class UsageError(ClickException): + """An internal exception that signals a usage error. This typically + aborts any further handling. + + :param message: the error message to display. + :param ctx: optionally the context that caused this error. Click will + fill in the context automatically in some situations. + """ + + exit_code = 2 + + def __init__(self, message: str, ctx: Context | None = None) -> None: + super().__init__(message) + self.ctx = ctx + self.cmd: Command | None = self.ctx.command if self.ctx else None + + def show(self, file: t.IO[t.Any] | None = None) -> None: + if file is None: + file = get_text_stderr() + color = None + hint = "" + if ( + self.ctx is not None + and self.ctx.command.get_help_option(self.ctx) is not None + ): + hint = _("Try '{command} {option}' for help.").format( + command=self.ctx.command_path, option=self.ctx.help_option_names[0] + ) + hint = f"{hint}\n" + if self.ctx is not None: + color = self.ctx.color + echo(f"{self.ctx.get_usage()}\n{hint}", file=file, color=color) + echo( + _("Error: {message}").format(message=self.format_message()), + file=file, + color=color, + ) + + +class BadParameter(UsageError): + """An exception that formats out a standardized error message for a + bad parameter. This is useful when thrown from a callback or type as + Click will attach contextual information to it (for instance, which + parameter it is). + + .. versionadded:: 2.0 + + :param param: the parameter object that caused this error. This can + be left out, and Click will attach this info itself + if possible. + :param param_hint: a string that shows up as parameter name. This + can be used as alternative to `param` in cases + where custom validation should happen. If it is + a string it's used as such, if it's a list then + each item is quoted and separated. + """ + + def __init__( + self, + message: str, + ctx: Context | None = None, + param: Parameter | None = None, + param_hint: str | None = None, + ) -> None: + super().__init__(message, ctx) + self.param = param + self.param_hint = param_hint + + def format_message(self) -> str: + if self.param_hint is not None: + param_hint = self.param_hint + elif self.param is not None: + param_hint = self.param.get_error_hint(self.ctx) # type: ignore + else: + return _("Invalid value: {message}").format(message=self.message) + + return _("Invalid value for {param_hint}: {message}").format( + param_hint=_join_param_hints(param_hint), message=self.message + ) + + +class MissingParameter(BadParameter): + """Raised if click required an option or argument but it was not + provided when invoking the script. + + .. versionadded:: 4.0 + + :param param_type: a string that indicates the type of the parameter. + The default is to inherit the parameter type from + the given `param`. Valid values are ``'parameter'``, + ``'option'`` or ``'argument'``. + """ + + def __init__( + self, + message: str | None = None, + ctx: Context | None = None, + param: Parameter | None = None, + param_hint: str | None = None, + param_type: str | None = None, + ) -> None: + super().__init__(message or "", ctx, param, param_hint) + self.param_type = param_type + + def format_message(self) -> str: + if self.param_hint is not None: + param_hint: str | None = self.param_hint + elif self.param is not None: + param_hint = self.param.get_error_hint(self.ctx) # type: ignore + else: + param_hint = None + + param_hint = _join_param_hints(param_hint) + param_hint = f" {param_hint}" if param_hint else "" + + param_type = self.param_type + if param_type is None and self.param is not None: + param_type = self.param.param_type_name + + msg = self.message + if self.param is not None: + msg_extra = self.param.type.get_missing_message( + param=self.param, ctx=self.ctx + ) + if msg_extra: + if msg: + msg += f". {msg_extra}" + else: + msg = msg_extra + + msg = f" {msg}" if msg else "" + + # Translate param_type for known types. + if param_type == "argument": + missing = _("Missing argument") + elif param_type == "option": + missing = _("Missing option") + elif param_type == "parameter": + missing = _("Missing parameter") + else: + missing = _("Missing {param_type}").format(param_type=param_type) + + return f"{missing}{param_hint}.{msg}" + + def __str__(self) -> str: + if not self.message: + param_name = self.param.name if self.param else None + return _("Missing parameter: {param_name}").format(param_name=param_name) + else: + return self.message + + +class NoSuchOption(UsageError): + """Raised if click attempted to handle an option that does not + exist. + + .. versionadded:: 4.0 + """ + + def __init__( + self, + option_name: str, + message: str | None = None, + possibilities: cabc.Sequence[str] | None = None, + ctx: Context | None = None, + ) -> None: + if message is None: + message = _("No such option: {name}").format(name=option_name) + + super().__init__(message, ctx) + self.option_name = option_name + self.possibilities = possibilities + + def format_message(self) -> str: + if not self.possibilities: + return self.message + + possibility_str = ", ".join(sorted(self.possibilities)) + suggest = ngettext( + "Did you mean {possibility}?", + "(Possible options: {possibilities})", + len(self.possibilities), + ).format(possibility=possibility_str, possibilities=possibility_str) + return f"{self.message} {suggest}" + + +class BadOptionUsage(UsageError): + """Raised if an option is generally supplied but the use of the option + was incorrect. This is for instance raised if the number of arguments + for an option is not correct. + + .. versionadded:: 4.0 + + :param option_name: the name of the option being used incorrectly. + """ + + def __init__( + self, option_name: str, message: str, ctx: Context | None = None + ) -> None: + super().__init__(message, ctx) + self.option_name = option_name + + +class BadArgumentUsage(UsageError): + """Raised if an argument is generally supplied but the use of the argument + was incorrect. This is for instance raised if the number of values + for an argument is not correct. + + .. versionadded:: 6.0 + """ + + +class NoArgsIsHelpError(UsageError): + def __init__(self, ctx: Context) -> None: + self.ctx: Context + super().__init__(ctx.get_help(), ctx=ctx) + + def show(self, file: t.IO[t.Any] | None = None) -> None: + echo(self.format_message(), file=file, err=True, color=self.ctx.color) + + +class FileError(ClickException): + """Raised if a file cannot be opened.""" + + def __init__(self, filename: str, hint: str | None = None) -> None: + if hint is None: + hint = _("unknown error") + + super().__init__(hint) + self.ui_filename: str = format_filename(filename) + self.filename = filename + + def format_message(self) -> str: + return _("Could not open file {filename!r}: {message}").format( + filename=self.ui_filename, message=self.message + ) + + +class Abort(RuntimeError): + """An internal signalling exception that signals Click to abort.""" + + +class Exit(RuntimeError): + """An exception that indicates that the application should exit with some + status code. + + :param code: the status code to exit with. + """ + + __slots__ = ("exit_code",) + + def __init__(self, code: int = 0) -> None: + self.exit_code: int = code diff --git a/lib/python3.12/site-packages/click/formatting.py b/lib/python3.12/site-packages/click/formatting.py new file mode 100644 index 0000000000000000000000000000000000000000..9891f8809feff24a529ce47a860589c129835322 --- /dev/null +++ b/lib/python3.12/site-packages/click/formatting.py @@ -0,0 +1,301 @@ +from __future__ import annotations + +import collections.abc as cabc +from contextlib import contextmanager +from gettext import gettext as _ + +from ._compat import term_len +from .parser import _split_opt + +# Can force a width. This is used by the test system +FORCED_WIDTH: int | None = None + + +def measure_table(rows: cabc.Iterable[tuple[str, str]]) -> tuple[int, ...]: + widths: dict[int, int] = {} + + for row in rows: + for idx, col in enumerate(row): + widths[idx] = max(widths.get(idx, 0), term_len(col)) + + return tuple(y for x, y in sorted(widths.items())) + + +def iter_rows( + rows: cabc.Iterable[tuple[str, str]], col_count: int +) -> cabc.Iterator[tuple[str, ...]]: + for row in rows: + yield row + ("",) * (col_count - len(row)) + + +def wrap_text( + text: str, + width: int = 78, + initial_indent: str = "", + subsequent_indent: str = "", + preserve_paragraphs: bool = False, +) -> str: + """A helper function that intelligently wraps text. By default, it + assumes that it operates on a single paragraph of text but if the + `preserve_paragraphs` parameter is provided it will intelligently + handle paragraphs (defined by two empty lines). + + If paragraphs are handled, a paragraph can be prefixed with an empty + line containing the ``\\b`` character (``\\x08``) to indicate that + no rewrapping should happen in that block. + + :param text: the text that should be rewrapped. + :param width: the maximum width for the text. + :param initial_indent: the initial indent that should be placed on the + first line as a string. + :param subsequent_indent: the indent string that should be placed on + each consecutive line. + :param preserve_paragraphs: if this flag is set then the wrapping will + intelligently handle paragraphs. + """ + from ._textwrap import TextWrapper + + text = text.expandtabs() + wrapper = TextWrapper( + width, + initial_indent=initial_indent, + subsequent_indent=subsequent_indent, + replace_whitespace=False, + ) + if not preserve_paragraphs: + return wrapper.fill(text) + + p: list[tuple[int, bool, str]] = [] + buf: list[str] = [] + indent = None + + def _flush_par() -> None: + if not buf: + return + if buf[0].strip() == "\b": + p.append((indent or 0, True, "\n".join(buf[1:]))) + else: + p.append((indent or 0, False, " ".join(buf))) + del buf[:] + + for line in text.splitlines(): + if not line: + _flush_par() + indent = None + else: + if indent is None: + orig_len = term_len(line) + line = line.lstrip() + indent = orig_len - term_len(line) + buf.append(line) + _flush_par() + + rv = [] + for indent, raw, text in p: + with wrapper.extra_indent(" " * indent): + if raw: + rv.append(wrapper.indent_only(text)) + else: + rv.append(wrapper.fill(text)) + + return "\n\n".join(rv) + + +class HelpFormatter: + """This class helps with formatting text-based help pages. It's + usually just needed for very special internal cases, but it's also + exposed so that developers can write their own fancy outputs. + + At present, it always writes into memory. + + :param indent_increment: the additional increment for each level. + :param width: the width for the text. This defaults to the terminal + width clamped to a maximum of 78. + """ + + def __init__( + self, + indent_increment: int = 2, + width: int | None = None, + max_width: int | None = None, + ) -> None: + import shutil + + self.indent_increment = indent_increment + if max_width is None: + max_width = 80 + if width is None: + width = FORCED_WIDTH + if width is None: + width = max(min(shutil.get_terminal_size().columns, max_width) - 2, 50) + self.width = width + self.current_indent: int = 0 + self.buffer: list[str] = [] + + def write(self, string: str) -> None: + """Writes a unicode string into the internal buffer.""" + self.buffer.append(string) + + def indent(self) -> None: + """Increases the indentation.""" + self.current_indent += self.indent_increment + + def dedent(self) -> None: + """Decreases the indentation.""" + self.current_indent -= self.indent_increment + + def write_usage(self, prog: str, args: str = "", prefix: str | None = None) -> None: + """Writes a usage line into the buffer. + + :param prog: the program name. + :param args: whitespace separated list of arguments. + :param prefix: The prefix for the first line. Defaults to + ``"Usage: "``. + """ + if prefix is None: + prefix = f"{_('Usage:')} " + + usage_prefix = f"{prefix:>{self.current_indent}}{prog} " + text_width = self.width - self.current_indent + + if text_width >= (term_len(usage_prefix) + 20): + # The arguments will fit to the right of the prefix. + indent = " " * term_len(usage_prefix) + self.write( + wrap_text( + args, + text_width, + initial_indent=usage_prefix, + subsequent_indent=indent, + ) + ) + else: + # The prefix is too long, put the arguments on the next line. + self.write(usage_prefix) + self.write("\n") + indent = " " * (max(self.current_indent, term_len(prefix)) + 4) + self.write( + wrap_text( + args, text_width, initial_indent=indent, subsequent_indent=indent + ) + ) + + self.write("\n") + + def write_heading(self, heading: str) -> None: + """Writes a heading into the buffer.""" + self.write(f"{'':>{self.current_indent}}{heading}:\n") + + def write_paragraph(self) -> None: + """Writes a paragraph into the buffer.""" + if self.buffer: + self.write("\n") + + def write_text(self, text: str) -> None: + """Writes re-indented text into the buffer. This rewraps and + preserves paragraphs. + """ + indent = " " * self.current_indent + self.write( + wrap_text( + text, + self.width, + initial_indent=indent, + subsequent_indent=indent, + preserve_paragraphs=True, + ) + ) + self.write("\n") + + def write_dl( + self, + rows: cabc.Sequence[tuple[str, str]], + col_max: int = 30, + col_spacing: int = 2, + ) -> None: + """Writes a definition list into the buffer. This is how options + and commands are usually formatted. + + :param rows: a list of two item tuples for the terms and values. + :param col_max: the maximum width of the first column. + :param col_spacing: the number of spaces between the first and + second column. + """ + rows = list(rows) + widths = measure_table(rows) + if len(widths) != 2: + raise TypeError("Expected two columns for definition list") + + first_col = min(widths[0], col_max) + col_spacing + + for first, second in iter_rows(rows, len(widths)): + self.write(f"{'':>{self.current_indent}}{first}") + if not second: + self.write("\n") + continue + if term_len(first) <= first_col - col_spacing: + self.write(" " * (first_col - term_len(first))) + else: + self.write("\n") + self.write(" " * (first_col + self.current_indent)) + + text_width = max(self.width - first_col - 2, 10) + wrapped_text = wrap_text(second, text_width, preserve_paragraphs=True) + lines = wrapped_text.splitlines() + + if lines: + self.write(f"{lines[0]}\n") + + for line in lines[1:]: + self.write(f"{'':>{first_col + self.current_indent}}{line}\n") + else: + self.write("\n") + + @contextmanager + def section(self, name: str) -> cabc.Iterator[None]: + """Helpful context manager that writes a paragraph, a heading, + and the indents. + + :param name: the section name that is written as heading. + """ + self.write_paragraph() + self.write_heading(name) + self.indent() + try: + yield + finally: + self.dedent() + + @contextmanager + def indentation(self) -> cabc.Iterator[None]: + """A context manager that increases the indentation.""" + self.indent() + try: + yield + finally: + self.dedent() + + def getvalue(self) -> str: + """Returns the buffer contents.""" + return "".join(self.buffer) + + +def join_options(options: cabc.Sequence[str]) -> tuple[str, bool]: + """Given a list of option strings this joins them in the most appropriate + way and returns them in the form ``(formatted_string, + any_prefix_is_slash)`` where the second item in the tuple is a flag that + indicates if any of the option prefixes was a slash. + """ + rv = [] + any_prefix_is_slash = False + + for opt in options: + prefix = _split_opt(opt)[0] + + if prefix == "/": + any_prefix_is_slash = True + + rv.append((len(prefix), opt)) + + rv.sort(key=lambda x: x[0]) + return ", ".join(x[1] for x in rv), any_prefix_is_slash diff --git a/lib/python3.12/site-packages/click/globals.py b/lib/python3.12/site-packages/click/globals.py new file mode 100644 index 0000000000000000000000000000000000000000..a2f91723d21cefdf658be327174e1f8ccdc20fcd --- /dev/null +++ b/lib/python3.12/site-packages/click/globals.py @@ -0,0 +1,67 @@ +from __future__ import annotations + +import typing as t +from threading import local + +if t.TYPE_CHECKING: + from .core import Context + +_local = local() + + +@t.overload +def get_current_context(silent: t.Literal[False] = False) -> Context: ... + + +@t.overload +def get_current_context(silent: bool = ...) -> Context | None: ... + + +def get_current_context(silent: bool = False) -> Context | None: + """Returns the current click context. This can be used as a way to + access the current context object from anywhere. This is a more implicit + alternative to the :func:`pass_context` decorator. This function is + primarily useful for helpers such as :func:`echo` which might be + interested in changing its behavior based on the current context. + + To push the current context, :meth:`Context.scope` can be used. + + .. versionadded:: 5.0 + + :param silent: if set to `True` the return value is `None` if no context + is available. The default behavior is to raise a + :exc:`RuntimeError`. + """ + try: + return t.cast("Context", _local.stack[-1]) + except (AttributeError, IndexError) as e: + if not silent: + raise RuntimeError("There is no active click context.") from e + + return None + + +def push_context(ctx: Context) -> None: + """Pushes a new context to the current stack.""" + _local.__dict__.setdefault("stack", []).append(ctx) + + +def pop_context() -> None: + """Removes the top level from the stack.""" + _local.stack.pop() + + +def resolve_color_default(color: bool | None = None) -> bool | None: + """Internal helper to get the default value of the color flag. If a + value is passed it's returned unchanged, otherwise it's looked up from + the current context. + """ + if color is not None: + return color + + ctx = get_current_context(silent=True) + + if ctx is not None: + return ctx.color + + return None diff --git a/lib/python3.12/site-packages/click/parser.py b/lib/python3.12/site-packages/click/parser.py new file mode 100644 index 0000000000000000000000000000000000000000..a8b7d2634a89df3c9e0b7a1f85dfd4bace60a608 --- /dev/null +++ b/lib/python3.12/site-packages/click/parser.py @@ -0,0 +1,532 @@ +""" +This module started out as largely a copy paste from the stdlib's +optparse module with the features removed that we do not need from +optparse because we implement them in Click on a higher level (for +instance type handling, help formatting and a lot more). + +The plan is to remove more and more from here over time. + +The reason this is a different module and not optparse from the stdlib +is that there are differences in 2.x and 3.x about the error messages +generated and optparse in the stdlib uses gettext for no good reason +and might cause us issues. + +Click uses parts of optparse written by Gregory P. Ward and maintained +by the Python Software Foundation. This is limited to code in parser.py. + +Copyright 2001-2006 Gregory P. Ward. All rights reserved. +Copyright 2002-2006 Python Software Foundation. All rights reserved. +""" + +# This code uses parts of optparse written by Gregory P. Ward and +# maintained by the Python Software Foundation. +# Copyright 2001-2006 Gregory P. Ward +# Copyright 2002-2006 Python Software Foundation +from __future__ import annotations + +import collections.abc as cabc +import typing as t +from collections import deque +from gettext import gettext as _ +from gettext import ngettext + +from .exceptions import BadArgumentUsage +from .exceptions import BadOptionUsage +from .exceptions import NoSuchOption +from .exceptions import UsageError + +if t.TYPE_CHECKING: + from .core import Argument as CoreArgument + from .core import Context + from .core import Option as CoreOption + from .core import Parameter as CoreParameter + +V = t.TypeVar("V") + +# Sentinel value that indicates an option was passed as a flag without a +# value but is not a flag option. Option.consume_value uses this to +# prompt or use the flag_value. +_flag_needs_value = object() + + +def _unpack_args( + args: cabc.Sequence[str], nargs_spec: cabc.Sequence[int] +) -> tuple[cabc.Sequence[str | cabc.Sequence[str | None] | None], list[str]]: + """Given an iterable of arguments and an iterable of nargs specifications, + it returns a tuple with all the unpacked arguments at the first index + and all remaining arguments as the second. + + The nargs specification is the number of arguments that should be consumed + or `-1` to indicate that this position should eat up all the remainders. + + Missing items are filled with `None`. + """ + args = deque(args) + nargs_spec = deque(nargs_spec) + rv: list[str | tuple[str | None, ...] | None] = [] + spos: int | None = None + + def _fetch(c: deque[V]) -> V | None: + try: + if spos is None: + return c.popleft() + else: + return c.pop() + except IndexError: + return None + + while nargs_spec: + nargs = _fetch(nargs_spec) + + if nargs is None: + continue + + if nargs == 1: + rv.append(_fetch(args)) + elif nargs > 1: + x = [_fetch(args) for _ in range(nargs)] + + # If we're reversed, we're pulling in the arguments in reverse, + # so we need to turn them around. + if spos is not None: + x.reverse() + + rv.append(tuple(x)) + elif nargs < 0: + if spos is not None: + raise TypeError("Cannot have two nargs < 0") + + spos = len(rv) + rv.append(None) + + # spos is the position of the wildcard (star). If it's not `None`, + # we fill it with the remainder. + if spos is not None: + rv[spos] = tuple(args) + args = [] + rv[spos + 1 :] = reversed(rv[spos + 1 :]) + + return tuple(rv), list(args) + + +def _split_opt(opt: str) -> tuple[str, str]: + first = opt[:1] + if first.isalnum(): + return "", opt + if opt[1:2] == first: + return opt[:2], opt[2:] + return first, opt[1:] + + +def _normalize_opt(opt: str, ctx: Context | None) -> str: + if ctx is None or ctx.token_normalize_func is None: + return opt + prefix, opt = _split_opt(opt) + return f"{prefix}{ctx.token_normalize_func(opt)}" + + +class _Option: + def __init__( + self, + obj: CoreOption, + opts: cabc.Sequence[str], + dest: str | None, + action: str | None = None, + nargs: int = 1, + const: t.Any | None = None, + ): + self._short_opts = [] + self._long_opts = [] + self.prefixes: set[str] = set() + + for opt in opts: + prefix, value = _split_opt(opt) + if not prefix: + raise ValueError(f"Invalid start character for option ({opt})") + self.prefixes.add(prefix[0]) + if len(prefix) == 1 and len(value) == 1: + self._short_opts.append(opt) + else: + self._long_opts.append(opt) + self.prefixes.add(prefix) + + if action is None: + action = "store" + + self.dest = dest + self.action = action + self.nargs = nargs + self.const = const + self.obj = obj + + @property + def takes_value(self) -> bool: + return self.action in ("store", "append") + + def process(self, value: t.Any, state: _ParsingState) -> None: + if self.action == "store": + state.opts[self.dest] = value # type: ignore + elif self.action == "store_const": + state.opts[self.dest] = self.const # type: ignore + elif self.action == "append": + state.opts.setdefault(self.dest, []).append(value) # type: ignore + elif self.action == "append_const": + state.opts.setdefault(self.dest, []).append(self.const) # type: ignore + elif self.action == "count": + state.opts[self.dest] = state.opts.get(self.dest, 0) + 1 # type: ignore + else: + raise ValueError(f"unknown action '{self.action}'") + state.order.append(self.obj) + + +class _Argument: + def __init__(self, obj: CoreArgument, dest: str | None, nargs: int = 1): + self.dest = dest + self.nargs = nargs + self.obj = obj + + def process( + self, + value: str | cabc.Sequence[str | None] | None, + state: _ParsingState, + ) -> None: + if self.nargs > 1: + assert value is not None + holes = sum(1 for x in value if x is None) + if holes == len(value): + value = None + elif holes != 0: + raise BadArgumentUsage( + _("Argument {name!r} takes {nargs} values.").format( + name=self.dest, nargs=self.nargs + ) + ) + + if self.nargs == -1 and self.obj.envvar is not None and value == (): + # Replace empty tuple with None so that a value from the + # environment may be tried. + value = None + + state.opts[self.dest] = value # type: ignore + state.order.append(self.obj) + + +class _ParsingState: + def __init__(self, rargs: list[str]) -> None: + self.opts: dict[str, t.Any] = {} + self.largs: list[str] = [] + self.rargs = rargs + self.order: list[CoreParameter] = [] + + +class _OptionParser: + """The option parser is an internal class that is ultimately used to + parse options and arguments. It's modelled after optparse and brings + a similar but vastly simplified API. It should generally not be used + directly as the high level Click classes wrap it for you. + + It's not nearly as extensible as optparse or argparse as it does not + implement features that are implemented on a higher level (such as + types or defaults). + + :param ctx: optionally the :class:`~click.Context` where this parser + should go with. + + .. deprecated:: 8.2 + Will be removed in Click 9.0. + """ + + def __init__(self, ctx: Context | None = None) -> None: + #: The :class:`~click.Context` for this parser. This might be + #: `None` for some advanced use cases. + self.ctx = ctx + #: This controls how the parser deals with interspersed arguments. + #: If this is set to `False`, the parser will stop on the first + #: non-option. Click uses this to implement nested subcommands + #: safely. + self.allow_interspersed_args: bool = True + #: This tells the parser how to deal with unknown options. By + #: default it will error out (which is sensible), but there is a + #: second mode where it will ignore it and continue processing + #: after shifting all the unknown options into the resulting args. + self.ignore_unknown_options: bool = False + + if ctx is not None: + self.allow_interspersed_args = ctx.allow_interspersed_args + self.ignore_unknown_options = ctx.ignore_unknown_options + + self._short_opt: dict[str, _Option] = {} + self._long_opt: dict[str, _Option] = {} + self._opt_prefixes = {"-", "--"} + self._args: list[_Argument] = [] + + def add_option( + self, + obj: CoreOption, + opts: cabc.Sequence[str], + dest: str | None, + action: str | None = None, + nargs: int = 1, + const: t.Any | None = None, + ) -> None: + """Adds a new option named `dest` to the parser. The destination + is not inferred (unlike with optparse) and needs to be explicitly + provided. Action can be any of ``store``, ``store_const``, + ``append``, ``append_const`` or ``count``. + + The `obj` can be used to identify the option in the order list + that is returned from the parser. + """ + opts = [_normalize_opt(opt, self.ctx) for opt in opts] + option = _Option(obj, opts, dest, action=action, nargs=nargs, const=const) + self._opt_prefixes.update(option.prefixes) + for opt in option._short_opts: + self._short_opt[opt] = option + for opt in option._long_opts: + self._long_opt[opt] = option + + def add_argument(self, obj: CoreArgument, dest: str | None, nargs: int = 1) -> None: + """Adds a positional argument named `dest` to the parser. + + The `obj` can be used to identify the option in the order list + that is returned from the parser. + """ + self._args.append(_Argument(obj, dest=dest, nargs=nargs)) + + def parse_args( + self, args: list[str] + ) -> tuple[dict[str, t.Any], list[str], list[CoreParameter]]: + """Parses positional arguments and returns ``(values, args, order)`` + for the parsed options and arguments as well as the leftover + arguments if there are any. The order is a list of objects as they + appear on the command line. If arguments appear multiple times they + will be memorized multiple times as well. + """ + state = _ParsingState(args) + try: + self._process_args_for_options(state) + self._process_args_for_args(state) + except UsageError: + if self.ctx is None or not self.ctx.resilient_parsing: + raise + return state.opts, state.largs, state.order + + def _process_args_for_args(self, state: _ParsingState) -> None: + pargs, args = _unpack_args( + state.largs + state.rargs, [x.nargs for x in self._args] + ) + + for idx, arg in enumerate(self._args): + arg.process(pargs[idx], state) + + state.largs = args + state.rargs = [] + + def _process_args_for_options(self, state: _ParsingState) -> None: + while state.rargs: + arg = state.rargs.pop(0) + arglen = len(arg) + # Double dashes always handled explicitly regardless of what + # prefixes are valid. + if arg == "--": + return + elif arg[:1] in self._opt_prefixes and arglen > 1: + self._process_opts(arg, state) + elif self.allow_interspersed_args: + state.largs.append(arg) + else: + state.rargs.insert(0, arg) + return + + # Say this is the original argument list: + # [arg0, arg1, ..., arg(i-1), arg(i), arg(i+1), ..., arg(N-1)] + # ^ + # (we are about to process arg(i)). + # + # Then rargs is [arg(i), ..., arg(N-1)] and largs is a *subset* of + # [arg0, ..., arg(i-1)] (any options and their arguments will have + # been removed from largs). + # + # The while loop will usually consume 1 or more arguments per pass. + # If it consumes 1 (eg. arg is an option that takes no arguments), + # then after _process_arg() is done the situation is: + # + # largs = subset of [arg0, ..., arg(i)] + # rargs = [arg(i+1), ..., arg(N-1)] + # + # If allow_interspersed_args is false, largs will always be + # *empty* -- still a subset of [arg0, ..., arg(i-1)], but + # not a very interesting subset! + + def _match_long_opt( + self, opt: str, explicit_value: str | None, state: _ParsingState + ) -> None: + if opt not in self._long_opt: + from difflib import get_close_matches + + possibilities = get_close_matches(opt, self._long_opt) + raise NoSuchOption(opt, possibilities=possibilities, ctx=self.ctx) + + option = self._long_opt[opt] + if option.takes_value: + # At this point it's safe to modify rargs by injecting the + # explicit value, because no exception is raised in this + # branch. This means that the inserted value will be fully + # consumed. + if explicit_value is not None: + state.rargs.insert(0, explicit_value) + + value = self._get_value_from_state(opt, option, state) + + elif explicit_value is not None: + raise BadOptionUsage( + opt, _("Option {name!r} does not take a value.").format(name=opt) + ) + + else: + value = None + + option.process(value, state) + + def _match_short_opt(self, arg: str, state: _ParsingState) -> None: + stop = False + i = 1 + prefix = arg[0] + unknown_options = [] + + for ch in arg[1:]: + opt = _normalize_opt(f"{prefix}{ch}", self.ctx) + option = self._short_opt.get(opt) + i += 1 + + if not option: + if self.ignore_unknown_options: + unknown_options.append(ch) + continue + raise NoSuchOption(opt, ctx=self.ctx) + if option.takes_value: + # Any characters left in arg? Pretend they're the + # next arg, and stop consuming characters of arg. + if i < len(arg): + state.rargs.insert(0, arg[i:]) + stop = True + + value = self._get_value_from_state(opt, option, state) + + else: + value = None + + option.process(value, state) + + if stop: + break + + # If we got any unknown options we recombine the string of the + # remaining options and re-attach the prefix, then report that + # to the state as new larg. This way there is basic combinatorics + # that can be achieved while still ignoring unknown arguments. + if self.ignore_unknown_options and unknown_options: + state.largs.append(f"{prefix}{''.join(unknown_options)}") + + def _get_value_from_state( + self, option_name: str, option: _Option, state: _ParsingState + ) -> t.Any: + nargs = option.nargs + + if len(state.rargs) < nargs: + if option.obj._flag_needs_value: + # Option allows omitting the value. + value = _flag_needs_value + else: + raise BadOptionUsage( + option_name, + ngettext( + "Option {name!r} requires an argument.", + "Option {name!r} requires {nargs} arguments.", + nargs, + ).format(name=option_name, nargs=nargs), + ) + elif nargs == 1: + next_rarg = state.rargs[0] + + if ( + option.obj._flag_needs_value + and isinstance(next_rarg, str) + and next_rarg[:1] in self._opt_prefixes + and len(next_rarg) > 1 + ): + # The next arg looks like the start of an option, don't + # use it as the value if omitting the value is allowed. + value = _flag_needs_value + else: + value = state.rargs.pop(0) + else: + value = tuple(state.rargs[:nargs]) + del state.rargs[:nargs] + + return value + + def _process_opts(self, arg: str, state: _ParsingState) -> None: + explicit_value = None + # Long option handling happens in two parts. The first part is + # supporting explicitly attached values. In any case, we will try + # to long match the option first. + if "=" in arg: + long_opt, explicit_value = arg.split("=", 1) + else: + long_opt = arg + norm_long_opt = _normalize_opt(long_opt, self.ctx) + + # At this point we will match the (assumed) long option through + # the long option matching code. Note that this allows options + # like "-foo" to be matched as long options. + try: + self._match_long_opt(norm_long_opt, explicit_value, state) + except NoSuchOption: + # At this point the long option matching failed, and we need + # to try with short options. However there is a special rule + # which says, that if we have a two character options prefix + # (applies to "--foo" for instance), we do not dispatch to the + # short option code and will instead raise the no option + # error. + if arg[:2] not in self._opt_prefixes: + self._match_short_opt(arg, state) + return + + if not self.ignore_unknown_options: + raise + + state.largs.append(arg) + + +def __getattr__(name: str) -> object: + import warnings + + if name in { + "OptionParser", + "Argument", + "Option", + "split_opt", + "normalize_opt", + "ParsingState", + }: + warnings.warn( + f"'parser.{name}' is deprecated and will be removed in Click 9.0." + " The old parser is available in 'optparse'.", + DeprecationWarning, + stacklevel=2, + ) + return globals()[f"_{name}"] + + if name == "split_arg_string": + from .shell_completion import split_arg_string + + warnings.warn( + "Importing 'parser.split_arg_string' is deprecated, it will only be" + " available in 'shell_completion' in Click 9.0.", + DeprecationWarning, + stacklevel=2, + ) + return split_arg_string + + raise AttributeError(name) diff --git a/lib/python3.12/site-packages/click/py.typed b/lib/python3.12/site-packages/click/py.typed new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/lib/python3.12/site-packages/click/shell_completion.py b/lib/python3.12/site-packages/click/shell_completion.py new file mode 100644 index 0000000000000000000000000000000000000000..6c39d5eb3889000b499d37262cc5f7de8b7a443e --- /dev/null +++ b/lib/python3.12/site-packages/click/shell_completion.py @@ -0,0 +1,644 @@ +from __future__ import annotations + +import collections.abc as cabc +import os +import re +import typing as t +from gettext import gettext as _ + +from .core import Argument +from .core import Command +from .core import Context +from .core import Group +from .core import Option +from .core import Parameter +from .core import ParameterSource +from .utils import echo + + +def shell_complete( + cli: Command, + ctx_args: cabc.MutableMapping[str, t.Any], + prog_name: str, + complete_var: str, + instruction: str, +) -> int: + """Perform shell completion for the given CLI program. + + :param cli: Command being called. + :param ctx_args: Extra arguments to pass to + ``cli.make_context``. + :param prog_name: Name of the executable in the shell. + :param complete_var: Name of the environment variable that holds + the completion instruction. + :param instruction: Value of ``complete_var`` with the completion + instruction and shell, in the form ``instruction_shell``. + :return: Status code to exit with. + """ + shell, _, instruction = instruction.partition("_") + comp_cls = get_completion_class(shell) + + if comp_cls is None: + return 1 + + comp = comp_cls(cli, ctx_args, prog_name, complete_var) + + if instruction == "source": + echo(comp.source()) + return 0 + + if instruction == "complete": + echo(comp.complete()) + return 0 + + return 1 + + +class CompletionItem: + """Represents a completion value and metadata about the value. The + default metadata is ``type`` to indicate special shell handling, + and ``help`` if a shell supports showing a help string next to the + value. + + Arbitrary parameters can be passed when creating the object, and + accessed using ``item.attr``. If an attribute wasn't passed, + accessing it returns ``None``. + + :param value: The completion suggestion. + :param type: Tells the shell script to provide special completion + support for the type. Click uses ``"dir"`` and ``"file"``. + :param help: String shown next to the value if supported. + :param kwargs: Arbitrary metadata. The built-in implementations + don't use this, but custom type completions paired with custom + shell support could use it. + """ + + __slots__ = ("value", "type", "help", "_info") + + def __init__( + self, + value: t.Any, + type: str = "plain", + help: str | None = None, + **kwargs: t.Any, + ) -> None: + self.value: t.Any = value + self.type: str = type + self.help: str | None = help + self._info = kwargs + + def __getattr__(self, name: str) -> t.Any: + return self._info.get(name) + + +# Only Bash >= 4.4 has the nosort option. +_SOURCE_BASH = """\ +%(complete_func)s() { + local IFS=$'\\n' + local response + + response=$(env COMP_WORDS="${COMP_WORDS[*]}" COMP_CWORD=$COMP_CWORD \ +%(complete_var)s=bash_complete $1) + + for completion in $response; do + IFS=',' read type value <<< "$completion" + + if [[ $type == 'dir' ]]; then + COMPREPLY=() + compopt -o dirnames + elif [[ $type == 'file' ]]; then + COMPREPLY=() + compopt -o default + elif [[ $type == 'plain' ]]; then + COMPREPLY+=($value) + fi + done + + return 0 +} + +%(complete_func)s_setup() { + complete -o nosort -F %(complete_func)s %(prog_name)s +} + +%(complete_func)s_setup; +""" + +_SOURCE_ZSH = """\ +#compdef %(prog_name)s + +%(complete_func)s() { + local -a completions + local -a completions_with_descriptions + local -a response + (( ! $+commands[%(prog_name)s] )) && return 1 + + response=("${(@f)$(env COMP_WORDS="${words[*]}" COMP_CWORD=$((CURRENT-1)) \ +%(complete_var)s=zsh_complete %(prog_name)s)}") + + for type key descr in ${response}; do + if [[ "$type" == "plain" ]]; then + if [[ "$descr" == "_" ]]; then + completions+=("$key") + else + completions_with_descriptions+=("$key":"$descr") + fi + elif [[ "$type" == "dir" ]]; then + _path_files -/ + elif [[ "$type" == "file" ]]; then + _path_files -f + fi + done + + if [ -n "$completions_with_descriptions" ]; then + _describe -V unsorted completions_with_descriptions -U + fi + + if [ -n "$completions" ]; then + compadd -U -V unsorted -a completions + fi +} + +if [[ $zsh_eval_context[-1] == loadautofunc ]]; then + # autoload from fpath, call function directly + %(complete_func)s "$@" +else + # eval/source/. command, register function for later + compdef %(complete_func)s %(prog_name)s +fi +""" + +_SOURCE_FISH = """\ +function %(complete_func)s; + set -l response (env %(complete_var)s=fish_complete COMP_WORDS=(commandline -cp) \ +COMP_CWORD=(commandline -t) %(prog_name)s); + + for completion in $response; + set -l metadata (string split "," $completion); + + if test $metadata[1] = "dir"; + __fish_complete_directories $metadata[2]; + else if test $metadata[1] = "file"; + __fish_complete_path $metadata[2]; + else if test $metadata[1] = "plain"; + echo $metadata[2]; + end; + end; +end; + +complete --no-files --command %(prog_name)s --arguments \ +"(%(complete_func)s)"; +""" + + +class ShellComplete: + """Base class for providing shell completion support. A subclass for + a given shell will override attributes and methods to implement the + completion instructions (``source`` and ``complete``). + + :param cli: Command being called. + :param prog_name: Name of the executable in the shell. + :param complete_var: Name of the environment variable that holds + the completion instruction. + + .. versionadded:: 8.0 + """ + + name: t.ClassVar[str] + """Name to register the shell as with :func:`add_completion_class`. + This is used in completion instructions (``{name}_source`` and + ``{name}_complete``). + """ + + source_template: t.ClassVar[str] + """Completion script template formatted by :meth:`source`. This must + be provided by subclasses. + """ + + def __init__( + self, + cli: Command, + ctx_args: cabc.MutableMapping[str, t.Any], + prog_name: str, + complete_var: str, + ) -> None: + self.cli = cli + self.ctx_args = ctx_args + self.prog_name = prog_name + self.complete_var = complete_var + + @property + def func_name(self) -> str: + """The name of the shell function defined by the completion + script. + """ + safe_name = re.sub(r"\W*", "", self.prog_name.replace("-", "_"), flags=re.ASCII) + return f"_{safe_name}_completion" + + def source_vars(self) -> dict[str, t.Any]: + """Vars for formatting :attr:`source_template`. + + By default this provides ``complete_func``, ``complete_var``, + and ``prog_name``. + """ + return { + "complete_func": self.func_name, + "complete_var": self.complete_var, + "prog_name": self.prog_name, + } + + def source(self) -> str: + """Produce the shell script that defines the completion + function. By default this ``%``-style formats + :attr:`source_template` with the dict returned by + :meth:`source_vars`. + """ + return self.source_template % self.source_vars() + + def get_completion_args(self) -> tuple[list[str], str]: + """Use the env vars defined by the shell script to return a + tuple of ``args, incomplete``. This must be implemented by + subclasses. + """ + raise NotImplementedError + + def get_completions(self, args: list[str], incomplete: str) -> list[CompletionItem]: + """Determine the context and last complete command or parameter + from the complete args. Call that object's ``shell_complete`` + method to get the completions for the incomplete value. + + :param args: List of complete args before the incomplete value. + :param incomplete: Value being completed. May be empty. + """ + ctx = _resolve_context(self.cli, self.ctx_args, self.prog_name, args) + obj, incomplete = _resolve_incomplete(ctx, args, incomplete) + return obj.shell_complete(ctx, incomplete) + + def format_completion(self, item: CompletionItem) -> str: + """Format a completion item into the form recognized by the + shell script. This must be implemented by subclasses. + + :param item: Completion item to format. + """ + raise NotImplementedError + + def complete(self) -> str: + """Produce the completion data to send back to the shell. + + By default this calls :meth:`get_completion_args`, gets the + completions, then calls :meth:`format_completion` for each + completion. + """ + args, incomplete = self.get_completion_args() + completions = self.get_completions(args, incomplete) + out = [self.format_completion(item) for item in completions] + return "\n".join(out) + + +class BashComplete(ShellComplete): + """Shell completion for Bash.""" + + name = "bash" + source_template = _SOURCE_BASH + + @staticmethod + def _check_version() -> None: + import shutil + import subprocess + + bash_exe = shutil.which("bash") + + if bash_exe is None: + match = None + else: + output = subprocess.run( + [bash_exe, "--norc", "-c", 'echo "${BASH_VERSION}"'], + stdout=subprocess.PIPE, + ) + match = re.search(r"^(\d+)\.(\d+)\.\d+", output.stdout.decode()) + + if match is not None: + major, minor = match.groups() + + if major < "4" or major == "4" and minor < "4": + echo( + _( + "Shell completion is not supported for Bash" + " versions older than 4.4." + ), + err=True, + ) + else: + echo( + _("Couldn't detect Bash version, shell completion is not supported."), + err=True, + ) + + def source(self) -> str: + self._check_version() + return super().source() + + def get_completion_args(self) -> tuple[list[str], str]: + cwords = split_arg_string(os.environ["COMP_WORDS"]) + cword = int(os.environ["COMP_CWORD"]) + args = cwords[1:cword] + + try: + incomplete = cwords[cword] + except IndexError: + incomplete = "" + + return args, incomplete + + def format_completion(self, item: CompletionItem) -> str: + return f"{item.type},{item.value}" + + +class ZshComplete(ShellComplete): + """Shell completion for Zsh.""" + + name = "zsh" + source_template = _SOURCE_ZSH + + def get_completion_args(self) -> tuple[list[str], str]: + cwords = split_arg_string(os.environ["COMP_WORDS"]) + cword = int(os.environ["COMP_CWORD"]) + args = cwords[1:cword] + + try: + incomplete = cwords[cword] + except IndexError: + incomplete = "" + + return args, incomplete + + def format_completion(self, item: CompletionItem) -> str: + return f"{item.type}\n{item.value}\n{item.help if item.help else '_'}" + + +class FishComplete(ShellComplete): + """Shell completion for Fish.""" + + name = "fish" + source_template = _SOURCE_FISH + + def get_completion_args(self) -> tuple[list[str], str]: + cwords = split_arg_string(os.environ["COMP_WORDS"]) + incomplete = os.environ["COMP_CWORD"] + args = cwords[1:] + + # Fish stores the partial word in both COMP_WORDS and + # COMP_CWORD, remove it from complete args. + if incomplete and args and args[-1] == incomplete: + args.pop() + + return args, incomplete + + def format_completion(self, item: CompletionItem) -> str: + if item.help: + return f"{item.type},{item.value}\t{item.help}" + + return f"{item.type},{item.value}" + + +ShellCompleteType = t.TypeVar("ShellCompleteType", bound="type[ShellComplete]") + + +_available_shells: dict[str, type[ShellComplete]] = { + "bash": BashComplete, + "fish": FishComplete, + "zsh": ZshComplete, +} + + +def add_completion_class( + cls: ShellCompleteType, name: str | None = None +) -> ShellCompleteType: + """Register a :class:`ShellComplete` subclass under the given name. + The name will be provided by the completion instruction environment + variable during completion. + + :param cls: The completion class that will handle completion for the + shell. + :param name: Name to register the class under. Defaults to the + class's ``name`` attribute. + """ + if name is None: + name = cls.name + + _available_shells[name] = cls + + return cls + + +def get_completion_class(shell: str) -> type[ShellComplete] | None: + """Look up a registered :class:`ShellComplete` subclass by the name + provided by the completion instruction environment variable. If the + name isn't registered, returns ``None``. + + :param shell: Name the class is registered under. + """ + return _available_shells.get(shell) + + +def split_arg_string(string: str) -> list[str]: + """Split an argument string as with :func:`shlex.split`, but don't + fail if the string is incomplete. Ignores a missing closing quote or + incomplete escape sequence and uses the partial token as-is. + + .. code-block:: python + + split_arg_string("example 'my file") + ["example", "my file"] + + split_arg_string("example my\\") + ["example", "my"] + + :param string: String to split. + + .. versionchanged:: 8.2 + Moved to ``shell_completion`` from ``parser``. + """ + import shlex + + lex = shlex.shlex(string, posix=True) + lex.whitespace_split = True + lex.commenters = "" + out = [] + + try: + for token in lex: + out.append(token) + except ValueError: + # Raised when end-of-string is reached in an invalid state. Use + # the partial token as-is. The quote or escape character is in + # lex.state, not lex.token. + out.append(lex.token) + + return out + + +def _is_incomplete_argument(ctx: Context, param: Parameter) -> bool: + """Determine if the given parameter is an argument that can still + accept values. + + :param ctx: Invocation context for the command represented by the + parsed complete args. + :param param: Argument object being checked. + """ + if not isinstance(param, Argument): + return False + + assert param.name is not None + # Will be None if expose_value is False. + value = ctx.params.get(param.name) + return ( + param.nargs == -1 + or ctx.get_parameter_source(param.name) is not ParameterSource.COMMANDLINE + or ( + param.nargs > 1 + and isinstance(value, (tuple, list)) + and len(value) < param.nargs + ) + ) + + +def _start_of_option(ctx: Context, value: str) -> bool: + """Check if the value looks like the start of an option.""" + if not value: + return False + + c = value[0] + return c in ctx._opt_prefixes + + +def _is_incomplete_option(ctx: Context, args: list[str], param: Parameter) -> bool: + """Determine if the given parameter is an option that needs a value. + + :param args: List of complete args before the incomplete value. + :param param: Option object being checked. + """ + if not isinstance(param, Option): + return False + + if param.is_flag or param.count: + return False + + last_option = None + + for index, arg in enumerate(reversed(args)): + if index + 1 > param.nargs: + break + + if _start_of_option(ctx, arg): + last_option = arg + + return last_option is not None and last_option in param.opts + + +def _resolve_context( + cli: Command, + ctx_args: cabc.MutableMapping[str, t.Any], + prog_name: str, + args: list[str], +) -> Context: + """Produce the context hierarchy starting with the command and + traversing the complete arguments. This only follows the commands, + it doesn't trigger input prompts or callbacks. + + :param cli: Command being called. + :param prog_name: Name of the executable in the shell. + :param args: List of complete args before the incomplete value. + """ + ctx_args["resilient_parsing"] = True + with cli.make_context(prog_name, args.copy(), **ctx_args) as ctx: + args = ctx._protected_args + ctx.args + + while args: + command = ctx.command + + if isinstance(command, Group): + if not command.chain: + name, cmd, args = command.resolve_command(ctx, args) + + if cmd is None: + return ctx + + with cmd.make_context( + name, args, parent=ctx, resilient_parsing=True + ) as sub_ctx: + ctx = sub_ctx + args = ctx._protected_args + ctx.args + else: + sub_ctx = ctx + + while args: + name, cmd, args = command.resolve_command(ctx, args) + + if cmd is None: + return ctx + + with cmd.make_context( + name, + args, + parent=ctx, + allow_extra_args=True, + allow_interspersed_args=False, + resilient_parsing=True, + ) as sub_sub_ctx: + sub_ctx = sub_sub_ctx + args = sub_ctx.args + + ctx = sub_ctx + args = [*sub_ctx._protected_args, *sub_ctx.args] + else: + break + + return ctx + + +def _resolve_incomplete( + ctx: Context, args: list[str], incomplete: str +) -> tuple[Command | Parameter, str]: + """Find the Click object that will handle the completion of the + incomplete value. Return the object and the incomplete value. + + :param ctx: Invocation context for the command represented by + the parsed complete args. + :param args: List of complete args before the incomplete value. + :param incomplete: Value being completed. May be empty. + """ + # Different shells treat an "=" between a long option name and + # value differently. Might keep the value joined, return the "=" + # as a separate item, or return the split name and value. Always + # split and discard the "=" to make completion easier. + if incomplete == "=": + incomplete = "" + elif "=" in incomplete and _start_of_option(ctx, incomplete): + name, _, incomplete = incomplete.partition("=") + args.append(name) + + # The "--" marker tells Click to stop treating values as options + # even if they start with the option character. If it hasn't been + # given and the incomplete arg looks like an option, the current + # command will provide option name completions. + if "--" not in args and _start_of_option(ctx, incomplete): + return ctx.command, incomplete + + params = ctx.command.get_params(ctx) + + # If the last complete arg is an option name with an incomplete + # value, the option will provide value completions. + for param in params: + if _is_incomplete_option(ctx, args, param): + return param, incomplete + + # It's not an option name or value. The first argument without a + # parsed value will provide value completions. + for param in params: + if _is_incomplete_argument(ctx, param): + return param, incomplete + + # There were no unparsed arguments, the command may be a group that + # will provide command name completions. + return ctx.command, incomplete diff --git a/lib/python3.12/site-packages/click/termui.py b/lib/python3.12/site-packages/click/termui.py new file mode 100644 index 0000000000000000000000000000000000000000..dcbb22216c4bac521eee02fbac97e3b67f302076 --- /dev/null +++ b/lib/python3.12/site-packages/click/termui.py @@ -0,0 +1,877 @@ +from __future__ import annotations + +import collections.abc as cabc +import inspect +import io +import itertools +import sys +import typing as t +from contextlib import AbstractContextManager +from gettext import gettext as _ + +from ._compat import isatty +from ._compat import strip_ansi +from .exceptions import Abort +from .exceptions import UsageError +from .globals import resolve_color_default +from .types import Choice +from .types import convert_type +from .types import ParamType +from .utils import echo +from .utils import LazyFile + +if t.TYPE_CHECKING: + from ._termui_impl import ProgressBar + +V = t.TypeVar("V") + +# The prompt functions to use. The doc tools currently override these +# functions to customize how they work. +visible_prompt_func: t.Callable[[str], str] = input + +_ansi_colors = { + "black": 30, + "red": 31, + "green": 32, + "yellow": 33, + "blue": 34, + "magenta": 35, + "cyan": 36, + "white": 37, + "reset": 39, + "bright_black": 90, + "bright_red": 91, + "bright_green": 92, + "bright_yellow": 93, + "bright_blue": 94, + "bright_magenta": 95, + "bright_cyan": 96, + "bright_white": 97, +} +_ansi_reset_all = "\033[0m" + + +def hidden_prompt_func(prompt: str) -> str: + import getpass + + return getpass.getpass(prompt) + + +def _build_prompt( + text: str, + suffix: str, + show_default: bool = False, + default: t.Any | None = None, + show_choices: bool = True, + type: ParamType | None = None, +) -> str: + prompt = text + if type is not None and show_choices and isinstance(type, Choice): + prompt += f" ({', '.join(map(str, type.choices))})" + if default is not None and show_default: + prompt = f"{prompt} [{_format_default(default)}]" + return f"{prompt}{suffix}" + + +def _format_default(default: t.Any) -> t.Any: + if isinstance(default, (io.IOBase, LazyFile)) and hasattr(default, "name"): + return default.name + + return default + + +def prompt( + text: str, + default: t.Any | None = None, + hide_input: bool = False, + confirmation_prompt: bool | str = False, + type: ParamType | t.Any | None = None, + value_proc: t.Callable[[str], t.Any] | None = None, + prompt_suffix: str = ": ", + show_default: bool = True, + err: bool = False, + show_choices: bool = True, +) -> t.Any: + """Prompts a user for input. This is a convenience function that can + be used to prompt a user for input later. + + If the user aborts the input by sending an interrupt signal, this + function will catch it and raise a :exc:`Abort` exception. + + :param text: the text to show for the prompt. + :param default: the default value to use if no input happens. If this + is not given it will prompt until it's aborted. + :param hide_input: if this is set to true then the input value will + be hidden. + :param confirmation_prompt: Prompt a second time to confirm the + value. Can be set to a string instead of ``True`` to customize + the message. + :param type: the type to use to check the value against. + :param value_proc: if this parameter is provided it's a function that + is invoked instead of the type conversion to + convert a value. + :param prompt_suffix: a suffix that should be added to the prompt. + :param show_default: shows or hides the default value in the prompt. + :param err: if set to true the file defaults to ``stderr`` instead of + ``stdout``, the same as with echo. + :param show_choices: Show or hide choices if the passed type is a Choice. + For example if type is a Choice of either day or week, + show_choices is true and text is "Group by" then the + prompt will be "Group by (day, week): ". + + .. versionadded:: 8.0 + ``confirmation_prompt`` can be a custom string. + + .. versionadded:: 7.0 + Added the ``show_choices`` parameter. + + .. versionadded:: 6.0 + Added unicode support for cmd.exe on Windows. + + .. versionadded:: 4.0 + Added the `err` parameter. + + """ + + def prompt_func(text: str) -> str: + f = hidden_prompt_func if hide_input else visible_prompt_func + try: + # Write the prompt separately so that we get nice + # coloring through colorama on Windows + echo(text.rstrip(" "), nl=False, err=err) + # Echo a space to stdout to work around an issue where + # readline causes backspace to clear the whole line. + return f(" ") + except (KeyboardInterrupt, EOFError): + # getpass doesn't print a newline if the user aborts input with ^C. + # Allegedly this behavior is inherited from getpass(3). + # A doc bug has been filed at https://bugs.python.org/issue24711 + if hide_input: + echo(None, err=err) + raise Abort() from None + + if value_proc is None: + value_proc = convert_type(type, default) + + prompt = _build_prompt( + text, prompt_suffix, show_default, default, show_choices, type + ) + + if confirmation_prompt: + if confirmation_prompt is True: + confirmation_prompt = _("Repeat for confirmation") + + confirmation_prompt = _build_prompt(confirmation_prompt, prompt_suffix) + + while True: + while True: + value = prompt_func(prompt) + if value: + break + elif default is not None: + value = default + break + try: + result = value_proc(value) + except UsageError as e: + if hide_input: + echo(_("Error: The value you entered was invalid."), err=err) + else: + echo(_("Error: {e.message}").format(e=e), err=err) + continue + if not confirmation_prompt: + return result + while True: + value2 = prompt_func(confirmation_prompt) + is_empty = not value and not value2 + if value2 or is_empty: + break + if value == value2: + return result + echo(_("Error: The two entered values do not match."), err=err) + + +def confirm( + text: str, + default: bool | None = False, + abort: bool = False, + prompt_suffix: str = ": ", + show_default: bool = True, + err: bool = False, +) -> bool: + """Prompts for confirmation (yes/no question). + + If the user aborts the input by sending a interrupt signal this + function will catch it and raise a :exc:`Abort` exception. + + :param text: the question to ask. + :param default: The default value to use when no input is given. If + ``None``, repeat until input is given. + :param abort: if this is set to `True` a negative answer aborts the + exception by raising :exc:`Abort`. + :param prompt_suffix: a suffix that should be added to the prompt. + :param show_default: shows or hides the default value in the prompt. + :param err: if set to true the file defaults to ``stderr`` instead of + ``stdout``, the same as with echo. + + .. versionchanged:: 8.0 + Repeat until input is given if ``default`` is ``None``. + + .. versionadded:: 4.0 + Added the ``err`` parameter. + """ + prompt = _build_prompt( + text, + prompt_suffix, + show_default, + "y/n" if default is None else ("Y/n" if default else "y/N"), + ) + + while True: + try: + # Write the prompt separately so that we get nice + # coloring through colorama on Windows + echo(prompt.rstrip(" "), nl=False, err=err) + # Echo a space to stdout to work around an issue where + # readline causes backspace to clear the whole line. + value = visible_prompt_func(" ").lower().strip() + except (KeyboardInterrupt, EOFError): + raise Abort() from None + if value in ("y", "yes"): + rv = True + elif value in ("n", "no"): + rv = False + elif default is not None and value == "": + rv = default + else: + echo(_("Error: invalid input"), err=err) + continue + break + if abort and not rv: + raise Abort() + return rv + + +def echo_via_pager( + text_or_generator: cabc.Iterable[str] | t.Callable[[], cabc.Iterable[str]] | str, + color: bool | None = None, +) -> None: + """This function takes a text and shows it via an environment specific + pager on stdout. + + .. versionchanged:: 3.0 + Added the `color` flag. + + :param text_or_generator: the text to page, or alternatively, a + generator emitting the text to page. + :param color: controls if the pager supports ANSI colors or not. The + default is autodetection. + """ + color = resolve_color_default(color) + + if inspect.isgeneratorfunction(text_or_generator): + i = t.cast("t.Callable[[], cabc.Iterable[str]]", text_or_generator)() + elif isinstance(text_or_generator, str): + i = [text_or_generator] + else: + i = iter(t.cast("cabc.Iterable[str]", text_or_generator)) + + # convert every element of i to a text type if necessary + text_generator = (el if isinstance(el, str) else str(el) for el in i) + + from ._termui_impl import pager + + return pager(itertools.chain(text_generator, "\n"), color) + + +@t.overload +def progressbar( + *, + length: int, + label: str | None = None, + hidden: bool = False, + show_eta: bool = True, + show_percent: bool | None = None, + show_pos: bool = False, + fill_char: str = "#", + empty_char: str = "-", + bar_template: str = "%(label)s [%(bar)s] %(info)s", + info_sep: str = " ", + width: int = 36, + file: t.TextIO | None = None, + color: bool | None = None, + update_min_steps: int = 1, +) -> ProgressBar[int]: ... + + +@t.overload +def progressbar( + iterable: cabc.Iterable[V] | None = None, + length: int | None = None, + label: str | None = None, + hidden: bool = False, + show_eta: bool = True, + show_percent: bool | None = None, + show_pos: bool = False, + item_show_func: t.Callable[[V | None], str | None] | None = None, + fill_char: str = "#", + empty_char: str = "-", + bar_template: str = "%(label)s [%(bar)s] %(info)s", + info_sep: str = " ", + width: int = 36, + file: t.TextIO | None = None, + color: bool | None = None, + update_min_steps: int = 1, +) -> ProgressBar[V]: ... + + +def progressbar( + iterable: cabc.Iterable[V] | None = None, + length: int | None = None, + label: str | None = None, + hidden: bool = False, + show_eta: bool = True, + show_percent: bool | None = None, + show_pos: bool = False, + item_show_func: t.Callable[[V | None], str | None] | None = None, + fill_char: str = "#", + empty_char: str = "-", + bar_template: str = "%(label)s [%(bar)s] %(info)s", + info_sep: str = " ", + width: int = 36, + file: t.TextIO | None = None, + color: bool | None = None, + update_min_steps: int = 1, +) -> ProgressBar[V]: + """This function creates an iterable context manager that can be used + to iterate over something while showing a progress bar. It will + either iterate over the `iterable` or `length` items (that are counted + up). While iteration happens, this function will print a rendered + progress bar to the given `file` (defaults to stdout) and will attempt + to calculate remaining time and more. By default, this progress bar + will not be rendered if the file is not a terminal. + + The context manager creates the progress bar. When the context + manager is entered the progress bar is already created. With every + iteration over the progress bar, the iterable passed to the bar is + advanced and the bar is updated. When the context manager exits, + a newline is printed and the progress bar is finalized on screen. + + Note: The progress bar is currently designed for use cases where the + total progress can be expected to take at least several seconds. + Because of this, the ProgressBar class object won't display + progress that is considered too fast, and progress where the time + between steps is less than a second. + + No printing must happen or the progress bar will be unintentionally + destroyed. + + Example usage:: + + with progressbar(items) as bar: + for item in bar: + do_something_with(item) + + Alternatively, if no iterable is specified, one can manually update the + progress bar through the `update()` method instead of directly + iterating over the progress bar. The update method accepts the number + of steps to increment the bar with:: + + with progressbar(length=chunks.total_bytes) as bar: + for chunk in chunks: + process_chunk(chunk) + bar.update(chunks.bytes) + + The ``update()`` method also takes an optional value specifying the + ``current_item`` at the new position. This is useful when used + together with ``item_show_func`` to customize the output for each + manual step:: + + with click.progressbar( + length=total_size, + label='Unzipping archive', + item_show_func=lambda a: a.filename + ) as bar: + for archive in zip_file: + archive.extract() + bar.update(archive.size, archive) + + :param iterable: an iterable to iterate over. If not provided the length + is required. + :param length: the number of items to iterate over. By default the + progressbar will attempt to ask the iterator about its + length, which might or might not work. If an iterable is + also provided this parameter can be used to override the + length. If an iterable is not provided the progress bar + will iterate over a range of that length. + :param label: the label to show next to the progress bar. + :param hidden: hide the progressbar. Defaults to ``False``. When no tty is + detected, it will only print the progressbar label. Setting this to + ``False`` also disables that. + :param show_eta: enables or disables the estimated time display. This is + automatically disabled if the length cannot be + determined. + :param show_percent: enables or disables the percentage display. The + default is `True` if the iterable has a length or + `False` if not. + :param show_pos: enables or disables the absolute position display. The + default is `False`. + :param item_show_func: A function called with the current item which + can return a string to show next to the progress bar. If the + function returns ``None`` nothing is shown. The current item can + be ``None``, such as when entering and exiting the bar. + :param fill_char: the character to use to show the filled part of the + progress bar. + :param empty_char: the character to use to show the non-filled part of + the progress bar. + :param bar_template: the format string to use as template for the bar. + The parameters in it are ``label`` for the label, + ``bar`` for the progress bar and ``info`` for the + info section. + :param info_sep: the separator between multiple info items (eta etc.) + :param width: the width of the progress bar in characters, 0 means full + terminal width + :param file: The file to write to. If this is not a terminal then + only the label is printed. + :param color: controls if the terminal supports ANSI colors or not. The + default is autodetection. This is only needed if ANSI + codes are included anywhere in the progress bar output + which is not the case by default. + :param update_min_steps: Render only when this many updates have + completed. This allows tuning for very fast iterators. + + .. versionadded:: 8.2 + The ``hidden`` argument. + + .. versionchanged:: 8.0 + Output is shown even if execution time is less than 0.5 seconds. + + .. versionchanged:: 8.0 + ``item_show_func`` shows the current item, not the previous one. + + .. versionchanged:: 8.0 + Labels are echoed if the output is not a TTY. Reverts a change + in 7.0 that removed all output. + + .. versionadded:: 8.0 + The ``update_min_steps`` parameter. + + .. versionadded:: 4.0 + The ``color`` parameter and ``update`` method. + + .. versionadded:: 2.0 + """ + from ._termui_impl import ProgressBar + + color = resolve_color_default(color) + return ProgressBar( + iterable=iterable, + length=length, + hidden=hidden, + show_eta=show_eta, + show_percent=show_percent, + show_pos=show_pos, + item_show_func=item_show_func, + fill_char=fill_char, + empty_char=empty_char, + bar_template=bar_template, + info_sep=info_sep, + file=file, + label=label, + width=width, + color=color, + update_min_steps=update_min_steps, + ) + + +def clear() -> None: + """Clears the terminal screen. This will have the effect of clearing + the whole visible space of the terminal and moving the cursor to the + top left. This does not do anything if not connected to a terminal. + + .. versionadded:: 2.0 + """ + if not isatty(sys.stdout): + return + + # ANSI escape \033[2J clears the screen, \033[1;1H moves the cursor + echo("\033[2J\033[1;1H", nl=False) + + +def _interpret_color(color: int | tuple[int, int, int] | str, offset: int = 0) -> str: + if isinstance(color, int): + return f"{38 + offset};5;{color:d}" + + if isinstance(color, (tuple, list)): + r, g, b = color + return f"{38 + offset};2;{r:d};{g:d};{b:d}" + + return str(_ansi_colors[color] + offset) + + +def style( + text: t.Any, + fg: int | tuple[int, int, int] | str | None = None, + bg: int | tuple[int, int, int] | str | None = None, + bold: bool | None = None, + dim: bool | None = None, + underline: bool | None = None, + overline: bool | None = None, + italic: bool | None = None, + blink: bool | None = None, + reverse: bool | None = None, + strikethrough: bool | None = None, + reset: bool = True, +) -> str: + """Styles a text with ANSI styles and returns the new string. By + default the styling is self contained which means that at the end + of the string a reset code is issued. This can be prevented by + passing ``reset=False``. + + Examples:: + + click.echo(click.style('Hello World!', fg='green')) + click.echo(click.style('ATTENTION!', blink=True)) + click.echo(click.style('Some things', reverse=True, fg='cyan')) + click.echo(click.style('More colors', fg=(255, 12, 128), bg=117)) + + Supported color names: + + * ``black`` (might be a gray) + * ``red`` + * ``green`` + * ``yellow`` (might be an orange) + * ``blue`` + * ``magenta`` + * ``cyan`` + * ``white`` (might be light gray) + * ``bright_black`` + * ``bright_red`` + * ``bright_green`` + * ``bright_yellow`` + * ``bright_blue`` + * ``bright_magenta`` + * ``bright_cyan`` + * ``bright_white`` + * ``reset`` (reset the color code only) + + If the terminal supports it, color may also be specified as: + + - An integer in the interval [0, 255]. The terminal must support + 8-bit/256-color mode. + - An RGB tuple of three integers in [0, 255]. The terminal must + support 24-bit/true-color mode. + + See https://en.wikipedia.org/wiki/ANSI_color and + https://gist.github.com/XVilka/8346728 for more information. + + :param text: the string to style with ansi codes. + :param fg: if provided this will become the foreground color. + :param bg: if provided this will become the background color. + :param bold: if provided this will enable or disable bold mode. + :param dim: if provided this will enable or disable dim mode. This is + badly supported. + :param underline: if provided this will enable or disable underline. + :param overline: if provided this will enable or disable overline. + :param italic: if provided this will enable or disable italic. + :param blink: if provided this will enable or disable blinking. + :param reverse: if provided this will enable or disable inverse + rendering (foreground becomes background and the + other way round). + :param strikethrough: if provided this will enable or disable + striking through text. + :param reset: by default a reset-all code is added at the end of the + string which means that styles do not carry over. This + can be disabled to compose styles. + + .. versionchanged:: 8.0 + A non-string ``message`` is converted to a string. + + .. versionchanged:: 8.0 + Added support for 256 and RGB color codes. + + .. versionchanged:: 8.0 + Added the ``strikethrough``, ``italic``, and ``overline`` + parameters. + + .. versionchanged:: 7.0 + Added support for bright colors. + + .. versionadded:: 2.0 + """ + if not isinstance(text, str): + text = str(text) + + bits = [] + + if fg: + try: + bits.append(f"\033[{_interpret_color(fg)}m") + except KeyError: + raise TypeError(f"Unknown color {fg!r}") from None + + if bg: + try: + bits.append(f"\033[{_interpret_color(bg, 10)}m") + except KeyError: + raise TypeError(f"Unknown color {bg!r}") from None + + if bold is not None: + bits.append(f"\033[{1 if bold else 22}m") + if dim is not None: + bits.append(f"\033[{2 if dim else 22}m") + if underline is not None: + bits.append(f"\033[{4 if underline else 24}m") + if overline is not None: + bits.append(f"\033[{53 if overline else 55}m") + if italic is not None: + bits.append(f"\033[{3 if italic else 23}m") + if blink is not None: + bits.append(f"\033[{5 if blink else 25}m") + if reverse is not None: + bits.append(f"\033[{7 if reverse else 27}m") + if strikethrough is not None: + bits.append(f"\033[{9 if strikethrough else 29}m") + bits.append(text) + if reset: + bits.append(_ansi_reset_all) + return "".join(bits) + + +def unstyle(text: str) -> str: + """Removes ANSI styling information from a string. Usually it's not + necessary to use this function as Click's echo function will + automatically remove styling if necessary. + + .. versionadded:: 2.0 + + :param text: the text to remove style information from. + """ + return strip_ansi(text) + + +def secho( + message: t.Any | None = None, + file: t.IO[t.AnyStr] | None = None, + nl: bool = True, + err: bool = False, + color: bool | None = None, + **styles: t.Any, +) -> None: + """This function combines :func:`echo` and :func:`style` into one + call. As such the following two calls are the same:: + + click.secho('Hello World!', fg='green') + click.echo(click.style('Hello World!', fg='green')) + + All keyword arguments are forwarded to the underlying functions + depending on which one they go with. + + Non-string types will be converted to :class:`str`. However, + :class:`bytes` are passed directly to :meth:`echo` without applying + style. If you want to style bytes that represent text, call + :meth:`bytes.decode` first. + + .. versionchanged:: 8.0 + A non-string ``message`` is converted to a string. Bytes are + passed through without style applied. + + .. versionadded:: 2.0 + """ + if message is not None and not isinstance(message, (bytes, bytearray)): + message = style(message, **styles) + + return echo(message, file=file, nl=nl, err=err, color=color) + + +@t.overload +def edit( + text: bytes | bytearray, + editor: str | None = None, + env: cabc.Mapping[str, str] | None = None, + require_save: bool = False, + extension: str = ".txt", +) -> bytes | None: ... + + +@t.overload +def edit( + text: str, + editor: str | None = None, + env: cabc.Mapping[str, str] | None = None, + require_save: bool = True, + extension: str = ".txt", +) -> str | None: ... + + +@t.overload +def edit( + text: None = None, + editor: str | None = None, + env: cabc.Mapping[str, str] | None = None, + require_save: bool = True, + extension: str = ".txt", + filename: str | cabc.Iterable[str] | None = None, +) -> None: ... + + +def edit( + text: str | bytes | bytearray | None = None, + editor: str | None = None, + env: cabc.Mapping[str, str] | None = None, + require_save: bool = True, + extension: str = ".txt", + filename: str | cabc.Iterable[str] | None = None, +) -> str | bytes | bytearray | None: + r"""Edits the given text in the defined editor. If an editor is given + (should be the full path to the executable but the regular operating + system search path is used for finding the executable) it overrides + the detected editor. Optionally, some environment variables can be + used. If the editor is closed without changes, `None` is returned. In + case a file is edited directly the return value is always `None` and + `require_save` and `extension` are ignored. + + If the editor cannot be opened a :exc:`UsageError` is raised. + + Note for Windows: to simplify cross-platform usage, the newlines are + automatically converted from POSIX to Windows and vice versa. As such, + the message here will have ``\n`` as newline markers. + + :param text: the text to edit. + :param editor: optionally the editor to use. Defaults to automatic + detection. + :param env: environment variables to forward to the editor. + :param require_save: if this is true, then not saving in the editor + will make the return value become `None`. + :param extension: the extension to tell the editor about. This defaults + to `.txt` but changing this might change syntax + highlighting. + :param filename: if provided it will edit this file instead of the + provided text contents. It will not use a temporary + file as an indirection in that case. If the editor supports + editing multiple files at once, a sequence of files may be + passed as well. Invoke `click.file` once per file instead + if multiple files cannot be managed at once or editing the + files serially is desired. + + .. versionchanged:: 8.2.0 + ``filename`` now accepts any ``Iterable[str]`` in addition to a ``str`` + if the ``editor`` supports editing multiple files at once. + + """ + from ._termui_impl import Editor + + ed = Editor(editor=editor, env=env, require_save=require_save, extension=extension) + + if filename is None: + return ed.edit(text) + + if isinstance(filename, str): + filename = (filename,) + + ed.edit_files(filenames=filename) + return None + + +def launch(url: str, wait: bool = False, locate: bool = False) -> int: + """This function launches the given URL (or filename) in the default + viewer application for this file type. If this is an executable, it + might launch the executable in a new session. The return value is + the exit code of the launched application. Usually, ``0`` indicates + success. + + Examples:: + + click.launch('https://click.palletsprojects.com/') + click.launch('/my/downloaded/file', locate=True) + + .. versionadded:: 2.0 + + :param url: URL or filename of the thing to launch. + :param wait: Wait for the program to exit before returning. This + only works if the launched program blocks. In particular, + ``xdg-open`` on Linux does not block. + :param locate: if this is set to `True` then instead of launching the + application associated with the URL it will attempt to + launch a file manager with the file located. This + might have weird effects if the URL does not point to + the filesystem. + """ + from ._termui_impl import open_url + + return open_url(url, wait=wait, locate=locate) + + +# If this is provided, getchar() calls into this instead. This is used +# for unittesting purposes. +_getchar: t.Callable[[bool], str] | None = None + + +def getchar(echo: bool = False) -> str: + """Fetches a single character from the terminal and returns it. This + will always return a unicode character and under certain rare + circumstances this might return more than one character. The + situations which more than one character is returned is when for + whatever reason multiple characters end up in the terminal buffer or + standard input was not actually a terminal. + + Note that this will always read from the terminal, even if something + is piped into the standard input. + + Note for Windows: in rare cases when typing non-ASCII characters, this + function might wait for a second character and then return both at once. + This is because certain Unicode characters look like special-key markers. + + .. versionadded:: 2.0 + + :param echo: if set to `True`, the character read will also show up on + the terminal. The default is to not show it. + """ + global _getchar + + if _getchar is None: + from ._termui_impl import getchar as f + + _getchar = f + + return _getchar(echo) + + +def raw_terminal() -> AbstractContextManager[int]: + from ._termui_impl import raw_terminal as f + + return f() + + +def pause(info: str | None = None, err: bool = False) -> None: + """This command stops execution and waits for the user to press any + key to continue. This is similar to the Windows batch "pause" + command. If the program is not run through a terminal, this command + will instead do nothing. + + .. versionadded:: 2.0 + + .. versionadded:: 4.0 + Added the `err` parameter. + + :param info: The message to print before pausing. Defaults to + ``"Press any key to continue..."``. + :param err: if set to message goes to ``stderr`` instead of + ``stdout``, the same as with echo. + """ + if not isatty(sys.stdin) or not isatty(sys.stdout): + return + + if info is None: + info = _("Press any key to continue...") + + try: + if info: + echo(info, nl=False, err=err) + try: + getchar() + except (KeyboardInterrupt, EOFError): + pass + finally: + if info: + echo(err=err) diff --git a/lib/python3.12/site-packages/click/testing.py b/lib/python3.12/site-packages/click/testing.py new file mode 100644 index 0000000000000000000000000000000000000000..7c0e8741e2d07f54294a82792c776d08c639add4 --- /dev/null +++ b/lib/python3.12/site-packages/click/testing.py @@ -0,0 +1,565 @@ +from __future__ import annotations + +import collections.abc as cabc +import contextlib +import io +import os +import shlex +import shutil +import sys +import tempfile +import typing as t +from types import TracebackType + +from . import _compat +from . import formatting +from . import termui +from . import utils +from ._compat import _find_binary_reader + +if t.TYPE_CHECKING: + from _typeshed import ReadableBuffer + + from .core import Command + + +class EchoingStdin: + def __init__(self, input: t.BinaryIO, output: t.BinaryIO) -> None: + self._input = input + self._output = output + self._paused = False + + def __getattr__(self, x: str) -> t.Any: + return getattr(self._input, x) + + def _echo(self, rv: bytes) -> bytes: + if not self._paused: + self._output.write(rv) + + return rv + + def read(self, n: int = -1) -> bytes: + return self._echo(self._input.read(n)) + + def read1(self, n: int = -1) -> bytes: + return self._echo(self._input.read1(n)) # type: ignore + + def readline(self, n: int = -1) -> bytes: + return self._echo(self._input.readline(n)) + + def readlines(self) -> list[bytes]: + return [self._echo(x) for x in self._input.readlines()] + + def __iter__(self) -> cabc.Iterator[bytes]: + return iter(self._echo(x) for x in self._input) + + def __repr__(self) -> str: + return repr(self._input) + + +@contextlib.contextmanager +def _pause_echo(stream: EchoingStdin | None) -> cabc.Iterator[None]: + if stream is None: + yield + else: + stream._paused = True + yield + stream._paused = False + + +class BytesIOCopy(io.BytesIO): + """Patch ``io.BytesIO`` to let the written stream be copied to another. + + .. versionadded:: 8.2 + """ + + def __init__(self, copy_to: io.BytesIO) -> None: + super().__init__() + self.copy_to = copy_to + + def flush(self) -> None: + super().flush() + self.copy_to.flush() + + def write(self, b: ReadableBuffer) -> int: + self.copy_to.write(b) + return super().write(b) + + +class StreamMixer: + """Mixes `` and `` streams. + + The result is available in the ``output`` attribute. + + .. versionadded:: 8.2 + """ + + def __init__(self) -> None: + self.output: io.BytesIO = io.BytesIO() + self.stdout: io.BytesIO = BytesIOCopy(copy_to=self.output) + self.stderr: io.BytesIO = BytesIOCopy(copy_to=self.output) + + +class _NamedTextIOWrapper(io.TextIOWrapper): + def __init__( + self, buffer: t.BinaryIO, name: str, mode: str, **kwargs: t.Any + ) -> None: + super().__init__(buffer, **kwargs) + self._name = name + self._mode = mode + + @property + def name(self) -> str: + return self._name + + @property + def mode(self) -> str: + return self._mode + + def __next__(self) -> str: # type: ignore + try: + line = super().__next__() + except StopIteration as e: + raise EOFError() from e + return line + + +def make_input_stream( + input: str | bytes | t.IO[t.Any] | None, charset: str +) -> t.BinaryIO: + # Is already an input stream. + if hasattr(input, "read"): + rv = _find_binary_reader(t.cast("t.IO[t.Any]", input)) + + if rv is not None: + return rv + + raise TypeError("Could not find binary reader for input stream.") + + if input is None: + input = b"" + elif isinstance(input, str): + input = input.encode(charset) + + return io.BytesIO(input) + + +class Result: + """Holds the captured result of an invoked CLI script. + + :param runner: The runner that created the result + :param stdout_bytes: The standard output as bytes. + :param stderr_bytes: The standard error as bytes. + :param output_bytes: A mix of ``stdout_bytes`` and ``stderr_bytes``, as the + user would see it in its terminal. + :param return_value: The value returned from the invoked command. + :param exit_code: The exit code as integer. + :param exception: The exception that happened if one did. + :param exc_info: Exception information (exception type, exception instance, + traceback type). + + .. versionchanged:: 8.2 + ``stderr_bytes`` no longer optional, ``output_bytes`` introduced and + ``mix_stderr`` has been removed. + + .. versionadded:: 8.0 + Added ``return_value``. + """ + + def __init__( + self, + runner: CliRunner, + stdout_bytes: bytes, + stderr_bytes: bytes, + output_bytes: bytes, + return_value: t.Any, + exit_code: int, + exception: BaseException | None, + exc_info: tuple[type[BaseException], BaseException, TracebackType] + | None = None, + ): + self.runner = runner + self.stdout_bytes = stdout_bytes + self.stderr_bytes = stderr_bytes + self.output_bytes = output_bytes + self.return_value = return_value + self.exit_code = exit_code + self.exception = exception + self.exc_info = exc_info + + @property + def output(self) -> str: + """The terminal output as unicode string, as the user would see it. + + .. versionchanged:: 8.2 + No longer a proxy for ``self.stdout``. Now has its own independent stream + that is mixing `` and ``, in the order they were written. + """ + return self.output_bytes.decode(self.runner.charset, "replace").replace( + "\r\n", "\n" + ) + + @property + def stdout(self) -> str: + """The standard output as unicode string.""" + return self.stdout_bytes.decode(self.runner.charset, "replace").replace( + "\r\n", "\n" + ) + + @property + def stderr(self) -> str: + """The standard error as unicode string. + + .. versionchanged:: 8.2 + No longer raise an exception, always returns the `` string. + """ + return self.stderr_bytes.decode(self.runner.charset, "replace").replace( + "\r\n", "\n" + ) + + def __repr__(self) -> str: + exc_str = repr(self.exception) if self.exception else "okay" + return f"<{type(self).__name__} {exc_str}>" + + +class CliRunner: + """The CLI runner provides functionality to invoke a Click command line + script for unittesting purposes in a isolated environment. This only + works in single-threaded systems without any concurrency as it changes the + global interpreter state. + + :param charset: the character set for the input and output data. + :param env: a dictionary with environment variables for overriding. + :param echo_stdin: if this is set to `True`, then reading from `` writes + to ``. This is useful for showing examples in + some circumstances. Note that regular prompts + will automatically echo the input. + :param catch_exceptions: Whether to catch any exceptions other than + ``SystemExit`` when running :meth:`~CliRunner.invoke`. + + .. versionchanged:: 8.2 + Added the ``catch_exceptions`` parameter. + + .. versionchanged:: 8.2 + ``mix_stderr`` parameter has been removed. + """ + + def __init__( + self, + charset: str = "utf-8", + env: cabc.Mapping[str, str | None] | None = None, + echo_stdin: bool = False, + catch_exceptions: bool = True, + ) -> None: + self.charset = charset + self.env: cabc.Mapping[str, str | None] = env or {} + self.echo_stdin = echo_stdin + self.catch_exceptions = catch_exceptions + + def get_default_prog_name(self, cli: Command) -> str: + """Given a command object it will return the default program name + for it. The default is the `name` attribute or ``"root"`` if not + set. + """ + return cli.name or "root" + + def make_env( + self, overrides: cabc.Mapping[str, str | None] | None = None + ) -> cabc.Mapping[str, str | None]: + """Returns the environment overrides for invoking a script.""" + rv = dict(self.env) + if overrides: + rv.update(overrides) + return rv + + @contextlib.contextmanager + def isolation( + self, + input: str | bytes | t.IO[t.Any] | None = None, + env: cabc.Mapping[str, str | None] | None = None, + color: bool = False, + ) -> cabc.Iterator[tuple[io.BytesIO, io.BytesIO, io.BytesIO]]: + """A context manager that sets up the isolation for invoking of a + command line tool. This sets up `` with the given input data + and `os.environ` with the overrides from the given dictionary. + This also rebinds some internals in Click to be mocked (like the + prompt functionality). + + This is automatically done in the :meth:`invoke` method. + + :param input: the input stream to put into `sys.stdin`. + :param env: the environment overrides as dictionary. + :param color: whether the output should contain color codes. The + application can still override this explicitly. + + .. versionadded:: 8.2 + An additional output stream is returned, which is a mix of + `` and `` streams. + + .. versionchanged:: 8.2 + Always returns the `` stream. + + .. versionchanged:: 8.0 + `` is opened with ``errors="backslashreplace"`` + instead of the default ``"strict"``. + + .. versionchanged:: 4.0 + Added the ``color`` parameter. + """ + bytes_input = make_input_stream(input, self.charset) + echo_input = None + + old_stdin = sys.stdin + old_stdout = sys.stdout + old_stderr = sys.stderr + old_forced_width = formatting.FORCED_WIDTH + formatting.FORCED_WIDTH = 80 + + env = self.make_env(env) + + stream_mixer = StreamMixer() + + if self.echo_stdin: + bytes_input = echo_input = t.cast( + t.BinaryIO, EchoingStdin(bytes_input, stream_mixer.stdout) + ) + + sys.stdin = text_input = _NamedTextIOWrapper( + bytes_input, encoding=self.charset, name="", mode="r" + ) + + if self.echo_stdin: + # Force unbuffered reads, otherwise TextIOWrapper reads a + # large chunk which is echoed early. + text_input._CHUNK_SIZE = 1 # type: ignore + + sys.stdout = _NamedTextIOWrapper( + stream_mixer.stdout, encoding=self.charset, name="", mode="w" + ) + + sys.stderr = _NamedTextIOWrapper( + stream_mixer.stderr, + encoding=self.charset, + name="", + mode="w", + errors="backslashreplace", + ) + + @_pause_echo(echo_input) # type: ignore + def visible_input(prompt: str | None = None) -> str: + sys.stdout.write(prompt or "") + val = next(text_input).rstrip("\r\n") + sys.stdout.write(f"{val}\n") + sys.stdout.flush() + return val + + @_pause_echo(echo_input) # type: ignore + def hidden_input(prompt: str | None = None) -> str: + sys.stdout.write(f"{prompt or ''}\n") + sys.stdout.flush() + return next(text_input).rstrip("\r\n") + + @_pause_echo(echo_input) # type: ignore + def _getchar(echo: bool) -> str: + char = sys.stdin.read(1) + + if echo: + sys.stdout.write(char) + + sys.stdout.flush() + return char + + default_color = color + + def should_strip_ansi( + stream: t.IO[t.Any] | None = None, color: bool | None = None + ) -> bool: + if color is None: + return not default_color + return not color + + old_visible_prompt_func = termui.visible_prompt_func + old_hidden_prompt_func = termui.hidden_prompt_func + old__getchar_func = termui._getchar + old_should_strip_ansi = utils.should_strip_ansi # type: ignore + old__compat_should_strip_ansi = _compat.should_strip_ansi + termui.visible_prompt_func = visible_input + termui.hidden_prompt_func = hidden_input + termui._getchar = _getchar + utils.should_strip_ansi = should_strip_ansi # type: ignore + _compat.should_strip_ansi = should_strip_ansi + + old_env = {} + try: + for key, value in env.items(): + old_env[key] = os.environ.get(key) + if value is None: + try: + del os.environ[key] + except Exception: + pass + else: + os.environ[key] = value + yield (stream_mixer.stdout, stream_mixer.stderr, stream_mixer.output) + finally: + for key, value in old_env.items(): + if value is None: + try: + del os.environ[key] + except Exception: + pass + else: + os.environ[key] = value + sys.stdout = old_stdout + sys.stderr = old_stderr + sys.stdin = old_stdin + termui.visible_prompt_func = old_visible_prompt_func + termui.hidden_prompt_func = old_hidden_prompt_func + termui._getchar = old__getchar_func + utils.should_strip_ansi = old_should_strip_ansi # type: ignore + _compat.should_strip_ansi = old__compat_should_strip_ansi + formatting.FORCED_WIDTH = old_forced_width + + def invoke( + self, + cli: Command, + args: str | cabc.Sequence[str] | None = None, + input: str | bytes | t.IO[t.Any] | None = None, + env: cabc.Mapping[str, str | None] | None = None, + catch_exceptions: bool | None = None, + color: bool = False, + **extra: t.Any, + ) -> Result: + """Invokes a command in an isolated environment. The arguments are + forwarded directly to the command line script, the `extra` keyword + arguments are passed to the :meth:`~clickpkg.Command.main` function of + the command. + + This returns a :class:`Result` object. + + :param cli: the command to invoke + :param args: the arguments to invoke. It may be given as an iterable + or a string. When given as string it will be interpreted + as a Unix shell command. More details at + :func:`shlex.split`. + :param input: the input data for `sys.stdin`. + :param env: the environment overrides. + :param catch_exceptions: Whether to catch any other exceptions than + ``SystemExit``. If :data:`None`, the value + from :class:`CliRunner` is used. + :param extra: the keyword arguments to pass to :meth:`main`. + :param color: whether the output should contain color codes. The + application can still override this explicitly. + + .. versionadded:: 8.2 + The result object has the ``output_bytes`` attribute with + the mix of ``stdout_bytes`` and ``stderr_bytes``, as the user would + see it in its terminal. + + .. versionchanged:: 8.2 + The result object always returns the ``stderr_bytes`` stream. + + .. versionchanged:: 8.0 + The result object has the ``return_value`` attribute with + the value returned from the invoked command. + + .. versionchanged:: 4.0 + Added the ``color`` parameter. + + .. versionchanged:: 3.0 + Added the ``catch_exceptions`` parameter. + + .. versionchanged:: 3.0 + The result object has the ``exc_info`` attribute with the + traceback if available. + """ + exc_info = None + if catch_exceptions is None: + catch_exceptions = self.catch_exceptions + + with self.isolation(input=input, env=env, color=color) as outstreams: + return_value = None + exception: BaseException | None = None + exit_code = 0 + + if isinstance(args, str): + args = shlex.split(args) + + try: + prog_name = extra.pop("prog_name") + except KeyError: + prog_name = self.get_default_prog_name(cli) + + try: + return_value = cli.main(args=args or (), prog_name=prog_name, **extra) + except SystemExit as e: + exc_info = sys.exc_info() + e_code = t.cast("int | t.Any | None", e.code) + + if e_code is None: + e_code = 0 + + if e_code != 0: + exception = e + + if not isinstance(e_code, int): + sys.stdout.write(str(e_code)) + sys.stdout.write("\n") + e_code = 1 + + exit_code = e_code + + except Exception as e: + if not catch_exceptions: + raise + exception = e + exit_code = 1 + exc_info = sys.exc_info() + finally: + sys.stdout.flush() + sys.stderr.flush() + stdout = outstreams[0].getvalue() + stderr = outstreams[1].getvalue() + output = outstreams[2].getvalue() + + return Result( + runner=self, + stdout_bytes=stdout, + stderr_bytes=stderr, + output_bytes=output, + return_value=return_value, + exit_code=exit_code, + exception=exception, + exc_info=exc_info, # type: ignore + ) + + @contextlib.contextmanager + def isolated_filesystem( + self, temp_dir: str | os.PathLike[str] | None = None + ) -> cabc.Iterator[str]: + """A context manager that creates a temporary directory and + changes the current working directory to it. This isolates tests + that affect the contents of the CWD to prevent them from + interfering with each other. + + :param temp_dir: Create the temporary directory under this + directory. If given, the created directory is not removed + when exiting. + + .. versionchanged:: 8.0 + Added the ``temp_dir`` parameter. + """ + cwd = os.getcwd() + dt = tempfile.mkdtemp(dir=temp_dir) + os.chdir(dt) + + try: + yield dt + finally: + os.chdir(cwd) + + if temp_dir is None: + try: + shutil.rmtree(dt) + except OSError: + pass diff --git a/lib/python3.12/site-packages/click/types.py b/lib/python3.12/site-packages/click/types.py new file mode 100644 index 0000000000000000000000000000000000000000..684cb3b1e6107bada194d18968de69a7f383a2a0 --- /dev/null +++ b/lib/python3.12/site-packages/click/types.py @@ -0,0 +1,1165 @@ +from __future__ import annotations + +import collections.abc as cabc +import enum +import os +import stat +import sys +import typing as t +from datetime import datetime +from gettext import gettext as _ +from gettext import ngettext + +from ._compat import _get_argv_encoding +from ._compat import open_stream +from .exceptions import BadParameter +from .utils import format_filename +from .utils import LazyFile +from .utils import safecall + +if t.TYPE_CHECKING: + import typing_extensions as te + + from .core import Context + from .core import Parameter + from .shell_completion import CompletionItem + +ParamTypeValue = t.TypeVar("ParamTypeValue") + + +class ParamType: + """Represents the type of a parameter. Validates and converts values + from the command line or Python into the correct type. + + To implement a custom type, subclass and implement at least the + following: + + - The :attr:`name` class attribute must be set. + - Calling an instance of the type with ``None`` must return + ``None``. This is already implemented by default. + - :meth:`convert` must convert string values to the correct type. + - :meth:`convert` must accept values that are already the correct + type. + - It must be able to convert a value if the ``ctx`` and ``param`` + arguments are ``None``. This can occur when converting prompt + input. + """ + + is_composite: t.ClassVar[bool] = False + arity: t.ClassVar[int] = 1 + + #: the descriptive name of this type + name: str + + #: if a list of this type is expected and the value is pulled from a + #: string environment variable, this is what splits it up. `None` + #: means any whitespace. For all parameters the general rule is that + #: whitespace splits them up. The exception are paths and files which + #: are split by ``os.path.pathsep`` by default (":" on Unix and ";" on + #: Windows). + envvar_list_splitter: t.ClassVar[str | None] = None + + def to_info_dict(self) -> dict[str, t.Any]: + """Gather information that could be useful for a tool generating + user-facing documentation. + + Use :meth:`click.Context.to_info_dict` to traverse the entire + CLI structure. + + .. versionadded:: 8.0 + """ + # The class name without the "ParamType" suffix. + param_type = type(self).__name__.partition("ParamType")[0] + param_type = param_type.partition("ParameterType")[0] + + # Custom subclasses might not remember to set a name. + if hasattr(self, "name"): + name = self.name + else: + name = param_type + + return {"param_type": param_type, "name": name} + + def __call__( + self, + value: t.Any, + param: Parameter | None = None, + ctx: Context | None = None, + ) -> t.Any: + if value is not None: + return self.convert(value, param, ctx) + + def get_metavar(self, param: Parameter, ctx: Context) -> str | None: + """Returns the metavar default for this param if it provides one.""" + + def get_missing_message(self, param: Parameter, ctx: Context | None) -> str | None: + """Optionally might return extra information about a missing + parameter. + + .. versionadded:: 2.0 + """ + + def convert( + self, value: t.Any, param: Parameter | None, ctx: Context | None + ) -> t.Any: + """Convert the value to the correct type. This is not called if + the value is ``None`` (the missing value). + + This must accept string values from the command line, as well as + values that are already the correct type. It may also convert + other compatible types. + + The ``param`` and ``ctx`` arguments may be ``None`` in certain + situations, such as when converting prompt input. + + If the value cannot be converted, call :meth:`fail` with a + descriptive message. + + :param value: The value to convert. + :param param: The parameter that is using this type to convert + its value. May be ``None``. + :param ctx: The current context that arrived at this value. May + be ``None``. + """ + return value + + def split_envvar_value(self, rv: str) -> cabc.Sequence[str]: + """Given a value from an environment variable this splits it up + into small chunks depending on the defined envvar list splitter. + + If the splitter is set to `None`, which means that whitespace splits, + then leading and trailing whitespace is ignored. Otherwise, leading + and trailing splitters usually lead to empty items being included. + """ + return (rv or "").split(self.envvar_list_splitter) + + def fail( + self, + message: str, + param: Parameter | None = None, + ctx: Context | None = None, + ) -> t.NoReturn: + """Helper method to fail with an invalid value message.""" + raise BadParameter(message, ctx=ctx, param=param) + + def shell_complete( + self, ctx: Context, param: Parameter, incomplete: str + ) -> list[CompletionItem]: + """Return a list of + :class:`~click.shell_completion.CompletionItem` objects for the + incomplete value. Most types do not provide completions, but + some do, and this allows custom types to provide custom + completions as well. + + :param ctx: Invocation context for this command. + :param param: The parameter that is requesting completion. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + return [] + + +class CompositeParamType(ParamType): + is_composite = True + + @property + def arity(self) -> int: # type: ignore + raise NotImplementedError() + + +class FuncParamType(ParamType): + def __init__(self, func: t.Callable[[t.Any], t.Any]) -> None: + self.name: str = func.__name__ + self.func = func + + def to_info_dict(self) -> dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict["func"] = self.func + return info_dict + + def convert( + self, value: t.Any, param: Parameter | None, ctx: Context | None + ) -> t.Any: + try: + return self.func(value) + except ValueError: + try: + value = str(value) + except UnicodeError: + value = value.decode("utf-8", "replace") + + self.fail(value, param, ctx) + + +class UnprocessedParamType(ParamType): + name = "text" + + def convert( + self, value: t.Any, param: Parameter | None, ctx: Context | None + ) -> t.Any: + return value + + def __repr__(self) -> str: + return "UNPROCESSED" + + +class StringParamType(ParamType): + name = "text" + + def convert( + self, value: t.Any, param: Parameter | None, ctx: Context | None + ) -> t.Any: + if isinstance(value, bytes): + enc = _get_argv_encoding() + try: + value = value.decode(enc) + except UnicodeError: + fs_enc = sys.getfilesystemencoding() + if fs_enc != enc: + try: + value = value.decode(fs_enc) + except UnicodeError: + value = value.decode("utf-8", "replace") + else: + value = value.decode("utf-8", "replace") + return value + return str(value) + + def __repr__(self) -> str: + return "STRING" + + +class Choice(ParamType, t.Generic[ParamTypeValue]): + """The choice type allows a value to be checked against a fixed set + of supported values. + + You may pass any iterable value which will be converted to a tuple + and thus will only be iterated once. + + The resulting value will always be one of the originally passed choices. + See :meth:`normalize_choice` for more info on the mapping of strings + to choices. See :ref:`choice-opts` for an example. + + :param case_sensitive: Set to false to make choices case + insensitive. Defaults to true. + + .. versionchanged:: 8.2.0 + Non-``str`` ``choices`` are now supported. It can additionally be any + iterable. Before you were not recommended to pass anything but a list or + tuple. + + .. versionadded:: 8.2.0 + Choice normalization can be overridden via :meth:`normalize_choice`. + """ + + name = "choice" + + def __init__( + self, choices: cabc.Iterable[ParamTypeValue], case_sensitive: bool = True + ) -> None: + self.choices: cabc.Sequence[ParamTypeValue] = tuple(choices) + self.case_sensitive = case_sensitive + + def to_info_dict(self) -> dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict["choices"] = self.choices + info_dict["case_sensitive"] = self.case_sensitive + return info_dict + + def _normalized_mapping( + self, ctx: Context | None = None + ) -> cabc.Mapping[ParamTypeValue, str]: + """ + Returns mapping where keys are the original choices and the values are + the normalized values that are accepted via the command line. + + This is a simple wrapper around :meth:`normalize_choice`, use that + instead which is supported. + """ + return { + choice: self.normalize_choice( + choice=choice, + ctx=ctx, + ) + for choice in self.choices + } + + def normalize_choice(self, choice: ParamTypeValue, ctx: Context | None) -> str: + """ + Normalize a choice value, used to map a passed string to a choice. + Each choice must have a unique normalized value. + + By default uses :meth:`Context.token_normalize_func` and if not case + sensitive, convert it to a casefolded value. + + .. versionadded:: 8.2.0 + """ + normed_value = choice.name if isinstance(choice, enum.Enum) else str(choice) + + if ctx is not None and ctx.token_normalize_func is not None: + normed_value = ctx.token_normalize_func(normed_value) + + if not self.case_sensitive: + normed_value = normed_value.casefold() + + return normed_value + + def get_metavar(self, param: Parameter, ctx: Context) -> str | None: + if param.param_type_name == "option" and not param.show_choices: # type: ignore + choice_metavars = [ + convert_type(type(choice)).name.upper() for choice in self.choices + ] + choices_str = "|".join([*dict.fromkeys(choice_metavars)]) + else: + choices_str = "|".join( + [str(i) for i in self._normalized_mapping(ctx=ctx).values()] + ) + + # Use curly braces to indicate a required argument. + if param.required and param.param_type_name == "argument": + return f"{{{choices_str}}}" + + # Use square braces to indicate an option or optional argument. + return f"[{choices_str}]" + + def get_missing_message(self, param: Parameter, ctx: Context | None) -> str: + """ + Message shown when no choice is passed. + + .. versionchanged:: 8.2.0 Added ``ctx`` argument. + """ + return _("Choose from:\n\t{choices}").format( + choices=",\n\t".join(self._normalized_mapping(ctx=ctx).values()) + ) + + def convert( + self, value: t.Any, param: Parameter | None, ctx: Context | None + ) -> ParamTypeValue: + """ + For a given value from the parser, normalize it and find its + matching normalized value in the list of choices. Then return the + matched "original" choice. + """ + normed_value = self.normalize_choice(choice=value, ctx=ctx) + normalized_mapping = self._normalized_mapping(ctx=ctx) + + try: + return next( + original + for original, normalized in normalized_mapping.items() + if normalized == normed_value + ) + except StopIteration: + self.fail( + self.get_invalid_choice_message(value=value, ctx=ctx), + param=param, + ctx=ctx, + ) + + def get_invalid_choice_message(self, value: t.Any, ctx: Context | None) -> str: + """Get the error message when the given choice is invalid. + + :param value: The invalid value. + + .. versionadded:: 8.2 + """ + choices_str = ", ".join(map(repr, self._normalized_mapping(ctx=ctx).values())) + return ngettext( + "{value!r} is not {choice}.", + "{value!r} is not one of {choices}.", + len(self.choices), + ).format(value=value, choice=choices_str, choices=choices_str) + + def __repr__(self) -> str: + return f"Choice({list(self.choices)})" + + def shell_complete( + self, ctx: Context, param: Parameter, incomplete: str + ) -> list[CompletionItem]: + """Complete choices that start with the incomplete value. + + :param ctx: Invocation context for this command. + :param param: The parameter that is requesting completion. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + from click.shell_completion import CompletionItem + + str_choices = map(str, self.choices) + + if self.case_sensitive: + matched = (c for c in str_choices if c.startswith(incomplete)) + else: + incomplete = incomplete.lower() + matched = (c for c in str_choices if c.lower().startswith(incomplete)) + + return [CompletionItem(c) for c in matched] + + +class DateTime(ParamType): + """The DateTime type converts date strings into `datetime` objects. + + The format strings which are checked are configurable, but default to some + common (non-timezone aware) ISO 8601 formats. + + When specifying *DateTime* formats, you should only pass a list or a tuple. + Other iterables, like generators, may lead to surprising results. + + The format strings are processed using ``datetime.strptime``, and this + consequently defines the format strings which are allowed. + + Parsing is tried using each format, in order, and the first format which + parses successfully is used. + + :param formats: A list or tuple of date format strings, in the order in + which they should be tried. Defaults to + ``'%Y-%m-%d'``, ``'%Y-%m-%dT%H:%M:%S'``, + ``'%Y-%m-%d %H:%M:%S'``. + """ + + name = "datetime" + + def __init__(self, formats: cabc.Sequence[str] | None = None): + self.formats: cabc.Sequence[str] = formats or [ + "%Y-%m-%d", + "%Y-%m-%dT%H:%M:%S", + "%Y-%m-%d %H:%M:%S", + ] + + def to_info_dict(self) -> dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict["formats"] = self.formats + return info_dict + + def get_metavar(self, param: Parameter, ctx: Context) -> str | None: + return f"[{'|'.join(self.formats)}]" + + def _try_to_convert_date(self, value: t.Any, format: str) -> datetime | None: + try: + return datetime.strptime(value, format) + except ValueError: + return None + + def convert( + self, value: t.Any, param: Parameter | None, ctx: Context | None + ) -> t.Any: + if isinstance(value, datetime): + return value + + for format in self.formats: + converted = self._try_to_convert_date(value, format) + + if converted is not None: + return converted + + formats_str = ", ".join(map(repr, self.formats)) + self.fail( + ngettext( + "{value!r} does not match the format {format}.", + "{value!r} does not match the formats {formats}.", + len(self.formats), + ).format(value=value, format=formats_str, formats=formats_str), + param, + ctx, + ) + + def __repr__(self) -> str: + return "DateTime" + + +class _NumberParamTypeBase(ParamType): + _number_class: t.ClassVar[type[t.Any]] + + def convert( + self, value: t.Any, param: Parameter | None, ctx: Context | None + ) -> t.Any: + try: + return self._number_class(value) + except ValueError: + self.fail( + _("{value!r} is not a valid {number_type}.").format( + value=value, number_type=self.name + ), + param, + ctx, + ) + + +class _NumberRangeBase(_NumberParamTypeBase): + def __init__( + self, + min: float | None = None, + max: float | None = None, + min_open: bool = False, + max_open: bool = False, + clamp: bool = False, + ) -> None: + self.min = min + self.max = max + self.min_open = min_open + self.max_open = max_open + self.clamp = clamp + + def to_info_dict(self) -> dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict.update( + min=self.min, + max=self.max, + min_open=self.min_open, + max_open=self.max_open, + clamp=self.clamp, + ) + return info_dict + + def convert( + self, value: t.Any, param: Parameter | None, ctx: Context | None + ) -> t.Any: + import operator + + rv = super().convert(value, param, ctx) + lt_min: bool = self.min is not None and ( + operator.le if self.min_open else operator.lt + )(rv, self.min) + gt_max: bool = self.max is not None and ( + operator.ge if self.max_open else operator.gt + )(rv, self.max) + + if self.clamp: + if lt_min: + return self._clamp(self.min, 1, self.min_open) # type: ignore + + if gt_max: + return self._clamp(self.max, -1, self.max_open) # type: ignore + + if lt_min or gt_max: + self.fail( + _("{value} is not in the range {range}.").format( + value=rv, range=self._describe_range() + ), + param, + ctx, + ) + + return rv + + def _clamp(self, bound: float, dir: t.Literal[1, -1], open: bool) -> float: + """Find the valid value to clamp to bound in the given + direction. + + :param bound: The boundary value. + :param dir: 1 or -1 indicating the direction to move. + :param open: If true, the range does not include the bound. + """ + raise NotImplementedError + + def _describe_range(self) -> str: + """Describe the range for use in help text.""" + if self.min is None: + op = "<" if self.max_open else "<=" + return f"x{op}{self.max}" + + if self.max is None: + op = ">" if self.min_open else ">=" + return f"x{op}{self.min}" + + lop = "<" if self.min_open else "<=" + rop = "<" if self.max_open else "<=" + return f"{self.min}{lop}x{rop}{self.max}" + + def __repr__(self) -> str: + clamp = " clamped" if self.clamp else "" + return f"<{type(self).__name__} {self._describe_range()}{clamp}>" + + +class IntParamType(_NumberParamTypeBase): + name = "integer" + _number_class = int + + def __repr__(self) -> str: + return "INT" + + +class IntRange(_NumberRangeBase, IntParamType): + """Restrict an :data:`click.INT` value to a range of accepted + values. See :ref:`ranges`. + + If ``min`` or ``max`` are not passed, any value is accepted in that + direction. If ``min_open`` or ``max_open`` are enabled, the + corresponding boundary is not included in the range. + + If ``clamp`` is enabled, a value outside the range is clamped to the + boundary instead of failing. + + .. versionchanged:: 8.0 + Added the ``min_open`` and ``max_open`` parameters. + """ + + name = "integer range" + + def _clamp( # type: ignore + self, bound: int, dir: t.Literal[1, -1], open: bool + ) -> int: + if not open: + return bound + + return bound + dir + + +class FloatParamType(_NumberParamTypeBase): + name = "float" + _number_class = float + + def __repr__(self) -> str: + return "FLOAT" + + +class FloatRange(_NumberRangeBase, FloatParamType): + """Restrict a :data:`click.FLOAT` value to a range of accepted + values. See :ref:`ranges`. + + If ``min`` or ``max`` are not passed, any value is accepted in that + direction. If ``min_open`` or ``max_open`` are enabled, the + corresponding boundary is not included in the range. + + If ``clamp`` is enabled, a value outside the range is clamped to the + boundary instead of failing. This is not supported if either + boundary is marked ``open``. + + .. versionchanged:: 8.0 + Added the ``min_open`` and ``max_open`` parameters. + """ + + name = "float range" + + def __init__( + self, + min: float | None = None, + max: float | None = None, + min_open: bool = False, + max_open: bool = False, + clamp: bool = False, + ) -> None: + super().__init__( + min=min, max=max, min_open=min_open, max_open=max_open, clamp=clamp + ) + + if (min_open or max_open) and clamp: + raise TypeError("Clamping is not supported for open bounds.") + + def _clamp(self, bound: float, dir: t.Literal[1, -1], open: bool) -> float: + if not open: + return bound + + # Could use math.nextafter here, but clamping an + # open float range doesn't seem to be particularly useful. It's + # left up to the user to write a callback to do it if needed. + raise RuntimeError("Clamping is not supported for open bounds.") + + +class BoolParamType(ParamType): + name = "boolean" + + def convert( + self, value: t.Any, param: Parameter | None, ctx: Context | None + ) -> t.Any: + if value in {False, True}: + return bool(value) + + norm = value.strip().lower() + + if norm in {"1", "true", "t", "yes", "y", "on"}: + return True + + if norm in {"0", "false", "f", "no", "n", "off"}: + return False + + self.fail( + _("{value!r} is not a valid boolean.").format(value=value), param, ctx + ) + + def __repr__(self) -> str: + return "BOOL" + + +class UUIDParameterType(ParamType): + name = "uuid" + + def convert( + self, value: t.Any, param: Parameter | None, ctx: Context | None + ) -> t.Any: + import uuid + + if isinstance(value, uuid.UUID): + return value + + value = value.strip() + + try: + return uuid.UUID(value) + except ValueError: + self.fail( + _("{value!r} is not a valid UUID.").format(value=value), param, ctx + ) + + def __repr__(self) -> str: + return "UUID" + + +class File(ParamType): + """Declares a parameter to be a file for reading or writing. The file + is automatically closed once the context tears down (after the command + finished working). + + Files can be opened for reading or writing. The special value ``-`` + indicates stdin or stdout depending on the mode. + + By default, the file is opened for reading text data, but it can also be + opened in binary mode or for writing. The encoding parameter can be used + to force a specific encoding. + + The `lazy` flag controls if the file should be opened immediately or upon + first IO. The default is to be non-lazy for standard input and output + streams as well as files opened for reading, `lazy` otherwise. When opening a + file lazily for reading, it is still opened temporarily for validation, but + will not be held open until first IO. lazy is mainly useful when opening + for writing to avoid creating the file until it is needed. + + Files can also be opened atomically in which case all writes go into a + separate file in the same folder and upon completion the file will + be moved over to the original location. This is useful if a file + regularly read by other users is modified. + + See :ref:`file-args` for more information. + + .. versionchanged:: 2.0 + Added the ``atomic`` parameter. + """ + + name = "filename" + envvar_list_splitter: t.ClassVar[str] = os.path.pathsep + + def __init__( + self, + mode: str = "r", + encoding: str | None = None, + errors: str | None = "strict", + lazy: bool | None = None, + atomic: bool = False, + ) -> None: + self.mode = mode + self.encoding = encoding + self.errors = errors + self.lazy = lazy + self.atomic = atomic + + def to_info_dict(self) -> dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict.update(mode=self.mode, encoding=self.encoding) + return info_dict + + def resolve_lazy_flag(self, value: str | os.PathLike[str]) -> bool: + if self.lazy is not None: + return self.lazy + if os.fspath(value) == "-": + return False + elif "w" in self.mode: + return True + return False + + def convert( + self, + value: str | os.PathLike[str] | t.IO[t.Any], + param: Parameter | None, + ctx: Context | None, + ) -> t.IO[t.Any]: + if _is_file_like(value): + return value + + value = t.cast("str | os.PathLike[str]", value) + + try: + lazy = self.resolve_lazy_flag(value) + + if lazy: + lf = LazyFile( + value, self.mode, self.encoding, self.errors, atomic=self.atomic + ) + + if ctx is not None: + ctx.call_on_close(lf.close_intelligently) + + return t.cast("t.IO[t.Any]", lf) + + f, should_close = open_stream( + value, self.mode, self.encoding, self.errors, atomic=self.atomic + ) + + # If a context is provided, we automatically close the file + # at the end of the context execution (or flush out). If a + # context does not exist, it's the caller's responsibility to + # properly close the file. This for instance happens when the + # type is used with prompts. + if ctx is not None: + if should_close: + ctx.call_on_close(safecall(f.close)) + else: + ctx.call_on_close(safecall(f.flush)) + + return f + except OSError as e: + self.fail(f"'{format_filename(value)}': {e.strerror}", param, ctx) + + def shell_complete( + self, ctx: Context, param: Parameter, incomplete: str + ) -> list[CompletionItem]: + """Return a special completion marker that tells the completion + system to use the shell to provide file path completions. + + :param ctx: Invocation context for this command. + :param param: The parameter that is requesting completion. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + from click.shell_completion import CompletionItem + + return [CompletionItem(incomplete, type="file")] + + +def _is_file_like(value: t.Any) -> te.TypeGuard[t.IO[t.Any]]: + return hasattr(value, "read") or hasattr(value, "write") + + +class Path(ParamType): + """The ``Path`` type is similar to the :class:`File` type, but + returns the filename instead of an open file. Various checks can be + enabled to validate the type of file and permissions. + + :param exists: The file or directory needs to exist for the value to + be valid. If this is not set to ``True``, and the file does not + exist, then all further checks are silently skipped. + :param file_okay: Allow a file as a value. + :param dir_okay: Allow a directory as a value. + :param readable: if true, a readable check is performed. + :param writable: if true, a writable check is performed. + :param executable: if true, an executable check is performed. + :param resolve_path: Make the value absolute and resolve any + symlinks. A ``~`` is not expanded, as this is supposed to be + done by the shell only. + :param allow_dash: Allow a single dash as a value, which indicates + a standard stream (but does not open it). Use + :func:`~click.open_file` to handle opening this value. + :param path_type: Convert the incoming path value to this type. If + ``None``, keep Python's default, which is ``str``. Useful to + convert to :class:`pathlib.Path`. + + .. versionchanged:: 8.1 + Added the ``executable`` parameter. + + .. versionchanged:: 8.0 + Allow passing ``path_type=pathlib.Path``. + + .. versionchanged:: 6.0 + Added the ``allow_dash`` parameter. + """ + + envvar_list_splitter: t.ClassVar[str] = os.path.pathsep + + def __init__( + self, + exists: bool = False, + file_okay: bool = True, + dir_okay: bool = True, + writable: bool = False, + readable: bool = True, + resolve_path: bool = False, + allow_dash: bool = False, + path_type: type[t.Any] | None = None, + executable: bool = False, + ): + self.exists = exists + self.file_okay = file_okay + self.dir_okay = dir_okay + self.readable = readable + self.writable = writable + self.executable = executable + self.resolve_path = resolve_path + self.allow_dash = allow_dash + self.type = path_type + + if self.file_okay and not self.dir_okay: + self.name: str = _("file") + elif self.dir_okay and not self.file_okay: + self.name = _("directory") + else: + self.name = _("path") + + def to_info_dict(self) -> dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict.update( + exists=self.exists, + file_okay=self.file_okay, + dir_okay=self.dir_okay, + writable=self.writable, + readable=self.readable, + allow_dash=self.allow_dash, + ) + return info_dict + + def coerce_path_result( + self, value: str | os.PathLike[str] + ) -> str | bytes | os.PathLike[str]: + if self.type is not None and not isinstance(value, self.type): + if self.type is str: + return os.fsdecode(value) + elif self.type is bytes: + return os.fsencode(value) + else: + return t.cast("os.PathLike[str]", self.type(value)) + + return value + + def convert( + self, + value: str | os.PathLike[str], + param: Parameter | None, + ctx: Context | None, + ) -> str | bytes | os.PathLike[str]: + rv = value + + is_dash = self.file_okay and self.allow_dash and rv in (b"-", "-") + + if not is_dash: + if self.resolve_path: + rv = os.path.realpath(rv) + + try: + st = os.stat(rv) + except OSError: + if not self.exists: + return self.coerce_path_result(rv) + self.fail( + _("{name} {filename!r} does not exist.").format( + name=self.name.title(), filename=format_filename(value) + ), + param, + ctx, + ) + + if not self.file_okay and stat.S_ISREG(st.st_mode): + self.fail( + _("{name} {filename!r} is a file.").format( + name=self.name.title(), filename=format_filename(value) + ), + param, + ctx, + ) + if not self.dir_okay and stat.S_ISDIR(st.st_mode): + self.fail( + _("{name} {filename!r} is a directory.").format( + name=self.name.title(), filename=format_filename(value) + ), + param, + ctx, + ) + + if self.readable and not os.access(rv, os.R_OK): + self.fail( + _("{name} {filename!r} is not readable.").format( + name=self.name.title(), filename=format_filename(value) + ), + param, + ctx, + ) + + if self.writable and not os.access(rv, os.W_OK): + self.fail( + _("{name} {filename!r} is not writable.").format( + name=self.name.title(), filename=format_filename(value) + ), + param, + ctx, + ) + + if self.executable and not os.access(value, os.X_OK): + self.fail( + _("{name} {filename!r} is not executable.").format( + name=self.name.title(), filename=format_filename(value) + ), + param, + ctx, + ) + + return self.coerce_path_result(rv) + + def shell_complete( + self, ctx: Context, param: Parameter, incomplete: str + ) -> list[CompletionItem]: + """Return a special completion marker that tells the completion + system to use the shell to provide path completions for only + directories or any paths. + + :param ctx: Invocation context for this command. + :param param: The parameter that is requesting completion. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + from click.shell_completion import CompletionItem + + type = "dir" if self.dir_okay and not self.file_okay else "file" + return [CompletionItem(incomplete, type=type)] + + +class Tuple(CompositeParamType): + """The default behavior of Click is to apply a type on a value directly. + This works well in most cases, except for when `nargs` is set to a fixed + count and different types should be used for different items. In this + case the :class:`Tuple` type can be used. This type can only be used + if `nargs` is set to a fixed number. + + For more information see :ref:`tuple-type`. + + This can be selected by using a Python tuple literal as a type. + + :param types: a list of types that should be used for the tuple items. + """ + + def __init__(self, types: cabc.Sequence[type[t.Any] | ParamType]) -> None: + self.types: cabc.Sequence[ParamType] = [convert_type(ty) for ty in types] + + def to_info_dict(self) -> dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict["types"] = [t.to_info_dict() for t in self.types] + return info_dict + + @property + def name(self) -> str: # type: ignore + return f"<{' '.join(ty.name for ty in self.types)}>" + + @property + def arity(self) -> int: # type: ignore + return len(self.types) + + def convert( + self, value: t.Any, param: Parameter | None, ctx: Context | None + ) -> t.Any: + len_type = len(self.types) + len_value = len(value) + + if len_value != len_type: + self.fail( + ngettext( + "{len_type} values are required, but {len_value} was given.", + "{len_type} values are required, but {len_value} were given.", + len_value, + ).format(len_type=len_type, len_value=len_value), + param=param, + ctx=ctx, + ) + + return tuple( + ty(x, param, ctx) for ty, x in zip(self.types, value, strict=False) + ) + + +def convert_type(ty: t.Any | None, default: t.Any | None = None) -> ParamType: + """Find the most appropriate :class:`ParamType` for the given Python + type. If the type isn't provided, it can be inferred from a default + value. + """ + guessed_type = False + + if ty is None and default is not None: + if isinstance(default, (tuple, list)): + # If the default is empty, ty will remain None and will + # return STRING. + if default: + item = default[0] + + # A tuple of tuples needs to detect the inner types. + # Can't call convert recursively because that would + # incorrectly unwind the tuple to a single type. + if isinstance(item, (tuple, list)): + ty = tuple(map(type, item)) + else: + ty = type(item) + else: + ty = type(default) + + guessed_type = True + + if isinstance(ty, tuple): + return Tuple(ty) + + if isinstance(ty, ParamType): + return ty + + if ty is str or ty is None: + return STRING + + if ty is int: + return INT + + if ty is float: + return FLOAT + + if ty is bool: + return BOOL + + if guessed_type: + return STRING + + if __debug__: + try: + if issubclass(ty, ParamType): + raise AssertionError( + f"Attempted to use an uninstantiated parameter type ({ty})." + ) + except TypeError: + # ty is an instance (correct), so issubclass fails. + pass + + return FuncParamType(ty) + + +#: A dummy parameter type that just does nothing. From a user's +#: perspective this appears to just be the same as `STRING` but +#: internally no string conversion takes place if the input was bytes. +#: This is usually useful when working with file paths as they can +#: appear in bytes and unicode. +#: +#: For path related uses the :class:`Path` type is a better choice but +#: there are situations where an unprocessed type is useful which is why +#: it is is provided. +#: +#: .. versionadded:: 4.0 +UNPROCESSED = UnprocessedParamType() + +#: A unicode string parameter type which is the implicit default. This +#: can also be selected by using ``str`` as type. +STRING = StringParamType() + +#: An integer parameter. This can also be selected by using ``int`` as +#: type. +INT = IntParamType() + +#: A floating point value parameter. This can also be selected by using +#: ``float`` as type. +FLOAT = FloatParamType() + +#: A boolean parameter. This is the default for boolean flags. This can +#: also be selected by using ``bool`` as a type. +BOOL = BoolParamType() + +#: A UUID parameter. +UUID = UUIDParameterType() + + +class OptionHelpExtra(t.TypedDict, total=False): + envvars: tuple[str, ...] + default: str + range: str + required: str diff --git a/lib/python3.12/site-packages/click/utils.py b/lib/python3.12/site-packages/click/utils.py new file mode 100644 index 0000000000000000000000000000000000000000..ab2fe5889cee75d559092442696f9b23bf20ebfb --- /dev/null +++ b/lib/python3.12/site-packages/click/utils.py @@ -0,0 +1,627 @@ +from __future__ import annotations + +import collections.abc as cabc +import os +import re +import sys +import typing as t +from functools import update_wrapper +from types import ModuleType +from types import TracebackType + +from ._compat import _default_text_stderr +from ._compat import _default_text_stdout +from ._compat import _find_binary_writer +from ._compat import auto_wrap_for_ansi +from ._compat import binary_streams +from ._compat import open_stream +from ._compat import should_strip_ansi +from ._compat import strip_ansi +from ._compat import text_streams +from ._compat import WIN +from .globals import resolve_color_default + +if t.TYPE_CHECKING: + import typing_extensions as te + + P = te.ParamSpec("P") + +R = t.TypeVar("R") + + +def _posixify(name: str) -> str: + return "-".join(name.split()).lower() + + +def safecall(func: t.Callable[P, R]) -> t.Callable[P, R | None]: + """Wraps a function so that it swallows exceptions.""" + + def wrapper(*args: P.args, **kwargs: P.kwargs) -> R | None: + try: + return func(*args, **kwargs) + except Exception: + pass + return None + + return update_wrapper(wrapper, func) + + +def make_str(value: t.Any) -> str: + """Converts a value into a valid string.""" + if isinstance(value, bytes): + try: + return value.decode(sys.getfilesystemencoding()) + except UnicodeError: + return value.decode("utf-8", "replace") + return str(value) + + +def make_default_short_help(help: str, max_length: int = 45) -> str: + """Returns a condensed version of help string.""" + # Consider only the first paragraph. + paragraph_end = help.find("\n\n") + + if paragraph_end != -1: + help = help[:paragraph_end] + + # Collapse newlines, tabs, and spaces. + words = help.split() + + if not words: + return "" + + # The first paragraph started with a "no rewrap" marker, ignore it. + if words[0] == "\b": + words = words[1:] + + total_length = 0 + last_index = len(words) - 1 + + for i, word in enumerate(words): + total_length += len(word) + (i > 0) + + if total_length > max_length: # too long, truncate + break + + if word[-1] == ".": # sentence end, truncate without "..." + return " ".join(words[: i + 1]) + + if total_length == max_length and i != last_index: + break # not at sentence end, truncate with "..." + else: + return " ".join(words) # no truncation needed + + # Account for the length of the suffix. + total_length += len("...") + + # remove words until the length is short enough + while i > 0: + total_length -= len(words[i]) + (i > 0) + + if total_length <= max_length: + break + + i -= 1 + + return " ".join(words[:i]) + "..." + + +class LazyFile: + """A lazy file works like a regular file but it does not fully open + the file but it does perform some basic checks early to see if the + filename parameter does make sense. This is useful for safely opening + files for writing. + """ + + def __init__( + self, + filename: str | os.PathLike[str], + mode: str = "r", + encoding: str | None = None, + errors: str | None = "strict", + atomic: bool = False, + ): + self.name: str = os.fspath(filename) + self.mode = mode + self.encoding = encoding + self.errors = errors + self.atomic = atomic + self._f: t.IO[t.Any] | None + self.should_close: bool + + if self.name == "-": + self._f, self.should_close = open_stream(filename, mode, encoding, errors) + else: + if "r" in mode: + # Open and close the file in case we're opening it for + # reading so that we can catch at least some errors in + # some cases early. + open(filename, mode).close() + self._f = None + self.should_close = True + + def __getattr__(self, name: str) -> t.Any: + return getattr(self.open(), name) + + def __repr__(self) -> str: + if self._f is not None: + return repr(self._f) + return f"" + + def open(self) -> t.IO[t.Any]: + """Opens the file if it's not yet open. This call might fail with + a :exc:`FileError`. Not handling this error will produce an error + that Click shows. + """ + if self._f is not None: + return self._f + try: + rv, self.should_close = open_stream( + self.name, self.mode, self.encoding, self.errors, atomic=self.atomic + ) + except OSError as e: + from .exceptions import FileError + + raise FileError(self.name, hint=e.strerror) from e + self._f = rv + return rv + + def close(self) -> None: + """Closes the underlying file, no matter what.""" + if self._f is not None: + self._f.close() + + def close_intelligently(self) -> None: + """This function only closes the file if it was opened by the lazy + file wrapper. For instance this will never close stdin. + """ + if self.should_close: + self.close() + + def __enter__(self) -> LazyFile: + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_value: BaseException | None, + tb: TracebackType | None, + ) -> None: + self.close_intelligently() + + def __iter__(self) -> cabc.Iterator[t.AnyStr]: + self.open() + return iter(self._f) # type: ignore + + +class KeepOpenFile: + def __init__(self, file: t.IO[t.Any]) -> None: + self._file: t.IO[t.Any] = file + + def __getattr__(self, name: str) -> t.Any: + return getattr(self._file, name) + + def __enter__(self) -> KeepOpenFile: + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_value: BaseException | None, + tb: TracebackType | None, + ) -> None: + pass + + def __repr__(self) -> str: + return repr(self._file) + + def __iter__(self) -> cabc.Iterator[t.AnyStr]: + return iter(self._file) + + +def echo( + message: t.Any | None = None, + file: t.IO[t.Any] | None = None, + nl: bool = True, + err: bool = False, + color: bool | None = None, +) -> None: + """Print a message and newline to stdout or a file. This should be + used instead of :func:`print` because it provides better support + for different data, files, and environments. + + Compared to :func:`print`, this does the following: + + - Ensures that the output encoding is not misconfigured on Linux. + - Supports Unicode in the Windows console. + - Supports writing to binary outputs, and supports writing bytes + to text outputs. + - Supports colors and styles on Windows. + - Removes ANSI color and style codes if the output does not look + like an interactive terminal. + - Always flushes the output. + + :param message: The string or bytes to output. Other objects are + converted to strings. + :param file: The file to write to. Defaults to ``stdout``. + :param err: Write to ``stderr`` instead of ``stdout``. + :param nl: Print a newline after the message. Enabled by default. + :param color: Force showing or hiding colors and other styles. By + default Click will remove color if the output does not look like + an interactive terminal. + + .. versionchanged:: 6.0 + Support Unicode output on the Windows console. Click does not + modify ``sys.stdout``, so ``sys.stdout.write()`` and ``print()`` + will still not support Unicode. + + .. versionchanged:: 4.0 + Added the ``color`` parameter. + + .. versionadded:: 3.0 + Added the ``err`` parameter. + + .. versionchanged:: 2.0 + Support colors on Windows if colorama is installed. + """ + if file is None: + if err: + file = _default_text_stderr() + else: + file = _default_text_stdout() + + # There are no standard streams attached to write to. For example, + # pythonw on Windows. + if file is None: + return + + # Convert non bytes/text into the native string type. + if message is not None and not isinstance(message, (str, bytes, bytearray)): + out: str | bytes | None = str(message) + else: + out = message + + if nl: + out = out or "" + if isinstance(out, str): + out += "\n" + else: + out += b"\n" + + if not out: + file.flush() + return + + # If there is a message and the value looks like bytes, we manually + # need to find the binary stream and write the message in there. + # This is done separately so that most stream types will work as you + # would expect. Eg: you can write to StringIO for other cases. + if isinstance(out, (bytes, bytearray)): + binary_file = _find_binary_writer(file) + + if binary_file is not None: + file.flush() + binary_file.write(out) + binary_file.flush() + return + + # ANSI style code support. For no message or bytes, nothing happens. + # When outputting to a file instead of a terminal, strip codes. + else: + color = resolve_color_default(color) + + if should_strip_ansi(file, color): + out = strip_ansi(out) + elif WIN: + if auto_wrap_for_ansi is not None: + file = auto_wrap_for_ansi(file, color) # type: ignore + elif not color: + out = strip_ansi(out) + + file.write(out) # type: ignore + file.flush() + + +def get_binary_stream(name: t.Literal["stdin", "stdout", "stderr"]) -> t.BinaryIO: + """Returns a system stream for byte processing. + + :param name: the name of the stream to open. Valid names are ``'stdin'``, + ``'stdout'`` and ``'stderr'`` + """ + opener = binary_streams.get(name) + if opener is None: + raise TypeError(f"Unknown standard stream '{name}'") + return opener() + + +def get_text_stream( + name: t.Literal["stdin", "stdout", "stderr"], + encoding: str | None = None, + errors: str | None = "strict", +) -> t.TextIO: + """Returns a system stream for text processing. This usually returns + a wrapped stream around a binary stream returned from + :func:`get_binary_stream` but it also can take shortcuts for already + correctly configured streams. + + :param name: the name of the stream to open. Valid names are ``'stdin'``, + ``'stdout'`` and ``'stderr'`` + :param encoding: overrides the detected default encoding. + :param errors: overrides the default error mode. + """ + opener = text_streams.get(name) + if opener is None: + raise TypeError(f"Unknown standard stream '{name}'") + return opener(encoding, errors) + + +def open_file( + filename: str | os.PathLike[str], + mode: str = "r", + encoding: str | None = None, + errors: str | None = "strict", + lazy: bool = False, + atomic: bool = False, +) -> t.IO[t.Any]: + """Open a file, with extra behavior to handle ``'-'`` to indicate + a standard stream, lazy open on write, and atomic write. Similar to + the behavior of the :class:`~click.File` param type. + + If ``'-'`` is given to open ``stdout`` or ``stdin``, the stream is + wrapped so that using it in a context manager will not close it. + This makes it possible to use the function without accidentally + closing a standard stream: + + .. code-block:: python + + with open_file(filename) as f: + ... + + :param filename: The name or Path of the file to open, or ``'-'`` for + ``stdin``/``stdout``. + :param mode: The mode in which to open the file. + :param encoding: The encoding to decode or encode a file opened in + text mode. + :param errors: The error handling mode. + :param lazy: Wait to open the file until it is accessed. For read + mode, the file is temporarily opened to raise access errors + early, then closed until it is read again. + :param atomic: Write to a temporary file and replace the given file + on close. + + .. versionadded:: 3.0 + """ + if lazy: + return t.cast( + "t.IO[t.Any]", LazyFile(filename, mode, encoding, errors, atomic=atomic) + ) + + f, should_close = open_stream(filename, mode, encoding, errors, atomic=atomic) + + if not should_close: + f = t.cast("t.IO[t.Any]", KeepOpenFile(f)) + + return f + + +def format_filename( + filename: str | bytes | os.PathLike[str] | os.PathLike[bytes], + shorten: bool = False, +) -> str: + """Format a filename as a string for display. Ensures the filename can be + displayed by replacing any invalid bytes or surrogate escapes in the name + with the replacement character ``�``. + + Invalid bytes or surrogate escapes will raise an error when written to a + stream with ``errors="strict"``. This will typically happen with ``stdout`` + when the locale is something like ``en_GB.UTF-8``. + + Many scenarios *are* safe to write surrogates though, due to PEP 538 and + PEP 540, including: + + - Writing to ``stderr``, which uses ``errors="backslashreplace"``. + - The system has ``LANG=C.UTF-8``, ``C``, or ``POSIX``. Python opens + stdout and stderr with ``errors="surrogateescape"``. + - None of ``LANG/LC_*`` are set. Python assumes ``LANG=C.UTF-8``. + - Python is started in UTF-8 mode with ``PYTHONUTF8=1`` or ``-X utf8``. + Python opens stdout and stderr with ``errors="surrogateescape"``. + + :param filename: formats a filename for UI display. This will also convert + the filename into unicode without failing. + :param shorten: this optionally shortens the filename to strip of the + path that leads up to it. + """ + if shorten: + filename = os.path.basename(filename) + else: + filename = os.fspath(filename) + + if isinstance(filename, bytes): + filename = filename.decode(sys.getfilesystemencoding(), "replace") + else: + filename = filename.encode("utf-8", "surrogateescape").decode( + "utf-8", "replace" + ) + + return filename + + +def get_app_dir(app_name: str, roaming: bool = True, force_posix: bool = False) -> str: + r"""Returns the config folder for the application. The default behavior + is to return whatever is most appropriate for the operating system. + + To give you an idea, for an app called ``"Foo Bar"``, something like + the following folders could be returned: + + Mac OS X: + ``~/Library/Application Support/Foo Bar`` + Mac OS X (POSIX): + ``~/.foo-bar`` + Unix: + ``~/.config/foo-bar`` + Unix (POSIX): + ``~/.foo-bar`` + Windows (roaming): + ``C:\Users\\AppData\Roaming\Foo Bar`` + Windows (not roaming): + ``C:\Users\\AppData\Local\Foo Bar`` + + .. versionadded:: 2.0 + + :param app_name: the application name. This should be properly capitalized + and can contain whitespace. + :param roaming: controls if the folder should be roaming or not on Windows. + Has no effect otherwise. + :param force_posix: if this is set to `True` then on any POSIX system the + folder will be stored in the home folder with a leading + dot instead of the XDG config home or darwin's + application support folder. + """ + if WIN: + key = "APPDATA" if roaming else "LOCALAPPDATA" + folder = os.environ.get(key) + if folder is None: + folder = os.path.expanduser("~") + return os.path.join(folder, app_name) + if force_posix: + return os.path.join(os.path.expanduser(f"~/.{_posixify(app_name)}")) + if sys.platform == "darwin": + return os.path.join( + os.path.expanduser("~/Library/Application Support"), app_name + ) + return os.path.join( + os.environ.get("XDG_CONFIG_HOME", os.path.expanduser("~/.config")), + _posixify(app_name), + ) + + +class PacifyFlushWrapper: + """This wrapper is used to catch and suppress BrokenPipeErrors resulting + from ``.flush()`` being called on broken pipe during the shutdown/final-GC + of the Python interpreter. Notably ``.flush()`` is always called on + ``sys.stdout`` and ``sys.stderr``. So as to have minimal impact on any + other cleanup code, and the case where the underlying file is not a broken + pipe, all calls and attributes are proxied. + """ + + def __init__(self, wrapped: t.IO[t.Any]) -> None: + self.wrapped = wrapped + + def flush(self) -> None: + try: + self.wrapped.flush() + except OSError as e: + import errno + + if e.errno != errno.EPIPE: + raise + + def __getattr__(self, attr: str) -> t.Any: + return getattr(self.wrapped, attr) + + +def _detect_program_name( + path: str | None = None, _main: ModuleType | None = None +) -> str: + """Determine the command used to run the program, for use in help + text. If a file or entry point was executed, the file name is + returned. If ``python -m`` was used to execute a module or package, + ``python -m name`` is returned. + + This doesn't try to be too precise, the goal is to give a concise + name for help text. Files are only shown as their name without the + path. ``python`` is only shown for modules, and the full path to + ``sys.executable`` is not shown. + + :param path: The Python file being executed. Python puts this in + ``sys.argv[0]``, which is used by default. + :param _main: The ``__main__`` module. This should only be passed + during internal testing. + + .. versionadded:: 8.0 + Based on command args detection in the Werkzeug reloader. + + :meta private: + """ + if _main is None: + _main = sys.modules["__main__"] + + if not path: + path = sys.argv[0] + + # The value of __package__ indicates how Python was called. It may + # not exist if a setuptools script is installed as an egg. It may be + # set incorrectly for entry points created with pip on Windows. + # It is set to "" inside a Shiv or PEX zipapp. + if getattr(_main, "__package__", None) in {None, ""} or ( + os.name == "nt" + and _main.__package__ == "" + and not os.path.exists(path) + and os.path.exists(f"{path}.exe") + ): + # Executed a file, like "python app.py". + return os.path.basename(path) + + # Executed a module, like "python -m example". + # Rewritten by Python from "-m script" to "/path/to/script.py". + # Need to look at main module to determine how it was executed. + py_module = t.cast(str, _main.__package__) + name = os.path.splitext(os.path.basename(path))[0] + + # A submodule like "example.cli". + if name != "__main__": + py_module = f"{py_module}.{name}" + + return f"python -m {py_module.lstrip('.')}" + + +def _expand_args( + args: cabc.Iterable[str], + *, + user: bool = True, + env: bool = True, + glob_recursive: bool = True, +) -> list[str]: + """Simulate Unix shell expansion with Python functions. + + See :func:`glob.glob`, :func:`os.path.expanduser`, and + :func:`os.path.expandvars`. + + This is intended for use on Windows, where the shell does not do any + expansion. It may not exactly match what a Unix shell would do. + + :param args: List of command line arguments to expand. + :param user: Expand user home directory. + :param env: Expand environment variables. + :param glob_recursive: ``**`` matches directories recursively. + + .. versionchanged:: 8.1 + Invalid glob patterns are treated as empty expansions rather + than raising an error. + + .. versionadded:: 8.0 + + :meta private: + """ + from glob import glob + + out = [] + + for arg in args: + if user: + arg = os.path.expanduser(arg) + + if env: + arg = os.path.expandvars(arg) + + try: + matches = glob(arg, recursive=glob_recursive) + except re.error: + matches = [] + + if not matches: + out.append(arg) + else: + out.extend(matches) + + return out diff --git a/lib/python3.12/site-packages/contourpy/__init__.py b/lib/python3.12/site-packages/contourpy/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..82d578db2fc3b165fe55fef879001733448d3524 --- /dev/null +++ b/lib/python3.12/site-packages/contourpy/__init__.py @@ -0,0 +1,285 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +import numpy as np + +from contourpy._contourpy import ( + ContourGenerator, + FillType, + LineType, + Mpl2005ContourGenerator, + Mpl2014ContourGenerator, + SerialContourGenerator, + ThreadedContourGenerator, + ZInterp, + max_threads, +) +from contourpy._version import __version__ +from contourpy.chunk import calc_chunk_sizes +from contourpy.convert import ( + convert_filled, + convert_lines, + convert_multi_filled, + convert_multi_lines, +) +from contourpy.dechunk import ( + dechunk_filled, + dechunk_lines, + dechunk_multi_filled, + dechunk_multi_lines, +) +from contourpy.enum_util import as_fill_type, as_line_type, as_z_interp + +if TYPE_CHECKING: + from typing import Any + + from numpy.typing import ArrayLike + + from ._contourpy import CoordinateArray, MaskArray + +__all__ = [ + "__version__", + "contour_generator", + "convert_filled", + "convert_lines", + "convert_multi_filled", + "convert_multi_lines", + "dechunk_filled", + "dechunk_lines", + "dechunk_multi_filled", + "dechunk_multi_lines", + "max_threads", + "FillType", + "LineType", + "ContourGenerator", + "Mpl2005ContourGenerator", + "Mpl2014ContourGenerator", + "SerialContourGenerator", + "ThreadedContourGenerator", + "ZInterp", +] + + +# Simple mapping of algorithm name to class name. +_class_lookup: dict[str, type[ContourGenerator]] = { + "mpl2005": Mpl2005ContourGenerator, + "mpl2014": Mpl2014ContourGenerator, + "serial": SerialContourGenerator, + "threaded": ThreadedContourGenerator, +} + + +def _remove_z_mask( + z: ArrayLike | np.ma.MaskedArray[Any, Any] | None, +) -> tuple[CoordinateArray, MaskArray | None]: + # Preserve mask if present. + z_array = np.ma.asarray(z, dtype=np.float64) # type: ignore[no-untyped-call] + z_masked = np.ma.masked_invalid(z_array, copy=False) # type: ignore[no-untyped-call] + + if np.ma.is_masked(z_masked): + mask = np.ma.getmask(z_masked) + else: + mask = None + + return np.ma.getdata(z_masked), mask # type: ignore[no-untyped-call] + + +def contour_generator( + x: ArrayLike | None = None, + y: ArrayLike | None = None, + z: ArrayLike | np.ma.MaskedArray[Any, Any] | None = None, + *, + name: str = "serial", + corner_mask: bool | None = None, + line_type: LineType | str | None = None, + fill_type: FillType | str | None = None, + chunk_size: int | tuple[int, int] | None = None, + chunk_count: int | tuple[int, int] | None = None, + total_chunk_count: int | None = None, + quad_as_tri: bool = False, + z_interp: ZInterp | str | None = ZInterp.Linear, + thread_count: int = 0, +) -> ContourGenerator: + """Create and return a :class:`~.ContourGenerator` object. + + The class and properties of the returned :class:`~.ContourGenerator` are determined by the + function arguments, with sensible defaults. + + Args: + x (array-like of shape (ny, nx) or (nx,), optional): The x-coordinates of the ``z`` values. + May be 2D with the same shape as ``z.shape``, or 1D with length ``nx = z.shape[1]``. + If not specified are assumed to be ``np.arange(nx)``. Must be ordered monotonically. + y (array-like of shape (ny, nx) or (ny,), optional): The y-coordinates of the ``z`` values. + May be 2D with the same shape as ``z.shape``, or 1D with length ``ny = z.shape[0]``. + If not specified are assumed to be ``np.arange(ny)``. Must be ordered monotonically. + z (array-like of shape (ny, nx), may be a masked array): The 2D gridded values to calculate + the contours of. May be a masked array, and any invalid values (``np.inf`` or + ``np.nan``) will also be masked out. + name (str): Algorithm name, one of ``"serial"``, ``"threaded"``, ``"mpl2005"`` or + ``"mpl2014"``, default ``"serial"``. + corner_mask (bool, optional): Enable/disable corner masking, which only has an effect if + ``z`` is a masked array. If ``False``, any quad touching a masked point is masked out. + If ``True``, only the triangular corners of quads nearest these points are always masked + out, other triangular corners comprising three unmasked points are contoured as usual. + If not specified, uses the default provided by the algorithm ``name``. + line_type (LineType or str, optional): The format of contour line data returned from calls + to :meth:`~.ContourGenerator.lines`, specified either as a :class:`~.LineType` or its + string equivalent such as ``"SeparateCode"``. + If not specified, uses the default provided by the algorithm ``name``. + The relationship between the :class:`~.LineType` enum and the data format returned from + :meth:`~.ContourGenerator.lines` is explained at :ref:`line_type`. + fill_type (FillType or str, optional): The format of filled contour data returned from calls + to :meth:`~.ContourGenerator.filled`, specified either as a :class:`~.FillType` or its + string equivalent such as ``"OuterOffset"``. + If not specified, uses the default provided by the algorithm ``name``. + The relationship between the :class:`~.FillType` enum and the data format returned from + :meth:`~.ContourGenerator.filled` is explained at :ref:`fill_type`. + chunk_size (int or tuple(int, int), optional): Chunk size in (y, x) directions, or the same + size in both directions if only one value is specified. + chunk_count (int or tuple(int, int), optional): Chunk count in (y, x) directions, or the + same count in both directions if only one value is specified. + total_chunk_count (int, optional): Total number of chunks. + quad_as_tri (bool): Enable/disable treating quads as 4 triangles, default ``False``. + If ``False``, a contour line within a quad is a straight line between points on two of + its edges. If ``True``, each full quad is divided into 4 triangles using a virtual point + at the centre (mean x, y of the corner points) and a contour line is piecewise linear + within those triangles. Corner-masked triangles are not affected by this setting, only + full unmasked quads. + z_interp (ZInterp or str, optional): How to interpolate ``z`` values when determining where + contour lines intersect the edges of quads and the ``z`` values of the central points of + quads, specified either as a :class:`~contourpy.ZInterp` or its string equivalent such + as ``"Log"``. Default is ``ZInterp.Linear``. + thread_count (int): Number of threads to use for contour calculation, default 0. Threads can + only be used with an algorithm ``name`` that supports threads (currently only + ``name="threaded"``) and there must be at least the same number of chunks as threads. + If ``thread_count=0`` and ``name="threaded"`` then it uses the maximum number of threads + as determined by the C++11 call ``std::thread::hardware_concurrency()``. If ``name`` is + something other than ``"threaded"`` then the ``thread_count`` will be set to ``1``. + + Return: + :class:`~.ContourGenerator`. + + Note: + A maximum of one of ``chunk_size``, ``chunk_count`` and ``total_chunk_count`` may be + specified. + + Warning: + The ``name="mpl2005"`` algorithm does not implement chunking for contour lines. + """ + x = np.asarray(x, dtype=np.float64) + y = np.asarray(y, dtype=np.float64) + z, mask = _remove_z_mask(z) + + # Check arguments: z. + if z.ndim != 2: + raise TypeError(f"Input z must be 2D, not {z.ndim}D") + + if z.shape[0] < 2 or z.shape[1] < 2: + raise TypeError(f"Input z must be at least a (2, 2) shaped array, but has shape {z.shape}") + + ny, nx = z.shape + + # Check arguments: x and y. + if x.ndim != y.ndim: + raise TypeError(f"Number of dimensions of x ({x.ndim}) and y ({y.ndim}) do not match") + + if x.ndim == 0: + x = np.arange(nx, dtype=np.float64) + y = np.arange(ny, dtype=np.float64) + x, y = np.meshgrid(x, y) + elif x.ndim == 1: + if len(x) != nx: + raise TypeError(f"Length of x ({len(x)}) must match number of columns in z ({nx})") + if len(y) != ny: + raise TypeError(f"Length of y ({len(y)}) must match number of rows in z ({ny})") + x, y = np.meshgrid(x, y) + elif x.ndim == 2: + if x.shape != z.shape: + raise TypeError(f"Shapes of x {x.shape} and z {z.shape} do not match") + if y.shape != z.shape: + raise TypeError(f"Shapes of y {y.shape} and z {z.shape} do not match") + else: + raise TypeError(f"Inputs x and y must be None, 1D or 2D, not {x.ndim}D") + + # Check mask shape just in case. + if mask is not None and mask.shape != z.shape: + raise ValueError("If mask is set it must be a 2D array with the same shape as z") + + # Check arguments: name. + if name not in _class_lookup: + raise ValueError(f"Unrecognised contour generator name: {name}") + + # Check arguments: chunk_size, chunk_count and total_chunk_count. + y_chunk_size, x_chunk_size = calc_chunk_sizes( + chunk_size, chunk_count, total_chunk_count, ny, nx) + + cls = _class_lookup[name] + + # Check arguments: corner_mask. + if corner_mask is None: + # Set it to default, which is True if the algorithm supports it. + corner_mask = cls.supports_corner_mask() + elif corner_mask and not cls.supports_corner_mask(): + raise ValueError(f"{name} contour generator does not support corner_mask=True") + + # Check arguments: line_type. + if line_type is None: + line_type = cls.default_line_type + else: + line_type = as_line_type(line_type) + + if not cls.supports_line_type(line_type): + raise ValueError(f"{name} contour generator does not support line_type {line_type}") + + # Check arguments: fill_type. + if fill_type is None: + fill_type = cls.default_fill_type + else: + fill_type = as_fill_type(fill_type) + + if not cls.supports_fill_type(fill_type): + raise ValueError(f"{name} contour generator does not support fill_type {fill_type}") + + # Check arguments: quad_as_tri. + if quad_as_tri and not cls.supports_quad_as_tri(): + raise ValueError(f"{name} contour generator does not support quad_as_tri=True") + + # Check arguments: z_interp. + if z_interp is None: + z_interp = ZInterp.Linear + else: + z_interp = as_z_interp(z_interp) + + if z_interp != ZInterp.Linear and not cls.supports_z_interp(): + raise ValueError(f"{name} contour generator does not support z_interp {z_interp}") + + # Check arguments: thread_count. + if thread_count not in (0, 1) and not cls.supports_threads(): + raise ValueError(f"{name} contour generator does not support thread_count {thread_count}") + + # Prepare args and kwargs for contour generator constructor. + args = [x, y, z, mask] + kwargs: dict[str, int | bool | LineType | FillType | ZInterp] = { + "x_chunk_size": x_chunk_size, + "y_chunk_size": y_chunk_size, + } + + if name not in ("mpl2005", "mpl2014"): + kwargs["line_type"] = line_type + kwargs["fill_type"] = fill_type + + if cls.supports_corner_mask(): + kwargs["corner_mask"] = corner_mask + + if cls.supports_quad_as_tri(): + kwargs["quad_as_tri"] = quad_as_tri + + if cls.supports_z_interp(): + kwargs["z_interp"] = z_interp + + if cls.supports_threads(): + kwargs["thread_count"] = thread_count + + # Create contour generator. + return cls(*args, **kwargs) diff --git a/lib/python3.12/site-packages/contourpy/__pycache__/__init__.cpython-312.pyc b/lib/python3.12/site-packages/contourpy/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3d493479da50631893a8f010a4bb7f1d07467eee Binary files /dev/null and b/lib/python3.12/site-packages/contourpy/__pycache__/__init__.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/contourpy/__pycache__/_version.cpython-312.pyc b/lib/python3.12/site-packages/contourpy/__pycache__/_version.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f632cbe6624f77c38260bab1fe8f11fde49c483c Binary files /dev/null and b/lib/python3.12/site-packages/contourpy/__pycache__/_version.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/contourpy/__pycache__/array.cpython-312.pyc b/lib/python3.12/site-packages/contourpy/__pycache__/array.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..10e85bf92a183f7db839e111f05bba3b61ad5dcd Binary files /dev/null and b/lib/python3.12/site-packages/contourpy/__pycache__/array.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/contourpy/__pycache__/chunk.cpython-312.pyc b/lib/python3.12/site-packages/contourpy/__pycache__/chunk.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..881b821b7457539b6a7fd58a6857041286835462 Binary files /dev/null and b/lib/python3.12/site-packages/contourpy/__pycache__/chunk.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/contourpy/__pycache__/convert.cpython-312.pyc b/lib/python3.12/site-packages/contourpy/__pycache__/convert.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..654061aeef66717c18e478ce77d47f3fec195b5b Binary files /dev/null and b/lib/python3.12/site-packages/contourpy/__pycache__/convert.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/contourpy/__pycache__/dechunk.cpython-312.pyc b/lib/python3.12/site-packages/contourpy/__pycache__/dechunk.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e4e8e0cb343830fa64041dfb650d896c5aedbcd6 Binary files /dev/null and b/lib/python3.12/site-packages/contourpy/__pycache__/dechunk.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/contourpy/__pycache__/enum_util.cpython-312.pyc b/lib/python3.12/site-packages/contourpy/__pycache__/enum_util.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..aa65e08c491e1ca9991ef40a98cd1b4f7fa4ac5b Binary files /dev/null and b/lib/python3.12/site-packages/contourpy/__pycache__/enum_util.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/contourpy/__pycache__/typecheck.cpython-312.pyc b/lib/python3.12/site-packages/contourpy/__pycache__/typecheck.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..122307a56d2cf3f4721a47b470eb8388bcebe975 Binary files /dev/null and b/lib/python3.12/site-packages/contourpy/__pycache__/typecheck.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/contourpy/__pycache__/types.cpython-312.pyc b/lib/python3.12/site-packages/contourpy/__pycache__/types.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b6a614c71617b788439ee8491815101b19c9ecd0 Binary files /dev/null and b/lib/python3.12/site-packages/contourpy/__pycache__/types.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/contourpy/_contourpy.pyi b/lib/python3.12/site-packages/contourpy/_contourpy.pyi new file mode 100644 index 0000000000000000000000000000000000000000..5f44eace52ab6df5f3d05443c89e17f363dd8174 --- /dev/null +++ b/lib/python3.12/site-packages/contourpy/_contourpy.pyi @@ -0,0 +1,199 @@ +from typing import ClassVar, NoReturn, TypeAlias + +import numpy as np +import numpy.typing as npt + +import contourpy._contourpy as cpy + +# Input numpy array types, the same as in common.h +CoordinateArray: TypeAlias = npt.NDArray[np.float64] +MaskArray: TypeAlias = npt.NDArray[np.bool_] +LevelArray: TypeAlias = npt.ArrayLike + +# Output numpy array types, the same as in common.h +PointArray: TypeAlias = npt.NDArray[np.float64] +CodeArray: TypeAlias = npt.NDArray[np.uint8] +OffsetArray: TypeAlias = npt.NDArray[np.uint32] + +# Types returned from filled() +FillReturn_OuterCode: TypeAlias = tuple[list[PointArray], list[CodeArray]] +FillReturn_OuterOffset: TypeAlias = tuple[list[PointArray], list[OffsetArray]] +FillReturn_ChunkCombinedCode: TypeAlias = tuple[list[PointArray | None], list[CodeArray | None]] +FillReturn_ChunkCombinedOffset: TypeAlias = tuple[list[PointArray | None], list[OffsetArray | None]] +FillReturn_ChunkCombinedCodeOffset: TypeAlias = tuple[list[PointArray | None], list[CodeArray | None], list[OffsetArray | None]] +FillReturn_ChunkCombinedOffsetOffset: TypeAlias = tuple[list[PointArray | None], list[OffsetArray | None], list[OffsetArray | None]] +FillReturn_Chunk: TypeAlias = FillReturn_ChunkCombinedCode | FillReturn_ChunkCombinedOffset | FillReturn_ChunkCombinedCodeOffset | FillReturn_ChunkCombinedOffsetOffset +FillReturn: TypeAlias = FillReturn_OuterCode | FillReturn_OuterOffset | FillReturn_Chunk + +# Types returned from lines() +LineReturn_Separate: TypeAlias = list[PointArray] +LineReturn_SeparateCode: TypeAlias = tuple[list[PointArray], list[CodeArray]] +LineReturn_ChunkCombinedCode: TypeAlias = tuple[list[PointArray | None], list[CodeArray | None]] +LineReturn_ChunkCombinedOffset: TypeAlias = tuple[list[PointArray | None], list[OffsetArray | None]] +LineReturn_ChunkCombinedNan: TypeAlias = tuple[list[PointArray | None]] +LineReturn_Chunk: TypeAlias = LineReturn_ChunkCombinedCode | LineReturn_ChunkCombinedOffset | LineReturn_ChunkCombinedNan +LineReturn: TypeAlias = LineReturn_Separate | LineReturn_SeparateCode | LineReturn_Chunk + + +NDEBUG: int +__version__: str + +class FillType: + ChunkCombinedCode: ClassVar[cpy.FillType] + ChunkCombinedCodeOffset: ClassVar[cpy.FillType] + ChunkCombinedOffset: ClassVar[cpy.FillType] + ChunkCombinedOffsetOffset: ClassVar[cpy.FillType] + OuterCode: ClassVar[cpy.FillType] + OuterOffset: ClassVar[cpy.FillType] + __members__: ClassVar[dict[str, cpy.FillType]] + def __eq__(self, other: object) -> bool: ... + def __getstate__(self) -> int: ... + def __hash__(self) -> int: ... + def __index__(self) -> int: ... + def __init__(self, value: int) -> None: ... + def __int__(self) -> int: ... + def __ne__(self, other: object) -> bool: ... + def __setstate__(self, state: int) -> NoReturn: ... + @property + def name(self) -> str: ... + @property + def value(self) -> int: ... + +class LineType: + ChunkCombinedCode: ClassVar[cpy.LineType] + ChunkCombinedNan: ClassVar[cpy.LineType] + ChunkCombinedOffset: ClassVar[cpy.LineType] + Separate: ClassVar[cpy.LineType] + SeparateCode: ClassVar[cpy.LineType] + __members__: ClassVar[dict[str, cpy.LineType]] + def __eq__(self, other: object) -> bool: ... + def __getstate__(self) -> int: ... + def __hash__(self) -> int: ... + def __index__(self) -> int: ... + def __init__(self, value: int) -> None: ... + def __int__(self) -> int: ... + def __ne__(self, other: object) -> bool: ... + def __setstate__(self, state: int) -> NoReturn: ... + @property + def name(self) -> str: ... + @property + def value(self) -> int: ... + +class ZInterp: + Linear: ClassVar[cpy.ZInterp] + Log: ClassVar[cpy.ZInterp] + __members__: ClassVar[dict[str, cpy.ZInterp]] + def __eq__(self, other: object) -> bool: ... + def __getstate__(self) -> int: ... + def __hash__(self) -> int: ... + def __index__(self) -> int: ... + def __init__(self, value: int) -> None: ... + def __int__(self) -> int: ... + def __ne__(self, other: object) -> bool: ... + def __setstate__(self, state: int) -> NoReturn: ... + @property + def name(self) -> str: ... + @property + def value(self) -> int: ... + +def max_threads() -> int: ... + +class ContourGenerator: + def create_contour(self, level: float) -> LineReturn: ... + def create_filled_contour(self, lower_level: float, upper_level: float) -> FillReturn: ... + def filled(self, lower_level: float, upper_level: float) -> FillReturn: ... + def lines(self, level: float) -> LineReturn: ... + def multi_filled(self, levels: LevelArray) -> list[FillReturn]: ... + def multi_lines(self, levels: LevelArray) -> list[LineReturn]: ... + @staticmethod + def supports_corner_mask() -> bool: ... + @staticmethod + def supports_fill_type(fill_type: FillType) -> bool: ... + @staticmethod + def supports_line_type(line_type: LineType) -> bool: ... + @staticmethod + def supports_quad_as_tri() -> bool: ... + @staticmethod + def supports_threads() -> bool: ... + @staticmethod + def supports_z_interp() -> bool: ... + @property + def chunk_count(self) -> tuple[int, int]: ... + @property + def chunk_size(self) -> tuple[int, int]: ... + @property + def corner_mask(self) -> bool: ... + @property + def fill_type(self) -> FillType: ... + @property + def line_type(self) -> LineType: ... + @property + def quad_as_tri(self) -> bool: ... + @property + def thread_count(self) -> int: ... + @property + def z_interp(self) -> ZInterp: ... + default_fill_type: cpy.FillType + default_line_type: cpy.LineType + +class Mpl2005ContourGenerator(ContourGenerator): + def __init__( + self, + x: CoordinateArray, + y: CoordinateArray, + z: CoordinateArray, + mask: MaskArray, + *, + x_chunk_size: int = 0, + y_chunk_size: int = 0, + ) -> None: ... + +class Mpl2014ContourGenerator(ContourGenerator): + def __init__( + self, + x: CoordinateArray, + y: CoordinateArray, + z: CoordinateArray, + mask: MaskArray, + *, + corner_mask: bool, + x_chunk_size: int = 0, + y_chunk_size: int = 0, + ) -> None: ... + +class SerialContourGenerator(ContourGenerator): + def __init__( + self, + x: CoordinateArray, + y: CoordinateArray, + z: CoordinateArray, + mask: MaskArray, + *, + corner_mask: bool, + line_type: LineType, + fill_type: FillType, + quad_as_tri: bool, + z_interp: ZInterp, + x_chunk_size: int = 0, + y_chunk_size: int = 0, + ) -> None: ... + def _write_cache(self) -> NoReturn: ... + +class ThreadedContourGenerator(ContourGenerator): + def __init__( + self, + x: CoordinateArray, + y: CoordinateArray, + z: CoordinateArray, + mask: MaskArray, + *, + corner_mask: bool, + line_type: LineType, + fill_type: FillType, + quad_as_tri: bool, + z_interp: ZInterp, + x_chunk_size: int = 0, + y_chunk_size: int = 0, + thread_count: int = 0, + ) -> None: ... + def _write_cache(self) -> None: ... diff --git a/lib/python3.12/site-packages/contourpy/_version.py b/lib/python3.12/site-packages/contourpy/_version.py new file mode 100644 index 0000000000000000000000000000000000000000..7b1e312007554cf3d9c95081dc04bb15e77d3018 --- /dev/null +++ b/lib/python3.12/site-packages/contourpy/_version.py @@ -0,0 +1 @@ +__version__ = "1.3.3" diff --git a/lib/python3.12/site-packages/contourpy/array.py b/lib/python3.12/site-packages/contourpy/array.py new file mode 100644 index 0000000000000000000000000000000000000000..a7054fae1dc2f39b2dc6789e60818b1dd9b49c55 --- /dev/null +++ b/lib/python3.12/site-packages/contourpy/array.py @@ -0,0 +1,261 @@ +from __future__ import annotations + +from itertools import chain, pairwise +from typing import TYPE_CHECKING + +import numpy as np + +from contourpy.typecheck import check_code_array, check_offset_array, check_point_array +from contourpy.types import CLOSEPOLY, LINETO, MOVETO, code_dtype, offset_dtype, point_dtype + +if TYPE_CHECKING: + import contourpy._contourpy as cpy + + +def codes_from_offsets(offsets: cpy.OffsetArray) -> cpy.CodeArray: + """Determine codes from offsets, assuming they all correspond to closed polygons. + """ + check_offset_array(offsets) + + n = offsets[-1] + codes = np.full(n, LINETO, dtype=code_dtype) + codes[offsets[:-1]] = MOVETO + codes[offsets[1:] - 1] = CLOSEPOLY + return codes + + +def codes_from_offsets_and_points( + offsets: cpy.OffsetArray, + points: cpy.PointArray, +) -> cpy.CodeArray: + """Determine codes from offsets and points, using the equality of the start and end points of + each line to determine if lines are closed or not. + """ + check_offset_array(offsets) + check_point_array(points) + + codes = np.full(len(points), LINETO, dtype=code_dtype) + codes[offsets[:-1]] = MOVETO + + end_offsets = offsets[1:] - 1 + closed = np.all(points[offsets[:-1]] == points[end_offsets], axis=1) + codes[end_offsets[closed]] = CLOSEPOLY + + return codes + + +def codes_from_points(points: cpy.PointArray) -> cpy.CodeArray: + """Determine codes for a single line, using the equality of the start and end points to + determine if the line is closed or not. + """ + check_point_array(points) + + n = len(points) + codes = np.full(n, LINETO, dtype=code_dtype) + codes[0] = MOVETO + if np.all(points[0] == points[-1]): + codes[-1] = CLOSEPOLY + return codes + + +def concat_codes(list_of_codes: list[cpy.CodeArray]) -> cpy.CodeArray: + """Concatenate a list of codes arrays into a single code array. + """ + if not list_of_codes: + raise ValueError("Empty list passed to concat_codes") + + return np.concatenate(list_of_codes, dtype=code_dtype) + + +def concat_codes_or_none(list_of_codes_or_none: list[cpy.CodeArray | None]) -> cpy.CodeArray | None: + """Concatenate a list of codes arrays or None into a single code array or None. + """ + list_of_codes = [codes for codes in list_of_codes_or_none if codes is not None] + if list_of_codes: + return concat_codes(list_of_codes) + else: + return None + + +def concat_offsets(list_of_offsets: list[cpy.OffsetArray]) -> cpy.OffsetArray: + """Concatenate a list of offsets arrays into a single offset array. + """ + if not list_of_offsets: + raise ValueError("Empty list passed to concat_offsets") + + n = len(list_of_offsets) + cumulative = np.cumsum([offsets[-1] for offsets in list_of_offsets], dtype=offset_dtype) + ret: cpy.OffsetArray = np.concatenate( + (list_of_offsets[0], *(list_of_offsets[i+1][1:] + cumulative[i] for i in range(n-1))), + dtype=offset_dtype, + ) + return ret + + +def concat_offsets_or_none( + list_of_offsets_or_none: list[cpy.OffsetArray | None], +) -> cpy.OffsetArray | None: + """Concatenate a list of offsets arrays or None into a single offset array or None. + """ + list_of_offsets = [offsets for offsets in list_of_offsets_or_none if offsets is not None] + if list_of_offsets: + return concat_offsets(list_of_offsets) + else: + return None + + +def concat_points(list_of_points: list[cpy.PointArray]) -> cpy.PointArray: + """Concatenate a list of point arrays into a single point array. + """ + if not list_of_points: + raise ValueError("Empty list passed to concat_points") + + return np.concatenate(list_of_points, dtype=point_dtype) + + +def concat_points_or_none( + list_of_points_or_none: list[cpy.PointArray | None], +) -> cpy.PointArray | None: + """Concatenate a list of point arrays or None into a single point array or None. + """ + list_of_points = [points for points in list_of_points_or_none if points is not None] + if list_of_points: + return concat_points(list_of_points) + else: + return None + + +def concat_points_or_none_with_nan( + list_of_points_or_none: list[cpy.PointArray | None], +) -> cpy.PointArray | None: + """Concatenate a list of points or None into a single point array or None, with NaNs used to + separate each line. + """ + list_of_points = [points for points in list_of_points_or_none if points is not None] + if list_of_points: + return concat_points_with_nan(list_of_points) + else: + return None + + +def concat_points_with_nan(list_of_points: list[cpy.PointArray]) -> cpy.PointArray: + """Concatenate a list of points into a single point array with NaNs used to separate each line. + """ + if not list_of_points: + raise ValueError("Empty list passed to concat_points_with_nan") + + if len(list_of_points) == 1: + return list_of_points[0] + else: + nan_spacer = np.full((1, 2), np.nan, dtype=point_dtype) + list_of_points = [list_of_points[0], + *list(chain(*((nan_spacer, x) for x in list_of_points[1:])))] + return concat_points(list_of_points) + + +def insert_nan_at_offsets(points: cpy.PointArray, offsets: cpy.OffsetArray) -> cpy.PointArray: + """Insert NaNs into a point array at locations specified by an offset array. + """ + check_point_array(points) + check_offset_array(offsets) + + if len(offsets) <= 2: + return points + else: + nan_spacer = np.array([np.nan, np.nan], dtype=point_dtype) + # Convert offsets to int64 to avoid numpy error when mixing signed and unsigned ints. + return np.insert(points, offsets[1:-1].astype(np.int64), nan_spacer, axis=0) + + +def offsets_from_codes(codes: cpy.CodeArray) -> cpy.OffsetArray: + """Determine offsets from codes using locations of MOVETO codes. + """ + check_code_array(codes) + + return np.append(np.nonzero(codes == MOVETO)[0], len(codes)).astype(offset_dtype) + + +def offsets_from_lengths(list_of_points: list[cpy.PointArray]) -> cpy.OffsetArray: + """Determine offsets from lengths of point arrays. + """ + if not list_of_points: + raise ValueError("Empty list passed to offsets_from_lengths") + + return np.cumsum([0] + [len(line) for line in list_of_points], dtype=offset_dtype) + + +def outer_offsets_from_list_of_codes(list_of_codes: list[cpy.CodeArray]) -> cpy.OffsetArray: + """Determine outer offsets from codes using locations of MOVETO codes. + """ + if not list_of_codes: + raise ValueError("Empty list passed to outer_offsets_from_list_of_codes") + + return np.cumsum([0] + [np.count_nonzero(codes == MOVETO) for codes in list_of_codes], + dtype=offset_dtype) + + +def outer_offsets_from_list_of_offsets(list_of_offsets: list[cpy.OffsetArray]) -> cpy.OffsetArray: + """Determine outer offsets from a list of offsets. + """ + if not list_of_offsets: + raise ValueError("Empty list passed to outer_offsets_from_list_of_offsets") + + return np.cumsum([0] + [len(offsets)-1 for offsets in list_of_offsets], dtype=offset_dtype) + + +def remove_nan(points: cpy.PointArray) -> tuple[cpy.PointArray, cpy.OffsetArray]: + """Remove NaN from a points array, also return the offsets corresponding to the NaN removed. + """ + check_point_array(points) + + nan_offsets = np.nonzero(np.isnan(points[:, 0]))[0] + if len(nan_offsets) == 0: + return points, np.array([0, len(points)], dtype=offset_dtype) + else: + points = np.delete(points, nan_offsets, axis=0) + nan_offsets -= np.arange(len(nan_offsets)) + offsets: cpy.OffsetArray = np.empty(len(nan_offsets)+2, dtype=offset_dtype) + offsets[0] = 0 + offsets[1:-1] = nan_offsets + offsets[-1] = len(points) + return points, offsets + + +def split_codes_by_offsets(codes: cpy.CodeArray, offsets: cpy.OffsetArray) -> list[cpy.CodeArray]: + """Split a code array at locations specified by an offset array into a list of code arrays. + """ + check_code_array(codes) + check_offset_array(offsets) + + if len(offsets) > 2: + return np.split(codes, offsets[1:-1]) + else: + return [codes] + + +def split_points_by_offsets( + points: cpy.PointArray, + offsets: cpy.OffsetArray, +) -> list[cpy.PointArray]: + """Split a point array at locations specified by an offset array into a list of point arrays. + """ + check_point_array(points) + check_offset_array(offsets) + + if len(offsets) > 2: + return np.split(points, offsets[1:-1]) + else: + return [points] + + +def split_points_at_nan(points: cpy.PointArray) -> list[cpy.PointArray]: + """Split a points array at NaNs into a list of point arrays. + """ + check_point_array(points) + + nan_offsets = np.nonzero(np.isnan(points[:, 0]))[0] + if len(nan_offsets) == 0: + return [points] + else: + nan_offsets = np.concatenate(([-1], nan_offsets, [len(points)])) + return [points[s+1:e] for s, e in pairwise(nan_offsets)] diff --git a/lib/python3.12/site-packages/contourpy/chunk.py b/lib/python3.12/site-packages/contourpy/chunk.py new file mode 100644 index 0000000000000000000000000000000000000000..94fded1b161d64adb23748b067630370d8fa2f3c --- /dev/null +++ b/lib/python3.12/site-packages/contourpy/chunk.py @@ -0,0 +1,95 @@ +from __future__ import annotations + +import math + + +def calc_chunk_sizes( + chunk_size: int | tuple[int, int] | None, + chunk_count: int | tuple[int, int] | None, + total_chunk_count: int | None, + ny: int, + nx: int, +) -> tuple[int, int]: + """Calculate chunk sizes. + + Args: + chunk_size (int or tuple(int, int), optional): Chunk size in (y, x) directions, or the same + size in both directions if only one is specified. Cannot be negative. + chunk_count (int or tuple(int, int), optional): Chunk count in (y, x) directions, or the + same count in both directions if only one is specified. If less than 1, set to 1. + total_chunk_count (int, optional): Total number of chunks. If less than 1, set to 1. + ny (int): Number of grid points in y-direction. + nx (int): Number of grid points in x-direction. + + Return: + tuple(int, int): Chunk sizes (y_chunk_size, x_chunk_size). + + Note: + Zero or one of ``chunk_size``, ``chunk_count`` and ``total_chunk_count`` should be + specified. + """ + if sum([chunk_size is not None, chunk_count is not None, total_chunk_count is not None]) > 1: + raise ValueError("Only one of chunk_size, chunk_count and total_chunk_count should be set") + + if nx < 2 or ny < 2: + raise ValueError(f"(ny, nx) must be at least (2, 2), not ({ny}, {nx})") + + if total_chunk_count is not None: + max_chunk_count = (nx-1)*(ny-1) + total_chunk_count = min(max(total_chunk_count, 1), max_chunk_count) + if total_chunk_count == 1: + chunk_size = 0 + elif total_chunk_count == max_chunk_count: + chunk_size = (1, 1) + else: + factors = two_factors(total_chunk_count) + if ny > nx: + chunk_count = factors + else: + chunk_count = (factors[1], factors[0]) + + if chunk_count is not None: + if isinstance(chunk_count, tuple): + y_chunk_count, x_chunk_count = chunk_count + else: + y_chunk_count = x_chunk_count = chunk_count + x_chunk_count = min(max(x_chunk_count, 1), nx-1) + y_chunk_count = min(max(y_chunk_count, 1), ny-1) + chunk_size = (math.ceil((ny-1) / y_chunk_count), math.ceil((nx-1) / x_chunk_count)) + + if chunk_size is None: + y_chunk_size = x_chunk_size = 0 + elif isinstance(chunk_size, tuple): + y_chunk_size, x_chunk_size = chunk_size + else: + y_chunk_size = x_chunk_size = chunk_size + + if x_chunk_size < 0 or y_chunk_size < 0: + raise ValueError("chunk_size cannot be negative") + + return y_chunk_size, x_chunk_size + + +def two_factors(n: int) -> tuple[int, int]: + """Split an integer into two integer factors. + + The two factors will be as close as possible to the sqrt of n, and are returned in decreasing + order. Worst case returns (n, 1). + + Args: + n (int): The integer to factorize, must be positive. + + Return: + tuple(int, int): The two factors of n, in decreasing order. + """ + if n < 0: + raise ValueError(f"two_factors expects positive integer not {n}") + + i = math.ceil(math.sqrt(n)) + while n % i != 0: + i -= 1 + j = n // i + if i > j: + return i, j + else: + return j, i diff --git a/lib/python3.12/site-packages/contourpy/convert.py b/lib/python3.12/site-packages/contourpy/convert.py new file mode 100644 index 0000000000000000000000000000000000000000..ebdb1af8bf2f9b30c2d177dce9adf876dcb32835 --- /dev/null +++ b/lib/python3.12/site-packages/contourpy/convert.py @@ -0,0 +1,621 @@ +from __future__ import annotations + +from itertools import pairwise +from typing import TYPE_CHECKING, cast + +import numpy as np + +from contourpy._contourpy import FillType, LineType +import contourpy.array as arr +from contourpy.enum_util import as_fill_type, as_line_type +from contourpy.typecheck import check_filled, check_lines +from contourpy.types import MOVETO, offset_dtype + +if TYPE_CHECKING: + import contourpy._contourpy as cpy + + +def _convert_filled_from_OuterCode( + filled: cpy.FillReturn_OuterCode, + fill_type_to: FillType, +) -> cpy.FillReturn: + if fill_type_to == FillType.OuterCode: + return filled + elif fill_type_to == FillType.OuterOffset: + return (filled[0], [arr.offsets_from_codes(codes) for codes in filled[1]]) + + if len(filled[0]) > 0: + points = arr.concat_points(filled[0]) + codes = arr.concat_codes(filled[1]) + else: + points = None + codes = None + + if fill_type_to == FillType.ChunkCombinedCode: + return ([points], [codes]) + elif fill_type_to == FillType.ChunkCombinedOffset: + return ([points], [None if codes is None else arr.offsets_from_codes(codes)]) + elif fill_type_to == FillType.ChunkCombinedCodeOffset: + outer_offsets = None if points is None else arr.offsets_from_lengths(filled[0]) + ret1: cpy.FillReturn_ChunkCombinedCodeOffset = ([points], [codes], [outer_offsets]) + return ret1 + elif fill_type_to == FillType.ChunkCombinedOffsetOffset: + if codes is None: + ret2: cpy.FillReturn_ChunkCombinedOffsetOffset = ([None], [None], [None]) + else: + offsets = arr.offsets_from_codes(codes) + outer_offsets = arr.outer_offsets_from_list_of_codes(filled[1]) + ret2 = ([points], [offsets], [outer_offsets]) + return ret2 + else: + raise ValueError(f"Invalid FillType {fill_type_to}") + + +def _convert_filled_from_OuterOffset( + filled: cpy.FillReturn_OuterOffset, + fill_type_to: FillType, +) -> cpy.FillReturn: + if fill_type_to == FillType.OuterCode: + separate_codes = [arr.codes_from_offsets(offsets) for offsets in filled[1]] + return (filled[0], separate_codes) + elif fill_type_to == FillType.OuterOffset: + return filled + + if len(filled[0]) > 0: + points = arr.concat_points(filled[0]) + offsets = arr.concat_offsets(filled[1]) + else: + points = None + offsets = None + + if fill_type_to == FillType.ChunkCombinedCode: + return ([points], [None if offsets is None else arr.codes_from_offsets(offsets)]) + elif fill_type_to == FillType.ChunkCombinedOffset: + return ([points], [offsets]) + elif fill_type_to == FillType.ChunkCombinedCodeOffset: + if offsets is None: + ret1: cpy.FillReturn_ChunkCombinedCodeOffset = ([None], [None], [None]) + else: + codes = arr.codes_from_offsets(offsets) + outer_offsets = arr.offsets_from_lengths(filled[0]) + ret1 = ([points], [codes], [outer_offsets]) + return ret1 + elif fill_type_to == FillType.ChunkCombinedOffsetOffset: + if points is None: + ret2: cpy.FillReturn_ChunkCombinedOffsetOffset = ([None], [None], [None]) + else: + outer_offsets = arr.outer_offsets_from_list_of_offsets(filled[1]) + ret2 = ([points], [offsets], [outer_offsets]) + return ret2 + else: + raise ValueError(f"Invalid FillType {fill_type_to}") + + +def _convert_filled_from_ChunkCombinedCode( + filled: cpy.FillReturn_ChunkCombinedCode, + fill_type_to: FillType, +) -> cpy.FillReturn: + if fill_type_to == FillType.ChunkCombinedCode: + return filled + elif fill_type_to == FillType.ChunkCombinedOffset: + codes = [None if codes is None else arr.offsets_from_codes(codes) for codes in filled[1]] + return (filled[0], codes) + else: + raise ValueError( + f"Conversion from {FillType.ChunkCombinedCode} to {fill_type_to} not supported") + + +def _convert_filled_from_ChunkCombinedOffset( + filled: cpy.FillReturn_ChunkCombinedOffset, + fill_type_to: FillType, +) -> cpy.FillReturn: + if fill_type_to == FillType.ChunkCombinedCode: + chunk_codes: list[cpy.CodeArray | None] = [] + for points, offsets in zip(*filled): + if points is None: + chunk_codes.append(None) + else: + if TYPE_CHECKING: + assert offsets is not None + chunk_codes.append(arr.codes_from_offsets_and_points(offsets, points)) + return (filled[0], chunk_codes) + elif fill_type_to == FillType.ChunkCombinedOffset: + return filled + else: + raise ValueError( + f"Conversion from {FillType.ChunkCombinedOffset} to {fill_type_to} not supported") + + +def _convert_filled_from_ChunkCombinedCodeOffset( + filled: cpy.FillReturn_ChunkCombinedCodeOffset, + fill_type_to: FillType, +) -> cpy.FillReturn: + if fill_type_to == FillType.OuterCode: + separate_points = [] + separate_codes = [] + for points, codes, outer_offsets in zip(*filled): + if points is not None: + if TYPE_CHECKING: + assert codes is not None + assert outer_offsets is not None + separate_points += arr.split_points_by_offsets(points, outer_offsets) + separate_codes += arr.split_codes_by_offsets(codes, outer_offsets) + return (separate_points, separate_codes) + elif fill_type_to == FillType.OuterOffset: + separate_points = [] + separate_offsets = [] + for points, codes, outer_offsets in zip(*filled): + if points is not None: + if TYPE_CHECKING: + assert codes is not None + assert outer_offsets is not None + separate_points += arr.split_points_by_offsets(points, outer_offsets) + separate_codes = arr.split_codes_by_offsets(codes, outer_offsets) + separate_offsets += [arr.offsets_from_codes(codes) for codes in separate_codes] + return (separate_points, separate_offsets) + elif fill_type_to == FillType.ChunkCombinedCode: + ret1: cpy.FillReturn_ChunkCombinedCode = (filled[0], filled[1]) + return ret1 + elif fill_type_to == FillType.ChunkCombinedOffset: + all_offsets = [None if codes is None else arr.offsets_from_codes(codes) + for codes in filled[1]] + ret2: cpy.FillReturn_ChunkCombinedOffset = (filled[0], all_offsets) + return ret2 + elif fill_type_to == FillType.ChunkCombinedCodeOffset: + return filled + elif fill_type_to == FillType.ChunkCombinedOffsetOffset: + chunk_offsets: list[cpy.OffsetArray | None] = [] + chunk_outer_offsets: list[cpy.OffsetArray | None] = [] + for codes, outer_offsets in zip(*filled[1:]): + if codes is None: + chunk_offsets.append(None) + chunk_outer_offsets.append(None) + else: + if TYPE_CHECKING: + assert outer_offsets is not None + offsets = arr.offsets_from_codes(codes) + outer_offsets = np.array([np.nonzero(offsets == oo)[0][0] for oo in outer_offsets], + dtype=offset_dtype) + chunk_offsets.append(offsets) + chunk_outer_offsets.append(outer_offsets) + ret3: cpy.FillReturn_ChunkCombinedOffsetOffset = ( + filled[0], chunk_offsets, chunk_outer_offsets, + ) + return ret3 + else: + raise ValueError(f"Invalid FillType {fill_type_to}") + + +def _convert_filled_from_ChunkCombinedOffsetOffset( + filled: cpy.FillReturn_ChunkCombinedOffsetOffset, + fill_type_to: FillType, +) -> cpy.FillReturn: + if fill_type_to == FillType.OuterCode: + separate_points = [] + separate_codes = [] + for points, offsets, outer_offsets in zip(*filled): + if points is not None: + if TYPE_CHECKING: + assert offsets is not None + assert outer_offsets is not None + codes = arr.codes_from_offsets_and_points(offsets, points) + outer_offsets = offsets[outer_offsets] + separate_points += arr.split_points_by_offsets(points, outer_offsets) + separate_codes += arr.split_codes_by_offsets(codes, outer_offsets) + return (separate_points, separate_codes) + elif fill_type_to == FillType.OuterOffset: + separate_points = [] + separate_offsets = [] + for points, offsets, outer_offsets in zip(*filled): + if points is not None: + if TYPE_CHECKING: + assert offsets is not None + assert outer_offsets is not None + if len(outer_offsets) > 2: + separate_offsets += [offsets[s:e+1] - offsets[s] for s, e in + pairwise(outer_offsets)] + else: + separate_offsets.append(offsets) + separate_points += arr.split_points_by_offsets(points, offsets[outer_offsets]) + return (separate_points, separate_offsets) + elif fill_type_to == FillType.ChunkCombinedCode: + chunk_codes: list[cpy.CodeArray | None] = [] + for points, offsets, outer_offsets in zip(*filled): + if points is None: + chunk_codes.append(None) + else: + if TYPE_CHECKING: + assert offsets is not None + assert outer_offsets is not None + chunk_codes.append(arr.codes_from_offsets_and_points(offsets, points)) + ret1: cpy.FillReturn_ChunkCombinedCode = (filled[0], chunk_codes) + return ret1 + elif fill_type_to == FillType.ChunkCombinedOffset: + return (filled[0], filled[1]) + elif fill_type_to == FillType.ChunkCombinedCodeOffset: + chunk_codes = [] + chunk_outer_offsets: list[cpy.OffsetArray | None] = [] + for points, offsets, outer_offsets in zip(*filled): + if points is None: + chunk_codes.append(None) + chunk_outer_offsets.append(None) + else: + if TYPE_CHECKING: + assert offsets is not None + assert outer_offsets is not None + chunk_codes.append(arr.codes_from_offsets_and_points(offsets, points)) + chunk_outer_offsets.append(offsets[outer_offsets]) + ret2: cpy.FillReturn_ChunkCombinedCodeOffset = (filled[0], chunk_codes, chunk_outer_offsets) + return ret2 + elif fill_type_to == FillType.ChunkCombinedOffsetOffset: + return filled + else: + raise ValueError(f"Invalid FillType {fill_type_to}") + + +def convert_filled( + filled: cpy.FillReturn, + fill_type_from: FillType | str, + fill_type_to: FillType | str, +) -> cpy.FillReturn: + """Convert filled contours from one :class:`~.FillType` to another. + + Args: + filled (sequence of arrays): Filled contour polygons to convert, such as those returned by + :meth:`.ContourGenerator.filled`. + fill_type_from (FillType or str): :class:`~.FillType` to convert from as enum or + string equivalent. + fill_type_to (FillType or str): :class:`~.FillType` to convert to as enum or string + equivalent. + + Return: + Converted filled contour polygons. + + When converting non-chunked fill types (``FillType.OuterCode`` or ``FillType.OuterOffset``) to + chunked ones, all polygons are placed in the first chunk. When converting in the other + direction, all chunk information is discarded. Converting a fill type that is not aware of the + relationship between outer boundaries and contained holes (``FillType.ChunkCombinedCode`` or + ``FillType.ChunkCombinedOffset``) to one that is will raise a ``ValueError``. + + .. versionadded:: 1.2.0 + """ + fill_type_from = as_fill_type(fill_type_from) + fill_type_to = as_fill_type(fill_type_to) + + check_filled(filled, fill_type_from) + + if fill_type_from == FillType.OuterCode: + if TYPE_CHECKING: + filled = cast(cpy.FillReturn_OuterCode, filled) + return _convert_filled_from_OuterCode(filled, fill_type_to) + elif fill_type_from == FillType.OuterOffset: + if TYPE_CHECKING: + filled = cast(cpy.FillReturn_OuterOffset, filled) + return _convert_filled_from_OuterOffset(filled, fill_type_to) + elif fill_type_from == FillType.ChunkCombinedCode: + if TYPE_CHECKING: + filled = cast(cpy.FillReturn_ChunkCombinedCode, filled) + return _convert_filled_from_ChunkCombinedCode(filled, fill_type_to) + elif fill_type_from == FillType.ChunkCombinedOffset: + if TYPE_CHECKING: + filled = cast(cpy.FillReturn_ChunkCombinedOffset, filled) + return _convert_filled_from_ChunkCombinedOffset(filled, fill_type_to) + elif fill_type_from == FillType.ChunkCombinedCodeOffset: + if TYPE_CHECKING: + filled = cast(cpy.FillReturn_ChunkCombinedCodeOffset, filled) + return _convert_filled_from_ChunkCombinedCodeOffset(filled, fill_type_to) + elif fill_type_from == FillType.ChunkCombinedOffsetOffset: + if TYPE_CHECKING: + filled = cast(cpy.FillReturn_ChunkCombinedOffsetOffset, filled) + return _convert_filled_from_ChunkCombinedOffsetOffset(filled, fill_type_to) + else: + raise ValueError(f"Invalid FillType {fill_type_from}") + + +def _convert_lines_from_Separate( + lines: cpy.LineReturn_Separate, + line_type_to: LineType, +) -> cpy.LineReturn: + if line_type_to == LineType.Separate: + return lines + elif line_type_to == LineType.SeparateCode: + separate_codes = [arr.codes_from_points(line) for line in lines] + return (lines, separate_codes) + elif line_type_to == LineType.ChunkCombinedCode: + if not lines: + ret1: cpy.LineReturn_ChunkCombinedCode = ([None], [None]) + else: + points = arr.concat_points(lines) + offsets = arr.offsets_from_lengths(lines) + codes = arr.codes_from_offsets_and_points(offsets, points) + ret1 = ([points], [codes]) + return ret1 + elif line_type_to == LineType.ChunkCombinedOffset: + if not lines: + ret2: cpy.LineReturn_ChunkCombinedOffset = ([None], [None]) + else: + ret2 = ([arr.concat_points(lines)], [arr.offsets_from_lengths(lines)]) + return ret2 + elif line_type_to == LineType.ChunkCombinedNan: + if not lines: + ret3: cpy.LineReturn_ChunkCombinedNan = ([None],) + else: + ret3 = ([arr.concat_points_with_nan(lines)],) + return ret3 + else: + raise ValueError(f"Invalid LineType {line_type_to}") + + +def _convert_lines_from_SeparateCode( + lines: cpy.LineReturn_SeparateCode, + line_type_to: LineType, +) -> cpy.LineReturn: + if line_type_to == LineType.Separate: + # Drop codes. + return lines[0] + elif line_type_to == LineType.SeparateCode: + return lines + elif line_type_to == LineType.ChunkCombinedCode: + if not lines[0]: + ret1: cpy.LineReturn_ChunkCombinedCode = ([None], [None]) + else: + ret1 = ([arr.concat_points(lines[0])], [arr.concat_codes(lines[1])]) + return ret1 + elif line_type_to == LineType.ChunkCombinedOffset: + if not lines[0]: + ret2: cpy.LineReturn_ChunkCombinedOffset = ([None], [None]) + else: + ret2 = ([arr.concat_points(lines[0])], [arr.offsets_from_lengths(lines[0])]) + return ret2 + elif line_type_to == LineType.ChunkCombinedNan: + if not lines[0]: + ret3: cpy.LineReturn_ChunkCombinedNan = ([None],) + else: + ret3 = ([arr.concat_points_with_nan(lines[0])],) + return ret3 + else: + raise ValueError(f"Invalid LineType {line_type_to}") + + +def _convert_lines_from_ChunkCombinedCode( + lines: cpy.LineReturn_ChunkCombinedCode, + line_type_to: LineType, +) -> cpy.LineReturn: + if line_type_to in (LineType.Separate, LineType.SeparateCode): + separate_lines = [] + for points, codes in zip(*lines): + if points is not None: + if TYPE_CHECKING: + assert codes is not None + split_at = np.nonzero(codes == MOVETO)[0] + if len(split_at) > 1: + separate_lines += np.split(points, split_at[1:]) + else: + separate_lines.append(points) + if line_type_to == LineType.Separate: + return separate_lines + else: + separate_codes = [arr.codes_from_points(line) for line in separate_lines] + return (separate_lines, separate_codes) + elif line_type_to == LineType.ChunkCombinedCode: + return lines + elif line_type_to == LineType.ChunkCombinedOffset: + chunk_offsets = [None if codes is None else arr.offsets_from_codes(codes) + for codes in lines[1]] + return (lines[0], chunk_offsets) + elif line_type_to == LineType.ChunkCombinedNan: + points_nan: list[cpy.PointArray | None] = [] + for points, codes in zip(*lines): + if points is None: + points_nan.append(None) + else: + if TYPE_CHECKING: + assert codes is not None + offsets = arr.offsets_from_codes(codes) + points_nan.append(arr.insert_nan_at_offsets(points, offsets)) + return (points_nan,) + else: + raise ValueError(f"Invalid LineType {line_type_to}") + + +def _convert_lines_from_ChunkCombinedOffset( + lines: cpy.LineReturn_ChunkCombinedOffset, + line_type_to: LineType, +) -> cpy.LineReturn: + if line_type_to in (LineType.Separate, LineType.SeparateCode): + separate_lines = [] + for points, offsets in zip(*lines): + if points is not None: + if TYPE_CHECKING: + assert offsets is not None + separate_lines += arr.split_points_by_offsets(points, offsets) + if line_type_to == LineType.Separate: + return separate_lines + else: + separate_codes = [arr.codes_from_points(line) for line in separate_lines] + return (separate_lines, separate_codes) + elif line_type_to == LineType.ChunkCombinedCode: + chunk_codes: list[cpy.CodeArray | None] = [] + for points, offsets in zip(*lines): + if points is None: + chunk_codes.append(None) + else: + if TYPE_CHECKING: + assert offsets is not None + chunk_codes.append(arr.codes_from_offsets_and_points(offsets, points)) + return (lines[0], chunk_codes) + elif line_type_to == LineType.ChunkCombinedOffset: + return lines + elif line_type_to == LineType.ChunkCombinedNan: + points_nan: list[cpy.PointArray | None] = [] + for points, offsets in zip(*lines): + if points is None: + points_nan.append(None) + else: + if TYPE_CHECKING: + assert offsets is not None + points_nan.append(arr.insert_nan_at_offsets(points, offsets)) + return (points_nan,) + else: + raise ValueError(f"Invalid LineType {line_type_to}") + + +def _convert_lines_from_ChunkCombinedNan( + lines: cpy.LineReturn_ChunkCombinedNan, + line_type_to: LineType, +) -> cpy.LineReturn: + if line_type_to in (LineType.Separate, LineType.SeparateCode): + separate_lines = [] + for points in lines[0]: + if points is not None: + separate_lines += arr.split_points_at_nan(points) + if line_type_to == LineType.Separate: + return separate_lines + else: + separate_codes = [arr.codes_from_points(points) for points in separate_lines] + return (separate_lines, separate_codes) + elif line_type_to == LineType.ChunkCombinedCode: + chunk_points: list[cpy.PointArray | None] = [] + chunk_codes: list[cpy.CodeArray | None] = [] + for points in lines[0]: + if points is None: + chunk_points.append(None) + chunk_codes.append(None) + else: + points, offsets = arr.remove_nan(points) + chunk_points.append(points) + chunk_codes.append(arr.codes_from_offsets_and_points(offsets, points)) + return (chunk_points, chunk_codes) + elif line_type_to == LineType.ChunkCombinedOffset: + chunk_points = [] + chunk_offsets: list[cpy.OffsetArray | None] = [] + for points in lines[0]: + if points is None: + chunk_points.append(None) + chunk_offsets.append(None) + else: + points, offsets = arr.remove_nan(points) + chunk_points.append(points) + chunk_offsets.append(offsets) + return (chunk_points, chunk_offsets) + elif line_type_to == LineType.ChunkCombinedNan: + return lines + else: + raise ValueError(f"Invalid LineType {line_type_to}") + + +def convert_lines( + lines: cpy.LineReturn, + line_type_from: LineType | str, + line_type_to: LineType | str, +) -> cpy.LineReturn: + """Convert contour lines from one :class:`~.LineType` to another. + + Args: + lines (sequence of arrays): Contour lines to convert, such as those returned by + :meth:`.ContourGenerator.lines`. + line_type_from (LineType or str): :class:`~.LineType` to convert from as enum or + string equivalent. + line_type_to (LineType or str): :class:`~.LineType` to convert to as enum or string + equivalent. + + Return: + Converted contour lines. + + When converting non-chunked line types (``LineType.Separate`` or ``LineType.SeparateCode``) to + chunked ones (``LineType.ChunkCombinedCode``, ``LineType.ChunkCombinedOffset`` or + ``LineType.ChunkCombinedNan``), all lines are placed in the first chunk. When converting in the + other direction, all chunk information is discarded. + + .. versionadded:: 1.2.0 + """ + line_type_from = as_line_type(line_type_from) + line_type_to = as_line_type(line_type_to) + + check_lines(lines, line_type_from) + + if line_type_from == LineType.Separate: + if TYPE_CHECKING: + lines = cast(cpy.LineReturn_Separate, lines) + return _convert_lines_from_Separate(lines, line_type_to) + elif line_type_from == LineType.SeparateCode: + if TYPE_CHECKING: + lines = cast(cpy.LineReturn_SeparateCode, lines) + return _convert_lines_from_SeparateCode(lines, line_type_to) + elif line_type_from == LineType.ChunkCombinedCode: + if TYPE_CHECKING: + lines = cast(cpy.LineReturn_ChunkCombinedCode, lines) + return _convert_lines_from_ChunkCombinedCode(lines, line_type_to) + elif line_type_from == LineType.ChunkCombinedOffset: + if TYPE_CHECKING: + lines = cast(cpy.LineReturn_ChunkCombinedOffset, lines) + return _convert_lines_from_ChunkCombinedOffset(lines, line_type_to) + elif line_type_from == LineType.ChunkCombinedNan: + if TYPE_CHECKING: + lines = cast(cpy.LineReturn_ChunkCombinedNan, lines) + return _convert_lines_from_ChunkCombinedNan(lines, line_type_to) + else: + raise ValueError(f"Invalid LineType {line_type_from}") + + +def convert_multi_filled( + multi_filled: list[cpy.FillReturn], + fill_type_from: FillType | str, + fill_type_to: FillType | str, +) -> list[cpy.FillReturn]: + """Convert multiple sets of filled contours from one :class:`~.FillType` to another. + + Args: + multi_filled (nested sequence of arrays): Filled contour polygons to convert, such as those + returned by :meth:`.ContourGenerator.multi_filled`. + fill_type_from (FillType or str): :class:`~.FillType` to convert from as enum or + string equivalent. + fill_type_to (FillType or str): :class:`~.FillType` to convert to as enum or string + equivalent. + + Return: + Converted sets filled contour polygons. + + When converting non-chunked fill types (``FillType.OuterCode`` or ``FillType.OuterOffset``) to + chunked ones, all polygons are placed in the first chunk. When converting in the other + direction, all chunk information is discarded. Converting a fill type that is not aware of the + relationship between outer boundaries and contained holes (``FillType.ChunkCombinedCode`` or + ``FillType.ChunkCombinedOffset``) to one that is will raise a ``ValueError``. + + .. versionadded:: 1.3.0 + """ + fill_type_from = as_fill_type(fill_type_from) + fill_type_to = as_fill_type(fill_type_to) + + return [convert_filled(filled, fill_type_from, fill_type_to) for filled in multi_filled] + + +def convert_multi_lines( + multi_lines: list[cpy.LineReturn], + line_type_from: LineType | str, + line_type_to: LineType | str, +) -> list[cpy.LineReturn]: + """Convert multiple sets of contour lines from one :class:`~.LineType` to another. + + Args: + multi_lines (nested sequence of arrays): Contour lines to convert, such as those returned by + :meth:`.ContourGenerator.multi_lines`. + line_type_from (LineType or str): :class:`~.LineType` to convert from as enum or + string equivalent. + line_type_to (LineType or str): :class:`~.LineType` to convert to as enum or string + equivalent. + + Return: + Converted set of contour lines. + + When converting non-chunked line types (``LineType.Separate`` or ``LineType.SeparateCode``) to + chunked ones (``LineType.ChunkCombinedCode``, ``LineType.ChunkCombinedOffset`` or + ``LineType.ChunkCombinedNan``), all lines are placed in the first chunk. When converting in the + other direction, all chunk information is discarded. + + .. versionadded:: 1.3.0 + """ + line_type_from = as_line_type(line_type_from) + line_type_to = as_line_type(line_type_to) + + return [convert_lines(lines, line_type_from, line_type_to) for lines in multi_lines] diff --git a/lib/python3.12/site-packages/contourpy/dechunk.py b/lib/python3.12/site-packages/contourpy/dechunk.py new file mode 100644 index 0000000000000000000000000000000000000000..f571b4b5ffbb4801cd687b065314b51a93a1997d --- /dev/null +++ b/lib/python3.12/site-packages/contourpy/dechunk.py @@ -0,0 +1,207 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, cast + +from contourpy._contourpy import FillType, LineType +from contourpy.array import ( + concat_codes_or_none, + concat_offsets_or_none, + concat_points_or_none, + concat_points_or_none_with_nan, +) +from contourpy.enum_util import as_fill_type, as_line_type +from contourpy.typecheck import check_filled, check_lines + +if TYPE_CHECKING: + import contourpy._contourpy as cpy + + +def dechunk_filled(filled: cpy.FillReturn, fill_type: FillType | str) -> cpy.FillReturn: + """Return the specified filled contours with chunked data moved into the first chunk. + + Filled contours that are not chunked (``FillType.OuterCode`` and ``FillType.OuterOffset``) and + those that are but only contain a single chunk are returned unmodified. Individual polygons are + unchanged, they are not geometrically combined. + + Args: + filled (sequence of arrays): Filled contour data, such as returned by + :meth:`.ContourGenerator.filled`. + fill_type (FillType or str): Type of :meth:`~.ContourGenerator.filled` as enum or string + equivalent. + + Return: + Filled contours in a single chunk. + + .. versionadded:: 1.2.0 + """ + fill_type = as_fill_type(fill_type) + + if fill_type in (FillType.OuterCode, FillType.OuterOffset): + # No-op if fill_type is not chunked. + return filled + + check_filled(filled, fill_type) + if len(filled[0]) < 2: + # No-op if just one chunk. + return filled + + if TYPE_CHECKING: + filled = cast(cpy.FillReturn_Chunk, filled) + points = concat_points_or_none(filled[0]) + + if fill_type == FillType.ChunkCombinedCode: + if TYPE_CHECKING: + filled = cast(cpy.FillReturn_ChunkCombinedCode, filled) + if points is None: + ret1: cpy.FillReturn_ChunkCombinedCode = ([None], [None]) + else: + ret1 = ([points], [concat_codes_or_none(filled[1])]) + return ret1 + elif fill_type == FillType.ChunkCombinedOffset: + if TYPE_CHECKING: + filled = cast(cpy.FillReturn_ChunkCombinedOffset, filled) + if points is None: + ret2: cpy.FillReturn_ChunkCombinedOffset = ([None], [None]) + else: + ret2 = ([points], [concat_offsets_or_none(filled[1])]) + return ret2 + elif fill_type == FillType.ChunkCombinedCodeOffset: + if TYPE_CHECKING: + filled = cast(cpy.FillReturn_ChunkCombinedCodeOffset, filled) + if points is None: + ret3: cpy.FillReturn_ChunkCombinedCodeOffset = ([None], [None], [None]) + else: + outer_offsets = concat_offsets_or_none(filled[2]) + ret3 = ([points], [concat_codes_or_none(filled[1])], [outer_offsets]) + return ret3 + elif fill_type == FillType.ChunkCombinedOffsetOffset: + if TYPE_CHECKING: + filled = cast(cpy.FillReturn_ChunkCombinedOffsetOffset, filled) + if points is None: + ret4: cpy.FillReturn_ChunkCombinedOffsetOffset = ([None], [None], [None]) + else: + outer_offsets = concat_offsets_or_none(filled[2]) + ret4 = ([points], [concat_offsets_or_none(filled[1])], [outer_offsets]) + return ret4 + else: + raise ValueError(f"Invalid FillType {fill_type}") + + +def dechunk_lines(lines: cpy.LineReturn, line_type: LineType | str) -> cpy.LineReturn: + """Return the specified contour lines with chunked data moved into the first chunk. + + Contour lines that are not chunked (``LineType.Separate`` and ``LineType.SeparateCode``) and + those that are but only contain a single chunk are returned unmodified. Individual lines are + unchanged, they are not geometrically combined. + + Args: + lines (sequence of arrays): Contour line data, such as returned by + :meth:`.ContourGenerator.lines`. + line_type (LineType or str): Type of :meth:`~.ContourGenerator.lines` as enum or string + equivalent. + + Return: + Contour lines in a single chunk. + + .. versionadded:: 1.2.0 + """ + line_type = as_line_type(line_type) + + if line_type in (LineType.Separate, LineType.SeparateCode): + # No-op if line_type is not chunked. + return lines + + check_lines(lines, line_type) + if len(lines[0]) < 2: + # No-op if just one chunk. + return lines + + if TYPE_CHECKING: + lines = cast(cpy.LineReturn_Chunk, lines) + + if line_type == LineType.ChunkCombinedCode: + if TYPE_CHECKING: + lines = cast(cpy.LineReturn_ChunkCombinedCode, lines) + points = concat_points_or_none(lines[0]) + if points is None: + ret1: cpy.LineReturn_ChunkCombinedCode = ([None], [None]) + else: + ret1 = ([points], [concat_codes_or_none(lines[1])]) + return ret1 + elif line_type == LineType.ChunkCombinedOffset: + if TYPE_CHECKING: + lines = cast(cpy.LineReturn_ChunkCombinedOffset, lines) + points = concat_points_or_none(lines[0]) + if points is None: + ret2: cpy.LineReturn_ChunkCombinedOffset = ([None], [None]) + else: + ret2 = ([points], [concat_offsets_or_none(lines[1])]) + return ret2 + elif line_type == LineType.ChunkCombinedNan: + if TYPE_CHECKING: + lines = cast(cpy.LineReturn_ChunkCombinedNan, lines) + points = concat_points_or_none_with_nan(lines[0]) + ret3: cpy.LineReturn_ChunkCombinedNan = ([points],) + return ret3 + else: + raise ValueError(f"Invalid LineType {line_type}") + + +def dechunk_multi_filled( + multi_filled: list[cpy.FillReturn], + fill_type: FillType | str, +) -> list[cpy.FillReturn]: + """Return multiple sets of filled contours with chunked data moved into the first chunks. + + Filled contours that are not chunked (``FillType.OuterCode`` and ``FillType.OuterOffset``) and + those that are but only contain a single chunk are returned unmodified. Individual polygons are + unchanged, they are not geometrically combined. + + Args: + multi_filled (nested sequence of arrays): Filled contour data, such as returned by + :meth:`.ContourGenerator.multi_filled`. + fill_type (FillType or str): Type of :meth:`~.ContourGenerator.filled` as enum or string + equivalent. + + Return: + Multiple sets of filled contours in a single chunk. + + .. versionadded:: 1.3.0 + """ + fill_type = as_fill_type(fill_type) + + if fill_type in (FillType.OuterCode, FillType.OuterOffset): + # No-op if fill_type is not chunked. + return multi_filled + + return [dechunk_filled(filled, fill_type) for filled in multi_filled] + + +def dechunk_multi_lines( + multi_lines: list[cpy.LineReturn], + line_type: LineType | str, +) -> list[cpy.LineReturn]: + """Return multiple sets of contour lines with all chunked data moved into the first chunks. + + Contour lines that are not chunked (``LineType.Separate`` and ``LineType.SeparateCode``) and + those that are but only contain a single chunk are returned unmodified. Individual lines are + unchanged, they are not geometrically combined. + + Args: + multi_lines (nested sequence of arrays): Contour line data, such as returned by + :meth:`.ContourGenerator.multi_lines`. + line_type (LineType or str): Type of :meth:`~.ContourGenerator.lines` as enum or string + equivalent. + + Return: + Multiple sets of contour lines in a single chunk. + + .. versionadded:: 1.3.0 + """ + line_type = as_line_type(line_type) + + if line_type in (LineType.Separate, LineType.SeparateCode): + # No-op if line_type is not chunked. + return multi_lines + + return [dechunk_lines(lines, line_type) for lines in multi_lines] diff --git a/lib/python3.12/site-packages/contourpy/enum_util.py b/lib/python3.12/site-packages/contourpy/enum_util.py new file mode 100644 index 0000000000000000000000000000000000000000..14229abebf7e342a2834ffec2a0ad427a727fa5c --- /dev/null +++ b/lib/python3.12/site-packages/contourpy/enum_util.py @@ -0,0 +1,57 @@ +from __future__ import annotations + +from contourpy._contourpy import FillType, LineType, ZInterp + + +def as_fill_type(fill_type: FillType | str) -> FillType: + """Coerce a FillType or string value to a FillType. + + Args: + fill_type (FillType or str): Value to convert. + + Return: + FillType: Converted value. + """ + if isinstance(fill_type, str): + try: + return FillType.__members__[fill_type] + except KeyError as e: + raise ValueError(f"'{fill_type}' is not a valid FillType") from e + else: + return fill_type + + +def as_line_type(line_type: LineType | str) -> LineType: + """Coerce a LineType or string value to a LineType. + + Args: + line_type (LineType or str): Value to convert. + + Return: + LineType: Converted value. + """ + if isinstance(line_type, str): + try: + return LineType.__members__[line_type] + except KeyError as e: + raise ValueError(f"'{line_type}' is not a valid LineType") from e + else: + return line_type + + +def as_z_interp(z_interp: ZInterp | str) -> ZInterp: + """Coerce a ZInterp or string value to a ZInterp. + + Args: + z_interp (ZInterp or str): Value to convert. + + Return: + ZInterp: Converted value. + """ + if isinstance(z_interp, str): + try: + return ZInterp.__members__[z_interp] + except KeyError as e: + raise ValueError(f"'{z_interp}' is not a valid ZInterp") from e + else: + return z_interp diff --git a/lib/python3.12/site-packages/contourpy/py.typed b/lib/python3.12/site-packages/contourpy/py.typed new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/lib/python3.12/site-packages/contourpy/typecheck.py b/lib/python3.12/site-packages/contourpy/typecheck.py new file mode 100644 index 0000000000000000000000000000000000000000..23fbd54856a96f6b6b60b8557c76936f615a9cdc --- /dev/null +++ b/lib/python3.12/site-packages/contourpy/typecheck.py @@ -0,0 +1,203 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, cast + +import numpy as np + +from contourpy import FillType, LineType +from contourpy.enum_util import as_fill_type, as_line_type +from contourpy.types import MOVETO, code_dtype, offset_dtype, point_dtype + +if TYPE_CHECKING: + import contourpy._contourpy as cpy + + +# Minimalist array-checking functions that check dtype, ndims and shape only. +# They do not walk the arrays to check the contents for performance reasons. +def check_code_array(codes: Any) -> None: + if not isinstance(codes, np.ndarray): + raise TypeError(f"Expected numpy array not {type(codes)}") + if codes.dtype != code_dtype: + raise ValueError(f"Expected numpy array of dtype {code_dtype} not {codes.dtype}") + if not (codes.ndim == 1 and len(codes) > 1): + raise ValueError(f"Expected numpy array of shape (?,) not {codes.shape}") + if codes[0] != MOVETO: + raise ValueError(f"First element of code array must be {MOVETO}, not {codes[0]}") + + +def check_offset_array(offsets: Any) -> None: + if not isinstance(offsets, np.ndarray): + raise TypeError(f"Expected numpy array not {type(offsets)}") + if offsets.dtype != offset_dtype: + raise ValueError(f"Expected numpy array of dtype {offset_dtype} not {offsets.dtype}") + if not (offsets.ndim == 1 and len(offsets) > 1): + raise ValueError(f"Expected numpy array of shape (?,) not {offsets.shape}") + if offsets[0] != 0: + raise ValueError(f"First element of offset array must be 0, not {offsets[0]}") + + +def check_point_array(points: Any) -> None: + if not isinstance(points, np.ndarray): + raise TypeError(f"Expected numpy array not {type(points)}") + if points.dtype != point_dtype: + raise ValueError(f"Expected numpy array of dtype {point_dtype} not {points.dtype}") + if not (points.ndim == 2 and points.shape[1] ==2 and points.shape[0] > 1): + raise ValueError(f"Expected numpy array of shape (?, 2) not {points.shape}") + + +def _check_tuple_of_lists_with_same_length( + maybe_tuple: Any, + tuple_length: int, + allow_empty_lists: bool = True, +) -> None: + if not isinstance(maybe_tuple, tuple): + raise TypeError(f"Expected tuple not {type(maybe_tuple)}") + if len(maybe_tuple) != tuple_length: + raise ValueError(f"Expected tuple of length {tuple_length} not {len(maybe_tuple)}") + for maybe_list in maybe_tuple: + if not isinstance(maybe_list, list): + msg = f"Expected tuple to contain {tuple_length} lists but found a {type(maybe_list)}" + raise TypeError(msg) + lengths = [len(item) for item in maybe_tuple] + if len(set(lengths)) != 1: + msg = f"Expected {tuple_length} lists with same length but lengths are {lengths}" + raise ValueError(msg) + if not allow_empty_lists and lengths[0] == 0: + raise ValueError(f"Expected {tuple_length} non-empty lists") + + +def check_filled(filled: cpy.FillReturn, fill_type: FillType | str) -> None: + fill_type = as_fill_type(fill_type) + + if fill_type == FillType.OuterCode: + if TYPE_CHECKING: + filled = cast(cpy.FillReturn_OuterCode, filled) + _check_tuple_of_lists_with_same_length(filled, 2) + for i, (points, codes) in enumerate(zip(*filled)): + check_point_array(points) + check_code_array(codes) + if len(points) != len(codes): + raise ValueError(f"Points and codes have different lengths in polygon {i}") + elif fill_type == FillType.OuterOffset: + if TYPE_CHECKING: + filled = cast(cpy.FillReturn_OuterOffset, filled) + _check_tuple_of_lists_with_same_length(filled, 2) + for i, (points, offsets) in enumerate(zip(*filled)): + check_point_array(points) + check_offset_array(offsets) + if offsets[-1] != len(points): + raise ValueError(f"Inconsistent points and offsets in polygon {i}") + elif fill_type == FillType.ChunkCombinedCode: + if TYPE_CHECKING: + filled = cast(cpy.FillReturn_ChunkCombinedCode, filled) + _check_tuple_of_lists_with_same_length(filled, 2, allow_empty_lists=False) + for chunk, (points_or_none, codes_or_none) in enumerate(zip(*filled)): + if points_or_none is not None and codes_or_none is not None: + check_point_array(points_or_none) + check_code_array(codes_or_none) + if len(points_or_none) != len(codes_or_none): + raise ValueError(f"Points and codes have different lengths in chunk {chunk}") + elif not (points_or_none is None and codes_or_none is None): + raise ValueError(f"Inconsistent Nones in chunk {chunk}") + elif fill_type == FillType.ChunkCombinedOffset: + if TYPE_CHECKING: + filled = cast(cpy.FillReturn_ChunkCombinedOffset, filled) + _check_tuple_of_lists_with_same_length(filled, 2, allow_empty_lists=False) + for chunk, (points_or_none, offsets_or_none) in enumerate(zip(*filled)): + if points_or_none is not None and offsets_or_none is not None: + check_point_array(points_or_none) + check_offset_array(offsets_or_none) + if offsets_or_none[-1] != len(points_or_none): + raise ValueError(f"Inconsistent points and offsets in chunk {chunk}") + elif not (points_or_none is None and offsets_or_none is None): + raise ValueError(f"Inconsistent Nones in chunk {chunk}") + elif fill_type == FillType.ChunkCombinedCodeOffset: + if TYPE_CHECKING: + filled = cast(cpy.FillReturn_ChunkCombinedCodeOffset, filled) + _check_tuple_of_lists_with_same_length(filled, 3, allow_empty_lists=False) + for i, (points_or_none, codes_or_none, outer_offsets_or_none) in enumerate(zip(*filled)): + if (points_or_none is not None and codes_or_none is not None and + outer_offsets_or_none is not None): + check_point_array(points_or_none) + check_code_array(codes_or_none) + check_offset_array(outer_offsets_or_none) + if len(codes_or_none) != len(points_or_none): + raise ValueError(f"Points and codes have different lengths in chunk {i}") + if outer_offsets_or_none[-1] != len(codes_or_none): + raise ValueError(f"Inconsistent codes and outer_offsets in chunk {i}") + elif not (points_or_none is None and codes_or_none is None and + outer_offsets_or_none is None): + raise ValueError(f"Inconsistent Nones in chunk {i}") + elif fill_type == FillType.ChunkCombinedOffsetOffset: + if TYPE_CHECKING: + filled = cast(cpy.FillReturn_ChunkCombinedOffsetOffset, filled) + _check_tuple_of_lists_with_same_length(filled, 3, allow_empty_lists=False) + for i, (points_or_none, offsets_or_none, outer_offsets_or_none) in enumerate(zip(*filled)): + if (points_or_none is not None and offsets_or_none is not None and + outer_offsets_or_none is not None): + check_point_array(points_or_none) + check_offset_array(offsets_or_none) + check_offset_array(outer_offsets_or_none) + if offsets_or_none[-1] != len(points_or_none): + raise ValueError(f"Inconsistent points and offsets in chunk {i}") + if outer_offsets_or_none[-1] != len(offsets_or_none) - 1: + raise ValueError(f"Inconsistent offsets and outer_offsets in chunk {i}") + elif not (points_or_none is None and offsets_or_none is None and + outer_offsets_or_none is None): + raise ValueError(f"Inconsistent Nones in chunk {i}") + else: + raise ValueError(f"Invalid FillType {fill_type}") + + +def check_lines(lines: cpy.LineReturn, line_type: LineType | str) -> None: + line_type = as_line_type(line_type) + + if line_type == LineType.Separate: + if TYPE_CHECKING: + lines = cast(cpy.LineReturn_Separate, lines) + if not isinstance(lines, list): + raise TypeError(f"Expected list not {type(lines)}") + for points in lines: + check_point_array(points) + elif line_type == LineType.SeparateCode: + if TYPE_CHECKING: + lines = cast(cpy.LineReturn_SeparateCode, lines) + _check_tuple_of_lists_with_same_length(lines, 2) + for i, (points, codes) in enumerate(zip(*lines)): + check_point_array(points) + check_code_array(codes) + if len(points) != len(codes): + raise ValueError(f"Points and codes have different lengths in line {i}") + elif line_type == LineType.ChunkCombinedCode: + if TYPE_CHECKING: + lines = cast(cpy.LineReturn_ChunkCombinedCode, lines) + _check_tuple_of_lists_with_same_length(lines, 2, allow_empty_lists=False) + for chunk, (points_or_none, codes_or_none) in enumerate(zip(*lines)): + if points_or_none is not None and codes_or_none is not None: + check_point_array(points_or_none) + check_code_array(codes_or_none) + if len(points_or_none) != len(codes_or_none): + raise ValueError(f"Points and codes have different lengths in chunk {chunk}") + elif not (points_or_none is None and codes_or_none is None): + raise ValueError(f"Inconsistent Nones in chunk {chunk}") + elif line_type == LineType.ChunkCombinedOffset: + if TYPE_CHECKING: + lines = cast(cpy.LineReturn_ChunkCombinedOffset, lines) + _check_tuple_of_lists_with_same_length(lines, 2, allow_empty_lists=False) + for chunk, (points_or_none, offsets_or_none) in enumerate(zip(*lines)): + if points_or_none is not None and offsets_or_none is not None: + check_point_array(points_or_none) + check_offset_array(offsets_or_none) + if offsets_or_none[-1] != len(points_or_none): + raise ValueError(f"Inconsistent points and offsets in chunk {chunk}") + elif not (points_or_none is None and offsets_or_none is None): + raise ValueError(f"Inconsistent Nones in chunk {chunk}") + elif line_type == LineType.ChunkCombinedNan: + if TYPE_CHECKING: + lines = cast(cpy.LineReturn_ChunkCombinedNan, lines) + _check_tuple_of_lists_with_same_length(lines, 1, allow_empty_lists=False) + for _chunk, points_or_none in enumerate(lines[0]): + if points_or_none is not None: + check_point_array(points_or_none) + else: + raise ValueError(f"Invalid LineType {line_type}") diff --git a/lib/python3.12/site-packages/contourpy/types.py b/lib/python3.12/site-packages/contourpy/types.py new file mode 100644 index 0000000000000000000000000000000000000000..e704b98eac0736d83586c66803db6b79cdfc5967 --- /dev/null +++ b/lib/python3.12/site-packages/contourpy/types.py @@ -0,0 +1,13 @@ +from __future__ import annotations + +import numpy as np + +# dtypes of arrays returned by ContourPy. +point_dtype = np.float64 +code_dtype = np.uint8 +offset_dtype = np.uint32 + +# Kind codes used in Matplotlib Paths. +MOVETO = 1 +LINETO = 2 +CLOSEPOLY = 79 diff --git a/lib/python3.12/site-packages/contourpy/util/__init__.py b/lib/python3.12/site-packages/contourpy/util/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..fe33fcef1e18d2a4b92287e434cf6b1257e4274f --- /dev/null +++ b/lib/python3.12/site-packages/contourpy/util/__init__.py @@ -0,0 +1,5 @@ +from __future__ import annotations + +from contourpy.util._build_config import build_config + +__all__ = ["build_config"] diff --git a/lib/python3.12/site-packages/contourpy/util/__pycache__/__init__.cpython-312.pyc b/lib/python3.12/site-packages/contourpy/util/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8284ba9ae2d9641aa9f0007b3267cd67b3759fbd Binary files /dev/null and b/lib/python3.12/site-packages/contourpy/util/__pycache__/__init__.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/contourpy/util/__pycache__/_build_config.cpython-312.pyc b/lib/python3.12/site-packages/contourpy/util/__pycache__/_build_config.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dea0e108aa3d111efbbdc8e6672acafab0118284 Binary files /dev/null and b/lib/python3.12/site-packages/contourpy/util/__pycache__/_build_config.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/contourpy/util/__pycache__/bokeh_renderer.cpython-312.pyc b/lib/python3.12/site-packages/contourpy/util/__pycache__/bokeh_renderer.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d2cfa3035664d02a6ec7b83d67ed865c63d7c83d Binary files /dev/null and b/lib/python3.12/site-packages/contourpy/util/__pycache__/bokeh_renderer.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/contourpy/util/__pycache__/bokeh_util.cpython-312.pyc b/lib/python3.12/site-packages/contourpy/util/__pycache__/bokeh_util.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6549a3121f21608cabba4aeee957e3b9dab87a1a Binary files /dev/null and b/lib/python3.12/site-packages/contourpy/util/__pycache__/bokeh_util.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/contourpy/util/__pycache__/data.cpython-312.pyc b/lib/python3.12/site-packages/contourpy/util/__pycache__/data.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3b5d155b7a2f7834db8d242957f6f983799901a9 Binary files /dev/null and b/lib/python3.12/site-packages/contourpy/util/__pycache__/data.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/contourpy/util/__pycache__/mpl_renderer.cpython-312.pyc b/lib/python3.12/site-packages/contourpy/util/__pycache__/mpl_renderer.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7d61dfecf8dd5b80c16dac2bd5b483101d413908 Binary files /dev/null and b/lib/python3.12/site-packages/contourpy/util/__pycache__/mpl_renderer.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/contourpy/util/__pycache__/mpl_util.cpython-312.pyc b/lib/python3.12/site-packages/contourpy/util/__pycache__/mpl_util.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3ff920a76e8f01e720bb91820c03248bb550c54b Binary files /dev/null and b/lib/python3.12/site-packages/contourpy/util/__pycache__/mpl_util.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/contourpy/util/__pycache__/renderer.cpython-312.pyc b/lib/python3.12/site-packages/contourpy/util/__pycache__/renderer.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..aabc60fdb331c5449d7d2dd50a1bf3da076c033c Binary files /dev/null and b/lib/python3.12/site-packages/contourpy/util/__pycache__/renderer.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/contourpy/util/_build_config.py b/lib/python3.12/site-packages/contourpy/util/_build_config.py new file mode 100644 index 0000000000000000000000000000000000000000..f30211907ae0c53ee28fcec657893fbfc209fe80 --- /dev/null +++ b/lib/python3.12/site-packages/contourpy/util/_build_config.py @@ -0,0 +1,60 @@ +# _build_config.py.in is converted into _build_config.py during the meson build process. + +from __future__ import annotations + + +def build_config() -> dict[str, str]: + """ + Return a dictionary containing build configuration settings. + + All dictionary keys and values are strings, for example ``False`` is + returned as ``"False"``. + + .. versionadded:: 1.1.0 + """ + return dict( + # Python settings + python_version="3.12", + python_install_dir=r"/usr/local/lib/python3.12/site-packages/", + python_path=r"/tmp/build-env-mif39ifw/bin/python", + + # Package versions + contourpy_version="1.3.3", + meson_version="1.8.2", + mesonpy_version="0.18.0", + pybind11_version="3.0.0", + + # Misc meson settings + meson_backend="ninja", + build_dir=r"/project/.mesonpy-skneszlg/lib/contourpy/util", + source_dir=r"/project/lib/contourpy/util", + cross_build="False", + + # Build options + build_options=r"-Dbuildtype=release -Db_ndebug=if-release -Db_vscrt=md -Dvsenv=True --native-file=/project/.mesonpy-skneszlg/meson-python-native-file.ini", + buildtype="release", + cpp_std="c++17", + debug="False", + optimization="3", + vsenv="True", + b_ndebug="if-release", + b_vscrt="from_buildtype", + + # C++ compiler + compiler_name="gcc", + compiler_version="14.2.1", + linker_id="ld.bfd", + compile_command="c++", + + # Host machine + host_cpu="x86_64", + host_cpu_family="x86_64", + host_cpu_endian="little", + host_cpu_system="linux", + + # Build machine, same as host machine if not a cross_build + build_cpu="x86_64", + build_cpu_family="x86_64", + build_cpu_endian="little", + build_cpu_system="linux", + ) diff --git a/lib/python3.12/site-packages/contourpy/util/bokeh_renderer.py b/lib/python3.12/site-packages/contourpy/util/bokeh_renderer.py new file mode 100644 index 0000000000000000000000000000000000000000..f80e93e9e4e2deb8372c8ac29e04a9e86096f4cb --- /dev/null +++ b/lib/python3.12/site-packages/contourpy/util/bokeh_renderer.py @@ -0,0 +1,339 @@ +from __future__ import annotations + +import io +from typing import TYPE_CHECKING, Any + +from bokeh.io import export_png, export_svg, show +from bokeh.io.export import get_screenshot_as_png +from bokeh.layouts import gridplot +from bokeh.models.annotations.labels import Label +from bokeh.palettes import Category10 +from bokeh.plotting import figure +import numpy as np + +from contourpy.enum_util import as_fill_type, as_line_type +from contourpy.util.bokeh_util import filled_to_bokeh, lines_to_bokeh +from contourpy.util.renderer import Renderer + +if TYPE_CHECKING: + from bokeh.core.enums import OutputBackendType + from bokeh.models import GridPlot + from bokeh.palettes import Palette + from numpy.typing import ArrayLike + from selenium.webdriver.remote.webdriver import WebDriver + + from contourpy import FillType, LineType + from contourpy._contourpy import FillReturn, LineReturn + + +class BokehRenderer(Renderer): + """Utility renderer using Bokeh to render a grid of plots over the same (x, y) range. + + Args: + nrows (int, optional): Number of rows of plots, default ``1``. + ncols (int, optional): Number of columns of plots, default ``1``. + figsize (tuple(float, float), optional): Figure size in inches (assuming 100 dpi), default + ``(9, 9)``. + show_frame (bool, optional): Whether to show frame and axes ticks, default ``True``. + want_svg (bool, optional): Whether output is required in SVG format or not, default + ``False``. + + Warning: + :class:`~.BokehRenderer`, unlike :class:`~.MplRenderer`, needs to be told in advance if + output to SVG format will be required later, otherwise it will assume PNG output. + """ + _figures: list[figure] + _layout: GridPlot + _palette: Palette + _want_svg: bool + + def __init__( + self, + nrows: int = 1, + ncols: int = 1, + figsize: tuple[float, float] = (9, 9), + show_frame: bool = True, + want_svg: bool = False, + ) -> None: + self._want_svg = want_svg + self._palette = Category10[10] + + total_size = 100*np.asarray(figsize, dtype=int) # Assuming 100 dpi. + + nfigures = nrows*ncols + self._figures = [] + backend: OutputBackendType = "svg" if self._want_svg else "canvas" + for _ in range(nfigures): + fig = figure(output_backend=backend) + fig.xgrid.visible = False + fig.ygrid.visible = False + self._figures.append(fig) + if not show_frame: + fig.outline_line_color = None + fig.axis.visible = False + + self._layout = gridplot( + self._figures, ncols=ncols, toolbar_location=None, # type: ignore[arg-type] + width=total_size[0] // ncols, height=total_size[1] // nrows) + + def _convert_color(self, color: str) -> str: + if isinstance(color, str) and color[0] == "C": + index = int(color[1:]) + color = self._palette[index] + return color + + def _get_figure(self, ax: figure | int) -> figure: + if isinstance(ax, int): + ax = self._figures[ax] + return ax + + def filled( + self, + filled: FillReturn, + fill_type: FillType | str, + ax: figure | int = 0, + color: str = "C0", + alpha: float = 0.7, + ) -> None: + """Plot filled contours on a single plot. + + Args: + filled (sequence of arrays): Filled contour data as returned by + :meth:`~.ContourGenerator.filled`. + fill_type (FillType or str): Type of :meth:`~.ContourGenerator.filled` data as returned + by :attr:`~.ContourGenerator.fill_type`, or a string equivalent. + ax (int or Bokeh Figure, optional): Which plot to use, default ``0``. + color (str, optional): Color to plot with. May be a string color or the letter ``"C"`` + followed by an integer in the range ``"C0"`` to ``"C9"`` to use a color from the + ``Category10`` palette. Default ``"C0"``. + alpha (float, optional): Opacity to plot with, default ``0.7``. + """ + fill_type = as_fill_type(fill_type) + fig = self._get_figure(ax) + color = self._convert_color(color) + xs, ys = filled_to_bokeh(filled, fill_type) + if len(xs) > 0: + fig.multi_polygons(xs=[xs], ys=[ys], color=color, fill_alpha=alpha, line_width=0) # type: ignore[arg-type] + + def grid( + self, + x: ArrayLike, + y: ArrayLike, + ax: figure | int = 0, + color: str = "black", + alpha: float = 0.1, + point_color: str | None = None, + quad_as_tri_alpha: float = 0, + ) -> None: + """Plot quad grid lines on a single plot. + + Args: + x (array-like of shape (ny, nx) or (nx,)): The x-coordinates of the grid points. + y (array-like of shape (ny, nx) or (ny,)): The y-coordinates of the grid points. + ax (int or Bokeh Figure, optional): Which plot to use, default ``0``. + color (str, optional): Color to plot grid lines, default ``"black"``. + alpha (float, optional): Opacity to plot lines with, default ``0.1``. + point_color (str, optional): Color to plot grid points or ``None`` if grid points + should not be plotted, default ``None``. + quad_as_tri_alpha (float, optional): Opacity to plot ``quad_as_tri`` grid, default + ``0``. + + Colors may be a string color or the letter ``"C"`` followed by an integer in the range + ``"C0"`` to ``"C9"`` to use a color from the ``Category10`` palette. + + Warning: + ``quad_as_tri_alpha > 0`` plots all quads as though they are unmasked. + """ + fig = self._get_figure(ax) + x, y = self._grid_as_2d(x, y) + xs = list(x) + list(x.T) + ys = list(y) + list(y.T) + kwargs = {"line_color": color, "alpha": alpha} + fig.multi_line(xs, ys, **kwargs) + if quad_as_tri_alpha > 0: + # Assumes no quad mask. + xmid = (0.25*(x[:-1, :-1] + x[1:, :-1] + x[:-1, 1:] + x[1:, 1:])).ravel() + ymid = (0.25*(y[:-1, :-1] + y[1:, :-1] + y[:-1, 1:] + y[1:, 1:])).ravel() + fig.multi_line( + list(np.stack((x[:-1, :-1].ravel(), xmid, x[1:, 1:].ravel()), axis=1)), + list(np.stack((y[:-1, :-1].ravel(), ymid, y[1:, 1:].ravel()), axis=1)), + **kwargs) + fig.multi_line( + list(np.stack((x[:-1, 1:].ravel(), xmid, x[1:, :-1].ravel()), axis=1)), + list(np.stack((y[:-1, 1:].ravel(), ymid, y[1:, :-1].ravel()), axis=1)), + **kwargs) + if point_color is not None: + fig.scatter( + x=x.ravel(), y=y.ravel(), fill_color=color, line_color=None, alpha=alpha, + marker="circle", size=8) + + def lines( + self, + lines: LineReturn, + line_type: LineType | str, + ax: figure | int = 0, + color: str = "C0", + alpha: float = 1.0, + linewidth: float = 1, + ) -> None: + """Plot contour lines on a single plot. + + Args: + lines (sequence of arrays): Contour line data as returned by + :meth:`~.ContourGenerator.lines`. + line_type (LineType or str): Type of :meth:`~.ContourGenerator.lines` data as returned + by :attr:`~.ContourGenerator.line_type`, or a string equivalent. + ax (int or Bokeh Figure, optional): Which plot to use, default ``0``. + color (str, optional): Color to plot lines. May be a string color or the letter ``"C"`` + followed by an integer in the range ``"C0"`` to ``"C9"`` to use a color from the + ``Category10`` palette. Default ``"C0"``. + alpha (float, optional): Opacity to plot lines with, default ``1.0``. + linewidth (float, optional): Width of lines, default ``1``. + + Note: + Assumes all lines are open line strips not closed line loops. + """ + line_type = as_line_type(line_type) + fig = self._get_figure(ax) + color = self._convert_color(color) + xs, ys = lines_to_bokeh(lines, line_type) + if xs is not None: + assert ys is not None + fig.line(xs, ys, line_color=color, line_alpha=alpha, line_width=linewidth) + + def mask( + self, + x: ArrayLike, + y: ArrayLike, + z: ArrayLike | np.ma.MaskedArray[Any, Any], + ax: figure | int = 0, + color: str = "black", + ) -> None: + """Plot masked out grid points as circles on a single plot. + + Args: + x (array-like of shape (ny, nx) or (nx,)): The x-coordinates of the grid points. + y (array-like of shape (ny, nx) or (ny,)): The y-coordinates of the grid points. + z (masked array of shape (ny, nx): z-values. + ax (int or Bokeh Figure, optional): Which plot to use, default ``0``. + color (str, optional): Circle color, default ``"black"``. + """ + mask = np.ma.getmask(z) + if mask is np.ma.nomask: + return + fig = self._get_figure(ax) + color = self._convert_color(color) + x, y = self._grid_as_2d(x, y) + fig.scatter(x[mask], y[mask], fill_color=color, marker="circle", size=10) + + def save( + self, + filename: str, + transparent: bool = False, + *, + webdriver: WebDriver | None = None, + ) -> None: + """Save plots to SVG or PNG file. + + Args: + filename (str): Filename to save to. + transparent (bool, optional): Whether background should be transparent, default + ``False``. + webdriver (WebDriver, optional): Selenium WebDriver instance to use to create the image. + + .. versionadded:: 1.1.1 + + Warning: + To output to SVG file, ``want_svg=True`` must have been passed to the constructor. + """ + if transparent: + for fig in self._figures: + fig.background_fill_color = None + fig.border_fill_color = None + + if self._want_svg: + export_svg(self._layout, filename=filename, webdriver=webdriver) + else: + export_png(self._layout, filename=filename, webdriver=webdriver) + + def save_to_buffer(self, *, webdriver: WebDriver | None = None) -> io.BytesIO: + """Save plots to an ``io.BytesIO`` buffer. + + Args: + webdriver (WebDriver, optional): Selenium WebDriver instance to use to create the image. + + .. versionadded:: 1.1.1 + + Return: + BytesIO: PNG image buffer. + """ + image = get_screenshot_as_png(self._layout, driver=webdriver) + buffer = io.BytesIO() + image.save(buffer, "png") + return buffer + + def show(self) -> None: + """Show plots in web browser, in usual Bokeh manner. + """ + show(self._layout) + + def title(self, title: str, ax: figure | int = 0, color: str | None = None) -> None: + """Set the title of a single plot. + + Args: + title (str): Title text. + ax (int or Bokeh Figure, optional): Which plot to set the title of, default ``0``. + color (str, optional): Color to set title. May be a string color or the letter ``"C"`` + followed by an integer in the range ``"C0"`` to ``"C9"`` to use a color from the + ``Category10`` palette. Default ``None`` which is ``black``. + """ + fig = self._get_figure(ax) + fig.title = title + fig.title.align = "center" # type: ignore[attr-defined] + if color is not None: + fig.title.text_color = self._convert_color(color) # type: ignore[attr-defined] + + def z_values( + self, + x: ArrayLike, + y: ArrayLike, + z: ArrayLike, + ax: figure | int = 0, + color: str = "green", + fmt: str = ".1f", + quad_as_tri: bool = False, + ) -> None: + """Show ``z`` values on a single plot. + + Args: + x (array-like of shape (ny, nx) or (nx,)): The x-coordinates of the grid points. + y (array-like of shape (ny, nx) or (ny,)): The y-coordinates of the grid points. + z (array-like of shape (ny, nx): z-values. + ax (int or Bokeh Figure, optional): Which plot to use, default ``0``. + color (str, optional): Color of added text. May be a string color or the letter ``"C"`` + followed by an integer in the range ``"C0"`` to ``"C9"`` to use a color from the + ``Category10`` palette. Default ``"green"``. + fmt (str, optional): Format to display z-values, default ``".1f"``. + quad_as_tri (bool, optional): Whether to show z-values at the ``quad_as_tri`` centres + of quads. + + Warning: + ``quad_as_tri=True`` shows z-values for all quads, even if masked. + """ + fig = self._get_figure(ax) + color = self._convert_color(color) + x, y = self._grid_as_2d(x, y) + z = np.asarray(z) + ny, nx = z.shape + kwargs = {"text_color": color, "text_align": "center", "text_baseline": "middle"} + for j in range(ny): + for i in range(nx): + label = Label(x=x[j, i], y=y[j, i], text=f"{z[j, i]:{fmt}}", **kwargs) # type: ignore[arg-type] + fig.add_layout(label) + if quad_as_tri: + for j in range(ny-1): + for i in range(nx-1): + xx = np.mean(x[j:j+2, i:i+2]) + yy = np.mean(y[j:j+2, i:i+2]) + zz = np.mean(z[j:j+2, i:i+2]) + fig.add_layout(Label(x=xx, y=yy, text=f"{zz:{fmt}}", **kwargs)) # type: ignore[arg-type] diff --git a/lib/python3.12/site-packages/contourpy/util/bokeh_util.py b/lib/python3.12/site-packages/contourpy/util/bokeh_util.py new file mode 100644 index 0000000000000000000000000000000000000000..e75eb844536b26dfe90de08bb65328e5d2e5ba1c --- /dev/null +++ b/lib/python3.12/site-packages/contourpy/util/bokeh_util.py @@ -0,0 +1,74 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, cast + +from contourpy import FillType, LineType +from contourpy.array import offsets_from_codes +from contourpy.convert import convert_lines +from contourpy.dechunk import dechunk_lines + +if TYPE_CHECKING: + from contourpy._contourpy import ( + CoordinateArray, + FillReturn, + LineReturn, + LineReturn_ChunkCombinedNan, + ) + + +def filled_to_bokeh( + filled: FillReturn, + fill_type: FillType, +) -> tuple[list[list[CoordinateArray]], list[list[CoordinateArray]]]: + xs: list[list[CoordinateArray]] = [] + ys: list[list[CoordinateArray]] = [] + if fill_type in (FillType.OuterOffset, FillType.ChunkCombinedOffset, + FillType.OuterCode, FillType.ChunkCombinedCode): + have_codes = fill_type in (FillType.OuterCode, FillType.ChunkCombinedCode) + + for points, offsets in zip(*filled): + if points is None: + continue + if have_codes: + offsets = offsets_from_codes(offsets) + xs.append([]) # New outer with zero or more holes. + ys.append([]) + for i in range(len(offsets)-1): + xys = points[offsets[i]:offsets[i+1]] + xs[-1].append(xys[:, 0]) + ys[-1].append(xys[:, 1]) + elif fill_type in (FillType.ChunkCombinedCodeOffset, FillType.ChunkCombinedOffsetOffset): + for points, codes_or_offsets, outer_offsets in zip(*filled): + if points is None: + continue + for j in range(len(outer_offsets)-1): + if fill_type == FillType.ChunkCombinedCodeOffset: + codes = codes_or_offsets[outer_offsets[j]:outer_offsets[j+1]] + offsets = offsets_from_codes(codes) + outer_offsets[j] + else: + offsets = codes_or_offsets[outer_offsets[j]:outer_offsets[j+1]+1] + xs.append([]) # New outer with zero or more holes. + ys.append([]) + for k in range(len(offsets)-1): + xys = points[offsets[k]:offsets[k+1]] + xs[-1].append(xys[:, 0]) + ys[-1].append(xys[:, 1]) + else: + raise RuntimeError(f"Conversion of FillType {fill_type} to Bokeh is not implemented") + + return xs, ys + + +def lines_to_bokeh( + lines: LineReturn, + line_type: LineType, +) -> tuple[CoordinateArray | None, CoordinateArray | None]: + lines = convert_lines(lines, line_type, LineType.ChunkCombinedNan) + lines = dechunk_lines(lines, LineType.ChunkCombinedNan) + if TYPE_CHECKING: + lines = cast(LineReturn_ChunkCombinedNan, lines) + points = lines[0][0] + if points is None: + return None, None + else: + return points[:, 0], points[:, 1] diff --git a/lib/python3.12/site-packages/contourpy/util/data.py b/lib/python3.12/site-packages/contourpy/util/data.py new file mode 100644 index 0000000000000000000000000000000000000000..5fa75486958c8277430f149daf872e859de8e000 --- /dev/null +++ b/lib/python3.12/site-packages/contourpy/util/data.py @@ -0,0 +1,78 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any + +import numpy as np + +if TYPE_CHECKING: + from contourpy._contourpy import CoordinateArray + + +def simple( + shape: tuple[int, int], want_mask: bool = False, +) -> tuple[CoordinateArray, CoordinateArray, CoordinateArray | np.ma.MaskedArray[Any, Any]]: + """Return simple test data consisting of the sum of two gaussians. + + Args: + shape (tuple(int, int)): 2D shape of data to return. + want_mask (bool, optional): Whether test data should be masked or not, default ``False``. + + Return: + Tuple of 3 arrays: ``x``, ``y``, ``z`` test data, ``z`` will be masked if + ``want_mask=True``. + """ + ny, nx = shape + x = np.arange(nx, dtype=np.float64) + y = np.arange(ny, dtype=np.float64) + x, y = np.meshgrid(x, y) + + xscale = nx - 1.0 + yscale = ny - 1.0 + + # z is sum of 2D gaussians. + amp = np.asarray([1.0, -1.0, 0.8, -0.9, 0.7]) + mid = np.asarray([[0.4, 0.2], [0.3, 0.8], [0.9, 0.75], [0.7, 0.3], [0.05, 0.7]]) + width = np.asarray([0.4, 0.2, 0.2, 0.2, 0.1]) + + z = np.zeros_like(x) + for i in range(len(amp)): + z += amp[i]*np.exp(-((x/xscale - mid[i, 0])**2 + (y/yscale - mid[i, 1])**2) / width[i]**2) + + if want_mask: + mask = np.logical_or( + ((x/xscale - 1.0)**2 / 0.2 + (y/yscale - 0.0)**2 / 0.1) < 1.0, + ((x/xscale - 0.2)**2 / 0.02 + (y/yscale - 0.45)**2 / 0.08) < 1.0, + ) + z = np.ma.array(z, mask=mask) # type: ignore[no-untyped-call] + + return x, y, z + + +def random( + shape: tuple[int, int], seed: int = 2187, mask_fraction: float = 0.0, +) -> tuple[CoordinateArray, CoordinateArray, CoordinateArray | np.ma.MaskedArray[Any, Any]]: + """Return random test data in the range 0 to 1. + + Args: + shape (tuple(int, int)): 2D shape of data to return. + seed (int, optional): Seed for random number generator, default 2187. + mask_fraction (float, optional): Fraction of elements to mask, default 0. + + Return: + Tuple of 3 arrays: ``x``, ``y``, ``z`` test data, ``z`` will be masked if + ``mask_fraction`` is greater than zero. + """ + ny, nx = shape + x = np.arange(nx, dtype=np.float64) + y = np.arange(ny, dtype=np.float64) + x, y = np.meshgrid(x, y) + + rng = np.random.default_rng(seed) + z = rng.uniform(size=shape) + + if mask_fraction > 0.0: + mask_fraction = min(mask_fraction, 0.99) + mask = rng.uniform(size=shape) < mask_fraction + z = np.ma.array(z, mask=mask) # type: ignore[no-untyped-call] + + return x, y, z diff --git a/lib/python3.12/site-packages/contourpy/util/mpl_renderer.py b/lib/python3.12/site-packages/contourpy/util/mpl_renderer.py new file mode 100644 index 0000000000000000000000000000000000000000..7c39b31ae882a05f7239b0483775576595121115 --- /dev/null +++ b/lib/python3.12/site-packages/contourpy/util/mpl_renderer.py @@ -0,0 +1,535 @@ +from __future__ import annotations + +import io +from itertools import pairwise +from typing import TYPE_CHECKING, Any, cast + +import matplotlib.collections as mcollections +import matplotlib.pyplot as plt +import numpy as np + +from contourpy import FillType, LineType +from contourpy.convert import convert_filled, convert_lines +from contourpy.enum_util import as_fill_type, as_line_type +from contourpy.util.mpl_util import filled_to_mpl_paths, lines_to_mpl_paths +from contourpy.util.renderer import Renderer + +if TYPE_CHECKING: + from collections.abc import Sequence + + from matplotlib.axes import Axes + from matplotlib.figure import Figure + from numpy.typing import ArrayLike + + import contourpy._contourpy as cpy + + +class MplRenderer(Renderer): + """Utility renderer using Matplotlib to render a grid of plots over the same (x, y) range. + + Args: + nrows (int, optional): Number of rows of plots, default ``1``. + ncols (int, optional): Number of columns of plots, default ``1``. + figsize (tuple(float, float), optional): Figure size in inches, default ``(9, 9)``. + show_frame (bool, optional): Whether to show frame and axes ticks, default ``True``. + backend (str, optional): Matplotlib backend to use or ``None`` for default backend. + Default ``None``. + gridspec_kw (dict, optional): Gridspec keyword arguments to pass to ``plt.subplots``, + default None. + """ + _axes: Sequence[Axes] + _fig: Figure + _want_tight: bool + + def __init__( + self, + nrows: int = 1, + ncols: int = 1, + figsize: tuple[float, float] = (9, 9), + show_frame: bool = True, + backend: str | None = None, + gridspec_kw: dict[str, Any] | None = None, + ) -> None: + if backend is not None: + import matplotlib as mpl + mpl.use(backend) + + kwargs: dict[str, Any] = {"figsize": figsize, "squeeze": False, + "sharex": True, "sharey": True} + if gridspec_kw is not None: + kwargs["gridspec_kw"] = gridspec_kw + else: + kwargs["subplot_kw"] = {"aspect": "equal"} + + self._fig, axes = plt.subplots(nrows, ncols, **kwargs) + self._axes = axes.flatten() + if not show_frame: + for ax in self._axes: + ax.axis("off") + + self._want_tight = True + + def __del__(self) -> None: + if hasattr(self, "_fig"): + plt.close(self._fig) + + def _autoscale(self) -> None: + # Using axes._need_autoscale attribute if need to autoscale before rendering after adding + # lines/filled. Only want to autoscale once per axes regardless of how many lines/filled + # added. + for ax in self._axes: + if getattr(ax, "_need_autoscale", False): + ax.autoscale_view(tight=True) + ax._need_autoscale = False # type: ignore[attr-defined] + if self._want_tight and len(self._axes) > 1: + self._fig.tight_layout() + + def _get_ax(self, ax: Axes | int) -> Axes: + if isinstance(ax, int): + ax = self._axes[ax] + return ax + + def filled( + self, + filled: cpy.FillReturn, + fill_type: FillType | str, + ax: Axes | int = 0, + color: str = "C0", + alpha: float = 0.7, + ) -> None: + """Plot filled contours on a single Axes. + + Args: + filled (sequence of arrays): Filled contour data as returned by + :meth:`~.ContourGenerator.filled`. + fill_type (FillType or str): Type of :meth:`~.ContourGenerator.filled` data as returned + by :attr:`~.ContourGenerator.fill_type`, or string equivalent + ax (int or Maplotlib Axes, optional): Which axes to plot on, default ``0``. + color (str, optional): Color to plot with. May be a string color or the letter ``"C"`` + followed by an integer in the range ``"C0"`` to ``"C9"`` to use a color from the + ``tab10`` colormap. Default ``"C0"``. + alpha (float, optional): Opacity to plot with, default ``0.7``. + """ + fill_type = as_fill_type(fill_type) + ax = self._get_ax(ax) + paths = filled_to_mpl_paths(filled, fill_type) + collection = mcollections.PathCollection( + paths, facecolors=color, edgecolors="none", lw=0, alpha=alpha) + ax.add_collection(collection) + ax._need_autoscale = True # type: ignore[attr-defined] + + def grid( + self, + x: ArrayLike, + y: ArrayLike, + ax: Axes | int = 0, + color: str = "black", + alpha: float = 0.1, + point_color: str | None = None, + quad_as_tri_alpha: float = 0, + ) -> None: + """Plot quad grid lines on a single Axes. + + Args: + x (array-like of shape (ny, nx) or (nx,)): The x-coordinates of the grid points. + y (array-like of shape (ny, nx) or (ny,)): The y-coordinates of the grid points. + ax (int or Matplotlib Axes, optional): Which Axes to plot on, default ``0``. + color (str, optional): Color to plot grid lines, default ``"black"``. + alpha (float, optional): Opacity to plot lines with, default ``0.1``. + point_color (str, optional): Color to plot grid points or ``None`` if grid points + should not be plotted, default ``None``. + quad_as_tri_alpha (float, optional): Opacity to plot ``quad_as_tri`` grid, default 0. + + Colors may be a string color or the letter ``"C"`` followed by an integer in the range + ``"C0"`` to ``"C9"`` to use a color from the ``tab10`` colormap. + + Warning: + ``quad_as_tri_alpha > 0`` plots all quads as though they are unmasked. + """ + ax = self._get_ax(ax) + x, y = self._grid_as_2d(x, y) + kwargs: dict[str, Any] = {"color": color, "alpha": alpha} + ax.plot(x, y, x.T, y.T, **kwargs) + if quad_as_tri_alpha > 0: + # Assumes no quad mask. + xmid = 0.25*(x[:-1, :-1] + x[1:, :-1] + x[:-1, 1:] + x[1:, 1:]) + ymid = 0.25*(y[:-1, :-1] + y[1:, :-1] + y[:-1, 1:] + y[1:, 1:]) + kwargs["alpha"] = quad_as_tri_alpha + ax.plot( + np.stack((x[:-1, :-1], xmid, x[1:, 1:])).reshape((3, -1)), + np.stack((y[:-1, :-1], ymid, y[1:, 1:])).reshape((3, -1)), + np.stack((x[1:, :-1], xmid, x[:-1, 1:])).reshape((3, -1)), + np.stack((y[1:, :-1], ymid, y[:-1, 1:])).reshape((3, -1)), + **kwargs) + if point_color is not None: + ax.plot(x, y, color=point_color, alpha=alpha, marker="o", lw=0) + ax._need_autoscale = True # type: ignore[attr-defined] + + def lines( + self, + lines: cpy.LineReturn, + line_type: LineType | str, + ax: Axes | int = 0, + color: str = "C0", + alpha: float = 1.0, + linewidth: float = 1, + ) -> None: + """Plot contour lines on a single Axes. + + Args: + lines (sequence of arrays): Contour line data as returned by + :meth:`~.ContourGenerator.lines`. + line_type (LineType or str): Type of :meth:`~.ContourGenerator.lines` data as returned + by :attr:`~.ContourGenerator.line_type`, or string equivalent. + ax (int or Matplotlib Axes, optional): Which Axes to plot on, default ``0``. + color (str, optional): Color to plot lines. May be a string color or the letter ``"C"`` + followed by an integer in the range ``"C0"`` to ``"C9"`` to use a color from the + ``tab10`` colormap. Default ``"C0"``. + alpha (float, optional): Opacity to plot lines with, default ``1.0``. + linewidth (float, optional): Width of lines, default ``1``. + """ + line_type = as_line_type(line_type) + ax = self._get_ax(ax) + paths = lines_to_mpl_paths(lines, line_type) + collection = mcollections.PathCollection( + paths, facecolors="none", edgecolors=color, lw=linewidth, alpha=alpha) + ax.add_collection(collection) + ax._need_autoscale = True # type: ignore[attr-defined] + + def mask( + self, + x: ArrayLike, + y: ArrayLike, + z: ArrayLike | np.ma.MaskedArray[Any, Any], + ax: Axes | int = 0, + color: str = "black", + ) -> None: + """Plot masked out grid points as circles on a single Axes. + + Args: + x (array-like of shape (ny, nx) or (nx,)): The x-coordinates of the grid points. + y (array-like of shape (ny, nx) or (ny,)): The y-coordinates of the grid points. + z (masked array of shape (ny, nx): z-values. + ax (int or Matplotlib Axes, optional): Which Axes to plot on, default ``0``. + color (str, optional): Circle color, default ``"black"``. + """ + mask = np.ma.getmask(z) + if mask is np.ma.nomask: + return + ax = self._get_ax(ax) + x, y = self._grid_as_2d(x, y) + ax.plot(x[mask], y[mask], "o", c=color) + + def save(self, filename: str, transparent: bool = False) -> None: + """Save plots to SVG or PNG file. + + Args: + filename (str): Filename to save to. + transparent (bool, optional): Whether background should be transparent, default + ``False``. + """ + self._autoscale() + self._fig.savefig(filename, transparent=transparent) + + def save_to_buffer(self) -> io.BytesIO: + """Save plots to an ``io.BytesIO`` buffer. + + Return: + BytesIO: PNG image buffer. + """ + self._autoscale() + buf = io.BytesIO() + self._fig.savefig(buf, format="png") + buf.seek(0) + return buf + + def show(self) -> None: + """Show plots in an interactive window, in the usual Matplotlib manner. + """ + self._autoscale() + plt.show() + + def title(self, title: str, ax: Axes | int = 0, color: str | None = None) -> None: + """Set the title of a single Axes. + + Args: + title (str): Title text. + ax (int or Matplotlib Axes, optional): Which Axes to set the title of, default ``0``. + color (str, optional): Color to set title. May be a string color or the letter ``"C"`` + followed by an integer in the range ``"C0"`` to ``"C9"`` to use a color from the + ``tab10`` colormap. Default is ``None`` which uses Matplotlib's default title color + that depends on the stylesheet in use. + """ + if color: + self._get_ax(ax).set_title(title, color=color) + else: + self._get_ax(ax).set_title(title) + + def z_values( + self, + x: ArrayLike, + y: ArrayLike, + z: ArrayLike, + ax: Axes | int = 0, + color: str = "green", + fmt: str = ".1f", + quad_as_tri: bool = False, + ) -> None: + """Show ``z`` values on a single Axes. + + Args: + x (array-like of shape (ny, nx) or (nx,)): The x-coordinates of the grid points. + y (array-like of shape (ny, nx) or (ny,)): The y-coordinates of the grid points. + z (array-like of shape (ny, nx): z-values. + ax (int or Matplotlib Axes, optional): Which Axes to plot on, default ``0``. + color (str, optional): Color of added text. May be a string color or the letter ``"C"`` + followed by an integer in the range ``"C0"`` to ``"C9"`` to use a color from the + ``tab10`` colormap. Default ``"green"``. + fmt (str, optional): Format to display z-values, default ``".1f"``. + quad_as_tri (bool, optional): Whether to show z-values at the ``quad_as_tri`` centers + of quads. + + Warning: + ``quad_as_tri=True`` shows z-values for all quads, even if masked. + """ + ax = self._get_ax(ax) + x, y = self._grid_as_2d(x, y) + z = np.asarray(z) + ny, nx = z.shape + for j in range(ny): + for i in range(nx): + ax.text(x[j, i], y[j, i], f"{z[j, i]:{fmt}}", ha="center", va="center", + color=color, clip_on=True) + if quad_as_tri: + for j in range(ny-1): + for i in range(nx-1): + xx = np.mean(x[j:j+2, i:i+2], dtype=np.float64) + yy = np.mean(y[j:j+2, i:i+2], dtype=np.float64) + zz = np.mean(z[j:j+2, i:i+2]) + ax.text(xx, yy, f"{zz:{fmt}}", ha="center", va="center", color=color, + clip_on=True) + + +class MplTestRenderer(MplRenderer): + """Test renderer implemented using Matplotlib. + + No whitespace around plots and no spines/ticks displayed. + Uses Agg backend, so can only save to file/buffer, cannot call ``show()``. + """ + def __init__( + self, + nrows: int = 1, + ncols: int = 1, + figsize: tuple[float, float] = (9, 9), + ) -> None: + gridspec = { + "left": 0.01, + "right": 0.99, + "top": 0.99, + "bottom": 0.01, + "wspace": 0.01, + "hspace": 0.01, + } + super().__init__( + nrows, ncols, figsize, show_frame=True, backend="Agg", gridspec_kw=gridspec, + ) + + for ax in self._axes: + ax.set_xmargin(0.0) + ax.set_ymargin(0.0) + ax.set_xticks([]) + ax.set_yticks([]) + + self._want_tight = False + + +class MplDebugRenderer(MplRenderer): + """Debug renderer implemented using Matplotlib. + + Extends ``MplRenderer`` to add extra information to help in debugging such as markers, arrows, + text, etc. + """ + def __init__( + self, + nrows: int = 1, + ncols: int = 1, + figsize: tuple[float, float] = (9, 9), + show_frame: bool = True, + ) -> None: + super().__init__(nrows, ncols, figsize, show_frame) + + def _arrow( + self, + ax: Axes, + line_start: cpy.CoordinateArray, + line_end: cpy.CoordinateArray, + color: str, + alpha: float, + arrow_size: float, + ) -> None: + mid = 0.5*(line_start + line_end) + along = line_end - line_start + along /= np.sqrt(np.dot(along, along)) # Unit vector. + right = np.asarray((along[1], -along[0])) + arrow = np.stack(( + mid - (along*0.5 - right)*arrow_size, + mid + along*0.5*arrow_size, + mid - (along*0.5 + right)*arrow_size, + )) + ax.plot(arrow[:, 0], arrow[:, 1], "-", c=color, alpha=alpha) + + def filled( + self, + filled: cpy.FillReturn, + fill_type: FillType | str, + ax: Axes | int = 0, + color: str = "C1", + alpha: float = 0.7, + line_color: str = "C0", + line_alpha: float = 0.7, + point_color: str = "C0", + start_point_color: str = "red", + arrow_size: float = 0.1, + ) -> None: + fill_type = as_fill_type(fill_type) + super().filled(filled, fill_type, ax, color, alpha) + + if line_color is None and point_color is None: + return + + ax = self._get_ax(ax) + filled = convert_filled(filled, fill_type, FillType.ChunkCombinedOffset) + + # Lines. + if line_color is not None: + for points, offsets in zip(*filled): + if points is None: + continue + for start, end in pairwise(offsets): + xys = points[start:end] + ax.plot(xys[:, 0], xys[:, 1], c=line_color, alpha=line_alpha) + + if arrow_size > 0.0: + n = len(xys) + for i in range(n-1): + self._arrow(ax, xys[i], xys[i+1], line_color, line_alpha, arrow_size) + + # Points. + if point_color is not None: + for points, offsets in zip(*filled): + if points is None: + continue + mask = np.ones(offsets[-1], dtype=bool) + mask[offsets[1:]-1] = False # Exclude end points. + if start_point_color is not None: + start_indices = offsets[:-1] + mask[start_indices] = False # Exclude start points. + ax.plot( + points[:, 0][mask], points[:, 1][mask], "o", c=point_color, alpha=line_alpha) + + if start_point_color is not None: + ax.plot(points[:, 0][start_indices], points[:, 1][start_indices], "o", + c=start_point_color, alpha=line_alpha) + + def lines( + self, + lines: cpy.LineReturn, + line_type: LineType | str, + ax: Axes | int = 0, + color: str = "C0", + alpha: float = 1.0, + linewidth: float = 1, + point_color: str = "C0", + start_point_color: str = "red", + arrow_size: float = 0.1, + ) -> None: + line_type = as_line_type(line_type) + super().lines(lines, line_type, ax, color, alpha, linewidth) + + if arrow_size == 0.0 and point_color is None: + return + + ax = self._get_ax(ax) + separate_lines = convert_lines(lines, line_type, LineType.Separate) + if TYPE_CHECKING: + separate_lines = cast(cpy.LineReturn_Separate, separate_lines) + + if arrow_size > 0.0: + for line in separate_lines: + for i in range(len(line)-1): + self._arrow(ax, line[i], line[i+1], color, alpha, arrow_size) + + if point_color is not None: + for line in separate_lines: + start_index = 0 + end_index = len(line) + if start_point_color is not None: + ax.plot(line[0, 0], line[0, 1], "o", c=start_point_color, alpha=alpha) + start_index = 1 + if line[0][0] == line[-1][0] and line[0][1] == line[-1][1]: + end_index -= 1 + ax.plot(line[start_index:end_index, 0], line[start_index:end_index, 1], "o", + c=color, alpha=alpha) + + def point_numbers( + self, + x: ArrayLike, + y: ArrayLike, + z: ArrayLike, + ax: Axes | int = 0, + color: str = "red", + ) -> None: + ax = self._get_ax(ax) + x, y = self._grid_as_2d(x, y) + z = np.asarray(z) + ny, nx = z.shape + for j in range(ny): + for i in range(nx): + quad = i + j*nx + ax.text(x[j, i], y[j, i], str(quad), ha="right", va="top", color=color, + clip_on=True) + + def quad_numbers( + self, + x: ArrayLike, + y: ArrayLike, + z: ArrayLike, + ax: Axes | int = 0, + color: str = "blue", + ) -> None: + ax = self._get_ax(ax) + x, y = self._grid_as_2d(x, y) + z = np.asarray(z) + ny, nx = z.shape + for j in range(1, ny): + for i in range(1, nx): + quad = i + j*nx + xmid = x[j-1:j+1, i-1:i+1].mean() + ymid = y[j-1:j+1, i-1:i+1].mean() + ax.text(xmid, ymid, str(quad), ha="center", va="center", color=color, clip_on=True) + + def z_levels( + self, + x: ArrayLike, + y: ArrayLike, + z: ArrayLike, + lower_level: float, + upper_level: float | None = None, + ax: Axes | int = 0, + color: str = "green", + ) -> None: + ax = self._get_ax(ax) + x, y = self._grid_as_2d(x, y) + z = np.asarray(z) + ny, nx = z.shape + for j in range(ny): + for i in range(nx): + zz = z[j, i] + if upper_level is not None and zz > upper_level: + z_level = 2 + elif zz > lower_level: + z_level = 1 + else: + z_level = 0 + ax.text(x[j, i], y[j, i], str(z_level), ha="left", va="bottom", color=color, + clip_on=True) diff --git a/lib/python3.12/site-packages/contourpy/util/mpl_util.py b/lib/python3.12/site-packages/contourpy/util/mpl_util.py new file mode 100644 index 0000000000000000000000000000000000000000..d8587798c499a9ab898ee6cb54738be7e41bf7cf --- /dev/null +++ b/lib/python3.12/site-packages/contourpy/util/mpl_util.py @@ -0,0 +1,77 @@ +from __future__ import annotations + +from itertools import pairwise +from typing import TYPE_CHECKING, cast + +import matplotlib.path as mpath +import numpy as np + +from contourpy import FillType, LineType +from contourpy.array import codes_from_offsets + +if TYPE_CHECKING: + from contourpy._contourpy import FillReturn, LineReturn, LineReturn_Separate + + +def filled_to_mpl_paths(filled: FillReturn, fill_type: FillType) -> list[mpath.Path]: + if fill_type in (FillType.OuterCode, FillType.ChunkCombinedCode): + paths = [mpath.Path(points, codes) for points, codes in zip(*filled) if points is not None] + elif fill_type in (FillType.OuterOffset, FillType.ChunkCombinedOffset): + paths = [mpath.Path(points, codes_from_offsets(offsets)) + for points, offsets in zip(*filled) if points is not None] + elif fill_type == FillType.ChunkCombinedCodeOffset: + paths = [] + for points, codes, outer_offsets in zip(*filled): + if points is None: + continue + points = np.split(points, outer_offsets[1:-1]) + codes = np.split(codes, outer_offsets[1:-1]) + paths += [mpath.Path(p, c) for p, c in zip(points, codes)] + elif fill_type == FillType.ChunkCombinedOffsetOffset: + paths = [] + for points, offsets, outer_offsets in zip(*filled): + if points is None: + continue + for i in range(len(outer_offsets)-1): + offs = offsets[outer_offsets[i]:outer_offsets[i+1]+1] + pts = points[offs[0]:offs[-1]] + paths += [mpath.Path(pts, codes_from_offsets(offs - offs[0]))] + else: + raise RuntimeError(f"Conversion of FillType {fill_type} to MPL Paths is not implemented") + return paths + + +def lines_to_mpl_paths(lines: LineReturn, line_type: LineType) -> list[mpath.Path]: + if line_type == LineType.Separate: + if TYPE_CHECKING: + lines = cast(LineReturn_Separate, lines) + paths = [] + for line in lines: + # Drawing as Paths so that they can be closed correctly. + closed = line[0, 0] == line[-1, 0] and line[0, 1] == line[-1, 1] + paths.append(mpath.Path(line, closed=closed)) + elif line_type in (LineType.SeparateCode, LineType.ChunkCombinedCode): + paths = [mpath.Path(points, codes) for points, codes in zip(*lines) if points is not None] + elif line_type == LineType.ChunkCombinedOffset: + paths = [] + for points, offsets in zip(*lines): + if points is None: + continue + for i in range(len(offsets)-1): + line = points[offsets[i]:offsets[i+1]] + closed = line[0, 0] == line[-1, 0] and line[0, 1] == line[-1, 1] + paths.append(mpath.Path(line, closed=closed)) + elif line_type == LineType.ChunkCombinedNan: + paths = [] + for points in lines[0]: + if points is None: + continue + nan_offsets = np.nonzero(np.isnan(points[:, 0]))[0] + nan_offsets = np.concatenate([[-1], nan_offsets, [len(points)]]) + for s, e in pairwise(nan_offsets): + line = points[s+1:e] + closed = line[0, 0] == line[-1, 0] and line[0, 1] == line[-1, 1] + paths.append(mpath.Path(line, closed=closed)) + else: + raise RuntimeError(f"Conversion of LineType {line_type} to MPL Paths is not implemented") + return paths diff --git a/lib/python3.12/site-packages/contourpy/util/renderer.py b/lib/python3.12/site-packages/contourpy/util/renderer.py new file mode 100644 index 0000000000000000000000000000000000000000..716569f776c4df9197a36f40e9f0a5e176292114 --- /dev/null +++ b/lib/python3.12/site-packages/contourpy/util/renderer.py @@ -0,0 +1,166 @@ +from __future__ import annotations + +from abc import ABC, abstractmethod +from typing import TYPE_CHECKING, Any + +import numpy as np + +if TYPE_CHECKING: + import io + + from numpy.typing import ArrayLike + + from contourpy._contourpy import CoordinateArray, FillReturn, FillType, LineReturn, LineType + + +class Renderer(ABC): + """Abstract base class for renderers.""" + + def _grid_as_2d(self, x: ArrayLike, y: ArrayLike) -> tuple[CoordinateArray, CoordinateArray]: + x = np.asarray(x) + y = np.asarray(y) + if x.ndim == 1: + x, y = np.meshgrid(x, y) + return x, y + + @abstractmethod + def filled( + self, + filled: FillReturn, + fill_type: FillType | str, + ax: Any = 0, + color: str = "C0", + alpha: float = 0.7, + ) -> None: + pass + + @abstractmethod + def grid( + self, + x: ArrayLike, + y: ArrayLike, + ax: Any = 0, + color: str = "black", + alpha: float = 0.1, + point_color: str | None = None, + quad_as_tri_alpha: float = 0, + ) -> None: + pass + + @abstractmethod + def lines( + self, + lines: LineReturn, + line_type: LineType | str, + ax: Any = 0, + color: str = "C0", + alpha: float = 1.0, + linewidth: float = 1, + ) -> None: + pass + + @abstractmethod + def mask( + self, + x: ArrayLike, + y: ArrayLike, + z: ArrayLike | np.ma.MaskedArray[Any, Any], + ax: Any = 0, + color: str = "black", + ) -> None: + pass + + def multi_filled( + self, + multi_filled: list[FillReturn], + fill_type: FillType | str, + ax: Any = 0, + color: str | None = None, + **kwargs: Any, + ) -> None: + """Plot multiple sets of filled contours on a single axes. + + Args: + multi_filled (list of filled contour arrays): Multiple filled contour sets as returned + by :meth:`.ContourGenerator.multi_filled`. + fill_type (FillType or str): Type of filled data as returned by + :attr:`~.ContourGenerator.fill_type`, or string equivalent. + ax (int or Renderer-specific axes or figure object, optional): Which axes to plot on, + default ``0``. + color (str or None, optional): If a string color then this same color is used for all + filled contours. If ``None``, the default, then the filled contour sets use colors + from the ``tab10`` colormap in order, wrapping around to the beginning if more than + 10 sets of filled contours are rendered. + kwargs: All other keyword argument are passed on to + :meth:`.Renderer.filled` unchanged. + + .. versionadded:: 1.3.0 + """ + if color is not None: + kwargs["color"] = color + for i, filled in enumerate(multi_filled): + if color is None: + kwargs["color"] = f"C{i % 10}" + self.filled(filled, fill_type, ax, **kwargs) + + def multi_lines( + self, + multi_lines: list[LineReturn], + line_type: LineType | str, + ax: Any = 0, + color: str | None = None, + **kwargs: Any, + ) -> None: + """Plot multiple sets of contour lines on a single axes. + + Args: + multi_lines (list of contour line arrays): Multiple contour line sets as returned by + :meth:`.ContourGenerator.multi_lines`. + line_type (LineType or str): Type of line data as returned by + :attr:`~.ContourGenerator.line_type`, or string equivalent. + ax (int or Renderer-specific axes or figure object, optional): Which axes to plot on, + default ``0``. + color (str or None, optional): If a string color then this same color is used for all + lines. If ``None``, the default, then the line sets use colors from the ``tab10`` + colormap in order, wrapping around to the beginning if more than 10 sets of lines + are rendered. + kwargs: All other keyword argument are passed on to + :meth:`Renderer.lines` unchanged. + + .. versionadded:: 1.3.0 + """ + if color is not None: + kwargs["color"] = color + for i, lines in enumerate(multi_lines): + if color is None: + kwargs["color"] = f"C{i % 10}" + self.lines(lines, line_type, ax, **kwargs) + + @abstractmethod + def save(self, filename: str, transparent: bool = False) -> None: + pass + + @abstractmethod + def save_to_buffer(self) -> io.BytesIO: + pass + + @abstractmethod + def show(self) -> None: + pass + + @abstractmethod + def title(self, title: str, ax: Any = 0, color: str | None = None) -> None: + pass + + @abstractmethod + def z_values( + self, + x: ArrayLike, + y: ArrayLike, + z: ArrayLike, + ax: Any = 0, + color: str = "green", + fmt: str = ".1f", + quad_as_tri: bool = False, + ) -> None: + pass diff --git a/lib/python3.12/site-packages/flash_attn-2.7.4.post1.dist-info/AUTHORS b/lib/python3.12/site-packages/flash_attn-2.7.4.post1.dist-info/AUTHORS new file mode 100644 index 0000000000000000000000000000000000000000..e35a781665eafa7421c30241962ef8e49588bffc --- /dev/null +++ b/lib/python3.12/site-packages/flash_attn-2.7.4.post1.dist-info/AUTHORS @@ -0,0 +1 @@ +Tri Dao, trid@cs.stanford.edu \ No newline at end of file diff --git a/lib/python3.12/site-packages/flash_attn-2.7.4.post1.dist-info/INSTALLER b/lib/python3.12/site-packages/flash_attn-2.7.4.post1.dist-info/INSTALLER new file mode 100644 index 0000000000000000000000000000000000000000..a1b589e38a32041e49332e5e81c2d363dc418d68 --- /dev/null +++ b/lib/python3.12/site-packages/flash_attn-2.7.4.post1.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/lib/python3.12/site-packages/flash_attn-2.7.4.post1.dist-info/LICENSE b/lib/python3.12/site-packages/flash_attn-2.7.4.post1.dist-info/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..5860e4b33f3d9d85fc636137c559331d51783a5b --- /dev/null +++ b/lib/python3.12/site-packages/flash_attn-2.7.4.post1.dist-info/LICENSE @@ -0,0 +1,29 @@ +BSD 3-Clause License + +Copyright (c) 2022, the respective contributors, as shown by the AUTHORS file. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/lib/python3.12/site-packages/flash_attn-2.7.4.post1.dist-info/METADATA b/lib/python3.12/site-packages/flash_attn-2.7.4.post1.dist-info/METADATA new file mode 100644 index 0000000000000000000000000000000000000000..e598bc862de109f0dee813f22f9795f1891dc919 --- /dev/null +++ b/lib/python3.12/site-packages/flash_attn-2.7.4.post1.dist-info/METADATA @@ -0,0 +1,550 @@ +Metadata-Version: 2.2 +Name: flash_attn +Version: 2.7.4.post1 +Summary: Flash Attention: Fast and Memory-Efficient Exact Attention +Home-page: https://github.com/Dao-AILab/flash-attention +Author: Tri Dao +Author-email: tri@tridao.me +Classifier: Programming Language :: Python :: 3 +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: Unix +Requires-Python: >=3.9 +Description-Content-Type: text/markdown +License-File: LICENSE +License-File: AUTHORS +Requires-Dist: torch +Requires-Dist: einops +Dynamic: author +Dynamic: author-email +Dynamic: classifier +Dynamic: description +Dynamic: description-content-type +Dynamic: home-page +Dynamic: requires-dist +Dynamic: requires-python +Dynamic: summary + +# FlashAttention +This repository provides the official implementation of FlashAttention and +FlashAttention-2 from the +following papers. + +**FlashAttention: Fast and Memory-Efficient Exact Attention with IO-Awareness** +Tri Dao, Daniel Y. Fu, Stefano Ermon, Atri Rudra, Christopher Ré +Paper: https://arxiv.org/abs/2205.14135 +IEEE Spectrum [article](https://spectrum.ieee.org/mlperf-rankings-2022) about our submission to the MLPerf 2.0 benchmark using FlashAttention. +![FlashAttention](assets/flashattn_banner.jpg) + +**FlashAttention-2: Faster Attention with Better Parallelism and Work Partitioning** +Tri Dao + +Paper: https://tridao.me/publications/flash2/flash2.pdf + +![FlashAttention-2](assets/flashattention_logo.png) + + +## Usage + +We've been very happy to see FlashAttention being widely adopted in such a short +time after its release. This [page](https://github.com/Dao-AILab/flash-attention/blob/main/usage.md) +contains a partial list of places where FlashAttention is being used. + +FlashAttention and FlashAttention-2 are free to use and modify (see LICENSE). +Please cite and credit FlashAttention if you use it. + + +## FlashAttention-3 beta release +FlashAttention-3 is optimized for Hopper GPUs (e.g. H100). + +Blogpost: https://tridao.me/blog/2024/flash3/ + +Paper: https://tridao.me/publications/flash3/flash3.pdf + +![FlashAttention-3 speedup on H100 80GB SXM5 with FP16](assets/flash3_fp16_fwd.png) + +This is a beta release for testing / benchmarking before we integrate that with +the rest of the repo. + +Currently released: +- FP16 / BF16 forward and backward, FP8 forward + +Requirements: H100 / H800 GPU, CUDA >= 12.3. + +For now, we highly recommend CUDA 12.3 for best performance. + +To install: +```sh +cd hopper +python setup.py install +``` +To run the test: +```sh +export PYTHONPATH=$PWD +pytest -q -s test_flash_attn.py +``` +Once the package is installed, you can import it as follows: +```python +import flash_attn_interface +flash_attn_interface.flash_attn_func() +``` + +## Installation and features +**Requirements:** +- CUDA toolkit or ROCm toolkit +- PyTorch 2.2 and above. +- `packaging` Python package (`pip install packaging`) +- `ninja` Python package (`pip install ninja`) * +- Linux. Might work for Windows starting v2.3.2 (we've seen a few positive [reports](https://github.com/Dao-AILab/flash-attention/issues/595)) but Windows compilation still requires more testing. If you have ideas on how to set up prebuilt CUDA wheels for Windows, please reach out via Github issue. + +\* Make sure that `ninja` is installed and that it works correctly (e.g. `ninja +--version` then `echo $?` should return exit code 0). If not (sometimes `ninja +--version` then `echo $?` returns a nonzero exit code), uninstall then reinstall +`ninja` (`pip uninstall -y ninja && pip install ninja`). Without `ninja`, +compiling can take a very long time (2h) since it does not use multiple CPU +cores. With `ninja` compiling takes 3-5 minutes on a 64-core machine using CUDA toolkit. + +**To install:** +```sh +pip install flash-attn --no-build-isolation +``` +Alternatively you can compile from source: +```sh +python setup.py install +``` + +If your machine has less than 96GB of RAM and lots of CPU cores, `ninja` might +run too many parallel compilation jobs that could exhaust the amount of RAM. To +limit the number of parallel compilation jobs, you can set the environment +variable `MAX_JOBS`: +```sh +MAX_JOBS=4 pip install flash-attn --no-build-isolation +``` + +**Interface:** `src/flash_attention_interface.py` + +### NVIDIA CUDA Support +**Requirements:** +- CUDA 12.0 and above. + +We recommend the +[Pytorch](https://catalog.ngc.nvidia.com/orgs/nvidia/containers/pytorch) +container from Nvidia, which has all the required tools to install FlashAttention. + +FlashAttention-2 with CUDA currently supports: +1. Ampere, Ada, or Hopper GPUs (e.g., A100, RTX 3090, RTX 4090, H100). Support for Turing + GPUs (T4, RTX 2080) is coming soon, please use FlashAttention 1.x for Turing + GPUs for now. +2. Datatype fp16 and bf16 (bf16 requires Ampere, Ada, or Hopper GPUs). +3. All head dimensions up to 256. ~~Head dim > 192 backward requires A100/A800 or H100/H800~~. Head dim 256 backward now works on consumer GPUs (if there's no dropout) as of flash-attn 2.5.5. + +### AMD ROCm Support +ROCm version has two backends. There is [composable_kernel](https://github.com/ROCm/composable_kernel) (ck) which is the default backend and a [Triton](https://github.com/triton-lang/triton) backend. They provide an implementation of FlashAttention-2. + +**Requirements:** +- ROCm 6.0 and above. + +We recommend the +[Pytorch](https://hub.docker.com/r/rocm/pytorch) +container from ROCm, which has all the required tools to install FlashAttention. + +#### Composable Kernel Backend +FlashAttention-2 ROCm CK backend currently supports: +1. MI200 or MI300 GPUs. +2. Datatype fp16 and bf16 +3. Both forward's and backward's head dimensions up to 256. + +#### Triton Backend +The Triton implementation of the [Flash Attention v2](https://tridao.me/publications/flash2/flash2.pdf) is currently a work in progress. + +It supports AMD's CDNA (MI200, MI300) and RDNA GPU's using fp16, bf16 and fp32 datatypes. + +These features are supported in Fwd and Bwd +1) Fwd and Bwd with causal masking +2) Variable sequence lengths +3) Arbitrary Q and KV sequence lengths +4) Arbitrary head sizes + +These features are supported in Fwd for now. We will add them to backward soon. +1) Multi and grouped query attention +2) ALiBi and matrix bias + +These features are in development +1) Paged Attention +2) Sliding Window +3) Rotary embeddings +4) Dropout +5) Performance Improvements + +#### Getting Started +To get started with the triton backend for AMD, follow the steps below. + +First install the recommended Triton [commit](https://github.com/triton-lang/triton/commit/3ca2f498e98ed7249b82722587c511a5610e00c4). + +``` +git clone https://github.com/triton-lang/triton +cd triton +git checkout 3ca2f498e98ed7249b82722587c511a5610e00c4 +pip install --verbose -e python +``` +Then install and test Flash Attention with the flag `FLASH_ATTENTION_TRITON_AMD_ENABLE` set to `"TRUE"`. + +``` +export FLASH_ATTENTION_TRITON_AMD_ENABLE="TRUE" +cd flash-attention +python setup.py install +pytest tests/test_flash_attn.py +``` + + +## How to use FlashAttention + +The main functions implement scaled dot product attention (softmax(Q @ K^T * +softmax_scale) @ V): +```python +from flash_attn import flash_attn_qkvpacked_func, flash_attn_func +``` + +```python +flash_attn_qkvpacked_func(qkv, dropout_p=0.0, softmax_scale=None, causal=False, + window_size=(-1, -1), alibi_slopes=None, deterministic=False): +"""dropout_p should be set to 0.0 during evaluation +If Q, K, V are already stacked into 1 tensor, this function will be faster than +calling flash_attn_func on Q, K, V since the backward pass avoids explicit concatenation +of the gradients of Q, K, V. +If window_size != (-1, -1), implements sliding window local attention. Query at position i +will only attend to keys between [i - window_size[0], i + window_size[1]] inclusive. +Arguments: + qkv: (batch_size, seqlen, 3, nheads, headdim) + dropout_p: float. Dropout probability. + softmax_scale: float. The scaling of QK^T before applying softmax. + Default to 1 / sqrt(headdim). + causal: bool. Whether to apply causal attention mask (e.g., for auto-regressive modeling). + window_size: (left, right). If not (-1, -1), implements sliding window local attention. + alibi_slopes: (nheads,) or (batch_size, nheads), fp32. A bias of (-alibi_slope * |i - j|) is added to + the attention score of query i and key j. + deterministic: bool. Whether to use the deterministic implementation of the backward pass, + which is slightly slower and uses more memory. The forward pass is always deterministic. +Return: + out: (batch_size, seqlen, nheads, headdim). +""" +``` + +```python +flash_attn_func(q, k, v, dropout_p=0.0, softmax_scale=None, causal=False, + window_size=(-1, -1), alibi_slopes=None, deterministic=False): +"""dropout_p should be set to 0.0 during evaluation +Supports multi-query and grouped-query attention (MQA/GQA) by passing in KV with fewer heads +than Q. Note that the number of heads in Q must be divisible by the number of heads in KV. +For example, if Q has 6 heads and K, V have 2 heads, head 0, 1, 2 of Q will attention to head +0 of K, V, and head 3, 4, 5 of Q will attention to head 1 of K, V. +If window_size != (-1, -1), implements sliding window local attention. Query at position i +will only attend to keys between +[i + seqlen_k - seqlen_q - window_size[0], i + seqlen_k - seqlen_q + window_size[1]] inclusive. + +Arguments: + q: (batch_size, seqlen, nheads, headdim) + k: (batch_size, seqlen, nheads_k, headdim) + v: (batch_size, seqlen, nheads_k, headdim) + dropout_p: float. Dropout probability. + softmax_scale: float. The scaling of QK^T before applying softmax. + Default to 1 / sqrt(headdim). + causal: bool. Whether to apply causal attention mask (e.g., for auto-regressive modeling). + window_size: (left, right). If not (-1, -1), implements sliding window local attention. + alibi_slopes: (nheads,) or (batch_size, nheads), fp32. A bias of + (-alibi_slope * |i + seqlen_k - seqlen_q - j|) + is added to the attention score of query i and key j. + deterministic: bool. Whether to use the deterministic implementation of the backward pass, + which is slightly slower and uses more memory. The forward pass is always deterministic. +Return: + out: (batch_size, seqlen, nheads, headdim). +""" +``` + +```python +def flash_attn_with_kvcache( + q, + k_cache, + v_cache, + k=None, + v=None, + rotary_cos=None, + rotary_sin=None, + cache_seqlens: Optional[Union[(int, torch.Tensor)]] = None, + cache_batch_idx: Optional[torch.Tensor] = None, + block_table: Optional[torch.Tensor] = None, + softmax_scale=None, + causal=False, + window_size=(-1, -1), # -1 means infinite context window + rotary_interleaved=True, + alibi_slopes=None, +): + """ + If k and v are not None, k_cache and v_cache will be updated *inplace* with the new values from + k and v. This is useful for incremental decoding: you can pass in the cached keys/values from + the previous step, and update them with the new keys/values from the current step, and do + attention with the updated cache, all in 1 kernel. + + If you pass in k / v, you must make sure that the cache is large enough to hold the new values. + For example, the KV cache could be pre-allocated with the max sequence length, and you can use + cache_seqlens to keep track of the current sequence lengths of each sequence in the batch. + + Also apply rotary embedding if rotary_cos and rotary_sin are passed in. The key @k will be + rotated by rotary_cos and rotary_sin at indices cache_seqlens, cache_seqlens + 1, etc. + If causal or local (i.e., window_size != (-1, -1)), the query @q will be rotated by rotary_cos + and rotary_sin at indices cache_seqlens, cache_seqlens + 1, etc. + If not causal and not local, the query @q will be rotated by rotary_cos and rotary_sin at + indices cache_seqlens only (i.e. we consider all tokens in @q to be at position cache_seqlens). + + See tests/test_flash_attn.py::test_flash_attn_kvcache for examples of how to use this function. + + Supports multi-query and grouped-query attention (MQA/GQA) by passing in KV with fewer heads + than Q. Note that the number of heads in Q must be divisible by the number of heads in KV. + For example, if Q has 6 heads and K, V have 2 heads, head 0, 1, 2 of Q will attention to head + 0 of K, V, and head 3, 4, 5 of Q will attention to head 1 of K, V. + + If causal=True, the causal mask is aligned to the bottom right corner of the attention matrix. + For example, if seqlen_q = 2 and seqlen_k = 5, the causal mask (1 = keep, 0 = masked out) is: + 1 1 1 1 0 + 1 1 1 1 1 + If seqlen_q = 5 and seqlen_k = 2, the causal mask is: + 0 0 + 0 0 + 0 0 + 1 0 + 1 1 + If the row of the mask is all zero, the output will be zero. + + If window_size != (-1, -1), implements sliding window local attention. Query at position i + will only attend to keys between + [i + seqlen_k - seqlen_q - window_size[0], i + seqlen_k - seqlen_q + window_size[1]] inclusive. + + Note: Does not support backward pass. + + Arguments: + q: (batch_size, seqlen, nheads, headdim) + k_cache: (batch_size_cache, seqlen_cache, nheads_k, headdim) if there's no block_table, + or (num_blocks, page_block_size, nheads_k, headdim) if there's a block_table (i.e. paged KV cache) + page_block_size must be a multiple of 256. + v_cache: (batch_size_cache, seqlen_cache, nheads_k, headdim) if there's no block_table, + or (num_blocks, page_block_size, nheads_k, headdim) if there's a block_table (i.e. paged KV cache) + k [optional]: (batch_size, seqlen_new, nheads_k, headdim). If not None, we concatenate + k with k_cache, starting at the indices specified by cache_seqlens. + v [optional]: (batch_size, seqlen_new, nheads_k, headdim). Similar to k. + rotary_cos [optional]: (seqlen_ro, rotary_dim / 2). If not None, we apply rotary embedding + to k and q. Only applicable if k and v are passed in. rotary_dim must be divisible by 16. + rotary_sin [optional]: (seqlen_ro, rotary_dim / 2). Similar to rotary_cos. + cache_seqlens: int, or (batch_size,), dtype torch.int32. The sequence lengths of the + KV cache. + block_table [optional]: (batch_size, max_num_blocks_per_seq), dtype torch.int32. + cache_batch_idx: (batch_size,), dtype torch.int32. The indices used to index into the KV cache. + If None, we assume that the batch indices are [0, 1, 2, ..., batch_size - 1]. + If the indices are not distinct, and k and v are provided, the values updated in the cache + might come from any of the duplicate indices. + softmax_scale: float. The scaling of QK^T before applying softmax. + Default to 1 / sqrt(headdim). + causal: bool. Whether to apply causal attention mask (e.g., for auto-regressive modeling). + window_size: (left, right). If not (-1, -1), implements sliding window local attention. + rotary_interleaved: bool. Only applicable if rotary_cos and rotary_sin are passed in. + If True, rotary embedding will combine dimensions 0 & 1, 2 & 3, etc. If False, + rotary embedding will combine dimensions 0 & rotary_dim / 2, 1 & rotary_dim / 2 + 1 + (i.e. GPT-NeoX style). + alibi_slopes: (nheads,) or (batch_size, nheads), fp32. A bias of + (-alibi_slope * |i + seqlen_k - seqlen_q - j|) + is added to the attention score of query i and key j. + + Return: + out: (batch_size, seqlen, nheads, headdim). + """ +``` + +To see how these functions are used in a multi-head attention layer (which +includes QKV projection, output projection), see the MHA [implementation](https://github.com/Dao-AILab/flash-attention/blob/main/flash_attn/modules/mha.py). + +## Changelog + +### 2.0: Complete rewrite, 2x faster +Upgrading from FlashAttention (1.x) to FlashAttention-2 + +These functions have been renamed: +- `flash_attn_unpadded_func` -> `flash_attn_varlen_func` +- `flash_attn_unpadded_qkvpacked_func` -> `flash_attn_varlen_qkvpacked_func` +- `flash_attn_unpadded_kvpacked_func` -> `flash_attn_varlen_kvpacked_func` + +If the inputs have the same sequence lengths in the same batch, it is simpler +and faster to use these functions: +```python +flash_attn_qkvpacked_func(qkv, dropout_p=0.0, softmax_scale=None, causal=False) +``` +```python +flash_attn_func(q, k, v, dropout_p=0.0, softmax_scale=None, causal=False) +``` +### 2.1: Change behavior of causal flag + +If seqlen_q != seqlen_k and causal=True, the causal mask is aligned to the +bottom right corner of the attention matrix, instead of the top-left corner. + +For example, if seqlen_q = 2 and seqlen_k = 5, the causal mask (1 = keep, 0 = +masked out) is: +v2.0: + 1 0 0 0 0 + 1 1 0 0 0 +v2.1: + 1 1 1 1 0 + 1 1 1 1 1 + +If seqlen_q = 5 and seqlen_k = 2, the causal mask is: +v2.0: + 1 0 + 1 1 + 1 1 + 1 1 + 1 1 +v2.1: + 0 0 + 0 0 + 0 0 + 1 0 + 1 1 +If the row of the mask is all zero, the output will be zero. + +### 2.2: Optimize for inference + +Optimize for inference (iterative decoding) when query has very small sequence +length (e.g., query sequence length = 1). The bottleneck here is to load KV +cache as fast as possible, and we split the loading across different thread +blocks, with a separate kernel to combine results. + +See the function `flash_attn_with_kvcache` with more features for inference +(perform rotary embedding, updating KV cache inplace). + +Thanks to the xformers team, and in particular Daniel Haziza, for this +collaboration. + +### 2.3: Local (i.e., sliding window) attention + +Implement sliding window attention (i.e., local attention). Thanks to [Mistral +AI](https://mistral.ai/) and in particular Timothée Lacroix for this +contribution. Sliding window was used in the [Mistral 7B](https://mistral.ai/news/announcing-mistral-7b/) model. + +### 2.4: ALiBi (attention with linear bias), deterministic backward pass. + +Implement ALiBi (Press et al., 2021). Thanks to Sanghun Cho from Kakao Brain for this contribution. + +Implement deterministic backward pass. Thanks to engineers from [Meituan](www.meituan.com) for this contribution. + +### 2.5: Paged KV cache. + +Support paged KV cache (i.e., [PagedAttention](https://arxiv.org/abs/2309.06180)). +Thanks to @beginlner for this contribution. + +### 2.6: Softcapping. + +Support attention with softcapping, as used in Gemma-2 and Grok models. +Thanks to @Narsil and @lucidrains for this contribution. + +### 2.7: Compatibility with torch compile + +Thanks to @ani300 for this contribution. + +## Performance + +We present expected speedup (combined forward + backward pass) and memory savings from using FlashAttention against PyTorch standard attention, depending on sequence length, on different GPUs (speedup depends on memory bandwidth - we see more speedup on slower GPU memory). + +We currently have benchmarks for these GPUs: +* [A100](#a100) +* [H100](#h100) + + + +### A100 + +We display FlashAttention speedup using these parameters: +* Head dimension 64 or 128, hidden dimension 2048 (i.e. either 32 or 16 heads). +* Sequence length 512, 1k, 2k, 4k, 8k, 16k. +* Batch size set to 16k / seqlen. + +#### Speedup + +![FlashAttention speedup on A100 80GB SXM5 with FP16/BF16](assets/flash2_a100_fwd_bwd_benchmark.png) + +#### Memory + +![FlashAttention memory](assets/flashattn_memory.jpg) + +We show memory savings in this graph (note that memory footprint is the same no matter if you use dropout or masking). +Memory savings are proportional to sequence length -- since standard attention has memory quadratic in sequence length, whereas FlashAttention has memory linear in sequence length. +We see 10X memory savings at sequence length 2K, and 20X at 4K. +As a result, FlashAttention can scale to much longer sequence lengths. + +### H100 + +![FlashAttention speedup on H100 SXM5 with FP16/BF16](assets/flash2_h100_fwd_bwd_benchmark.png) + +## Full model code and training script + +We have released the full GPT model +[implementation](https://github.com/Dao-AILab/flash-attention/blob/main/flash_attn/models/gpt.py). +We also provide optimized implementations of other layers (e.g., MLP, LayerNorm, +cross-entropy loss, rotary embedding). Overall this speeds up training by 3-5x +compared to the baseline implementation from Huggingface, reaching up to 225 +TFLOPs/sec per A100, equivalent to 72% model FLOPs utilization (we don't need +any activation checkpointing). + +We also include a training +[script](https://github.com/Dao-AILab/flash-attention/tree/main/training) to +train GPT2 on Openwebtext and GPT3 on The Pile. + +## Triton implementation of FlashAttention + +Phil Tillet (OpenAI) has an experimental implementation of FlashAttention in Triton: +https://github.com/openai/triton/blob/master/python/tutorials/06-fused-attention.py + +As Triton is a higher-level language than CUDA, it might be easier to understand +and experiment with. The notations in the Triton implementation are also closer +to what's used in our paper. + +We also have an experimental implementation in Triton that support attention +bias (e.g. ALiBi): +https://github.com/Dao-AILab/flash-attention/blob/main/flash_attn/flash_attn_triton.py + + +## Tests +We test that FlashAttention produces the same output and gradient as a reference +implementation, up to some numerical tolerance. In particular, we check that the +maximum numerical error of FlashAttention is at most twice the numerical error +of a baseline implementation in Pytorch (for different head dimensions, input +dtype, sequence length, causal / non-causal). + +To run the tests: +```sh +pytest -q -s tests/test_flash_attn.py +``` +## When you encounter issues + +This new release of FlashAttention-2 has been tested on several GPT-style +models, mostly on A100 GPUs. + +If you encounter bugs, please open a GitHub Issue! + +## Tests +To run the tests: +```sh +pytest tests/test_flash_attn_ck.py +``` + +## Citation +If you use this codebase, or otherwise found our work valuable, please cite: +``` +@inproceedings{dao2022flashattention, + title={Flash{A}ttention: Fast and Memory-Efficient Exact Attention with {IO}-Awareness}, + author={Dao, Tri and Fu, Daniel Y. and Ermon, Stefano and Rudra, Atri and R{\'e}, Christopher}, + booktitle={Advances in Neural Information Processing Systems (NeurIPS)}, + year={2022} +} +@inproceedings{dao2023flashattention2, + title={Flash{A}ttention-2: Faster Attention with Better Parallelism and Work Partitioning}, + author={Dao, Tri}, + booktitle={International Conference on Learning Representations (ICLR)}, + year={2024} +} +``` diff --git a/lib/python3.12/site-packages/flash_attn-2.7.4.post1.dist-info/RECORD b/lib/python3.12/site-packages/flash_attn-2.7.4.post1.dist-info/RECORD new file mode 100644 index 0000000000000000000000000000000000000000..a14ca71deda90851b048c7e3234192f377149253 --- /dev/null +++ b/lib/python3.12/site-packages/flash_attn-2.7.4.post1.dist-info/RECORD @@ -0,0 +1,149 @@ +flash_attn-2.7.4.post1.dist-info/AUTHORS,sha256=879BRIJqYoQbf5rrxQV_ddotMqZSpXPtxnJQ7JSjd6c,29 +flash_attn-2.7.4.post1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +flash_attn-2.7.4.post1.dist-info/LICENSE,sha256=jJzLlsBl5wYTW2y60nm3IdphVuUfOl8nxrMymvlBbXM,1558 +flash_attn-2.7.4.post1.dist-info/METADATA,sha256=Egn5Z0olqWbvZwoYmEVGP-dLl1r8vYvxBf4-GDl99hc,22617 +flash_attn-2.7.4.post1.dist-info/RECORD,, +flash_attn-2.7.4.post1.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +flash_attn-2.7.4.post1.dist-info/WHEEL,sha256=aDUArjEbIuDQzblE24IvdIsBzRhJEjiXmuNUYQ-gpaA,104 +flash_attn-2.7.4.post1.dist-info/top_level.txt,sha256=CAfUeAM8RDLF5nDjghmaJPNA5bOIUbYHfMXK0rvVjcw,36 +flash_attn/__init__.py,sha256=Gd5qri7WrOa9KaD6RcGHLacwKzCLwkYRRNMVLxooerg,291 +flash_attn/__pycache__/__init__.cpython-312.pyc,, +flash_attn/__pycache__/bert_padding.cpython-312.pyc,, +flash_attn/__pycache__/flash_attn_interface.cpython-312.pyc,, +flash_attn/__pycache__/flash_attn_triton.cpython-312.pyc,, +flash_attn/__pycache__/flash_attn_triton_og.cpython-312.pyc,, +flash_attn/__pycache__/flash_blocksparse_attention.cpython-312.pyc,, +flash_attn/__pycache__/flash_blocksparse_attn_interface.cpython-312.pyc,, +flash_attn/__pycache__/fused_softmax.cpython-312.pyc,, +flash_attn/bert_padding.py,sha256=gF1EmsdJ-HpQ86MRQ4VxDw-Sb_RVISdQALdNnoByHlw,9930 +flash_attn/flash_attn_interface.py,sha256=2Or5iaVAH-3SPRGjNqfzjmdZbQpNWied8Uy-LX3i7XU,60781 +flash_attn/flash_attn_triton.py,sha256=Du81zbh8Ls70ExEsm00opziGvjGFfcZCoZDUO2zut9Q,41112 +flash_attn/flash_attn_triton_amd/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +flash_attn/flash_attn_triton_amd/__pycache__/__init__.cpython-312.pyc,, +flash_attn/flash_attn_triton_amd/__pycache__/bench.cpython-312.pyc,, +flash_attn/flash_attn_triton_amd/__pycache__/bwd_prefill.cpython-312.pyc,, +flash_attn/flash_attn_triton_amd/__pycache__/bwd_ref.cpython-312.pyc,, +flash_attn/flash_attn_triton_amd/__pycache__/fwd_decode.cpython-312.pyc,, +flash_attn/flash_attn_triton_amd/__pycache__/fwd_prefill.cpython-312.pyc,, +flash_attn/flash_attn_triton_amd/__pycache__/fwd_ref.cpython-312.pyc,, +flash_attn/flash_attn_triton_amd/__pycache__/interface_fa.cpython-312.pyc,, +flash_attn/flash_attn_triton_amd/__pycache__/interface_torch.cpython-312.pyc,, +flash_attn/flash_attn_triton_amd/__pycache__/test.cpython-312.pyc,, +flash_attn/flash_attn_triton_amd/__pycache__/utils.cpython-312.pyc,, +flash_attn/flash_attn_triton_amd/bench.py,sha256=wIGZHcYI_Ria2BB6FBJ87GKRxzboRP1LOf2L__aPeA4,9837 +flash_attn/flash_attn_triton_amd/bwd_prefill.py,sha256=iCe1jLP5_osxzEGyMBpLUW9koGezp91A5tsI0o6OQQM,20269 +flash_attn/flash_attn_triton_amd/bwd_ref.py,sha256=BL2_4jYjRvUodX4GjgpvyQhVDgm2a6OcnVZJnozEz_A,9972 +flash_attn/flash_attn_triton_amd/fwd_decode.py,sha256=vrk6GQqo9Tp8-SWSUzREWftqyokI5BQlTA_AAdmbEYA,23435 +flash_attn/flash_attn_triton_amd/fwd_prefill.py,sha256=ynZUspV95iXQ0Ccld3jQ0ZxGuZHWrLWwysDL4FS71R0,32986 +flash_attn/flash_attn_triton_amd/fwd_ref.py,sha256=ORf0qC92d3FFqPe0l6VA0gMjc2bVlYwUwF-g4Ilq2ko,11362 +flash_attn/flash_attn_triton_amd/interface_fa.py,sha256=7NTNBEsr3RQfylD-0IrQXN_ECryEtR7BlvJFVPz-HDM,16292 +flash_attn/flash_attn_triton_amd/interface_torch.py,sha256=DU_iepQ4h5FFvYxM4qDR5eh76l55iUiCLXdGBsc6KYo,3308 +flash_attn/flash_attn_triton_amd/test.py,sha256=7jN9tQIRNJ_xpfPHrERU9aeG70OewtQKIYqyRFxed7o,30832 +flash_attn/flash_attn_triton_amd/utils.py,sha256=XoKPb0Zzrjbbn7kX_j3YIqqhDW0xIhX4EjzqYvwPmng,12247 +flash_attn/flash_attn_triton_og.py,sha256=LmvDju7LJG-wOYhoR6Zc2AmdPK2oWyB1VJpMjRhnWnE,11328 +flash_attn/flash_blocksparse_attention.py,sha256=gsdH9VtYaVcTcP1rzZYPy1V_wUqgdvVcsB1h4Mk7RGs,7472 +flash_attn/flash_blocksparse_attn_interface.py,sha256=2qK2KvVCt851_j8ZzHvjS-aMfdgVDu1yne67-iScWfo,7265 +flash_attn/fused_softmax.py,sha256=0-XbXo7R1a5h4-EpUzPy--lwlGytfTDW34WGM5nmBAY,7793 +flash_attn/layers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +flash_attn/layers/__pycache__/__init__.cpython-312.pyc,, +flash_attn/layers/__pycache__/patch_embed.cpython-312.pyc,, +flash_attn/layers/__pycache__/rotary.cpython-312.pyc,, +flash_attn/layers/patch_embed.py,sha256=H58CgME_qSOPTZLOG08wFgrQS1j34pvNwMPrkTj3Ek4,2136 +flash_attn/layers/rotary.py,sha256=MqsUZ-Gxa0OcYLtL8OsjHIOkqyTacQHkMGpqADa2e6Q,21239 +flash_attn/losses/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +flash_attn/losses/__pycache__/__init__.cpython-312.pyc,, +flash_attn/losses/__pycache__/cross_entropy.cpython-312.pyc,, +flash_attn/losses/cross_entropy.py,sha256=tj5IoeUZuSzA1_82UFr7o-1WuoHyKAc1gVS6fWzAbDQ,3197 +flash_attn/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +flash_attn/models/__pycache__/__init__.cpython-312.pyc,, +flash_attn/models/__pycache__/baichuan.cpython-312.pyc,, +flash_attn/models/__pycache__/bert.cpython-312.pyc,, +flash_attn/models/__pycache__/bigcode.cpython-312.pyc,, +flash_attn/models/__pycache__/btlm.cpython-312.pyc,, +flash_attn/models/__pycache__/falcon.cpython-312.pyc,, +flash_attn/models/__pycache__/gpt.cpython-312.pyc,, +flash_attn/models/__pycache__/gpt_neox.cpython-312.pyc,, +flash_attn/models/__pycache__/gptj.cpython-312.pyc,, +flash_attn/models/__pycache__/llama.cpython-312.pyc,, +flash_attn/models/__pycache__/opt.cpython-312.pyc,, +flash_attn/models/__pycache__/vit.cpython-312.pyc,, +flash_attn/models/baichuan.py,sha256=eFNWwoRQ02AIeQP0OoK8pNvYw0dqnHOshLigCQPkAEc,5730 +flash_attn/models/bert.py,sha256=dMM6-Pj814pgQdsKkgkwg_grNZ7snM2juSgoUB14R7Q,33232 +flash_attn/models/bigcode.py,sha256=mkYeItoJtmWVf2wKkUs5oXjwdbTdGSo5eHxi0-1maZ8,9383 +flash_attn/models/btlm.py,sha256=d8YDjYTa2G1DutYu-YuVf15S_Dn6oKn8-HzERoersLA,4631 +flash_attn/models/falcon.py,sha256=mA3wGv1a4zhbrUSlFNVVmTgVjiXc1sFTOi55eYpgSPo,6033 +flash_attn/models/gpt.py,sha256=QGBMCw_osxD4VMMj1uC6TMlXlM5lIInxSUKmq5J5kSU,47669 +flash_attn/models/gpt_neox.py,sha256=_704a9KQ2PcnID8uMV7yZ4ggjGlh1zZH5gszue6D1bI,5159 +flash_attn/models/gptj.py,sha256=k2eqMNyMbU7CJVM_BHBjlKt0ByFz6ITSETqS1mJa89g,4436 +flash_attn/models/llama.py,sha256=bDRI308iRpeJngZLrQlLTGYAmwYotqzUxnjBMirfn-k,16581 +flash_attn/models/opt.py,sha256=L0ZIWKpSP44lcEbiVCzVT9un_5gFMAW6cvnS3KHcb-A,5164 +flash_attn/models/vit.py,sha256=7i0WUI_jZvQ5TMoSKPPzf77ZcyMDfDJuQaINzXN_iQU,14074 +flash_attn/modules/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +flash_attn/modules/__pycache__/__init__.cpython-312.pyc,, +flash_attn/modules/__pycache__/block.cpython-312.pyc,, +flash_attn/modules/__pycache__/embedding.cpython-312.pyc,, +flash_attn/modules/__pycache__/mha.cpython-312.pyc,, +flash_attn/modules/__pycache__/mlp.cpython-312.pyc,, +flash_attn/modules/block.py,sha256=WLi7JKj9_Zpk89ppzC7WTIoykJJ7TLOJbUSZePNnW1E,17349 +flash_attn/modules/embedding.py,sha256=RCVeeiomlGNkLeQD8G6Udvex-NDI_xKD45hXjgZ2lbQ,8693 +flash_attn/modules/mha.py,sha256=V6Ynog9pb_G9UVxetRjXlmWGExZlxmJkYVwAExXqUEk,43297 +flash_attn/modules/mlp.py,sha256=G6KPQagfKq1DRn7hQRJ3OHznFJLZHj_PiidZE_zcLgg,6033 +flash_attn/ops/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +flash_attn/ops/__pycache__/__init__.cpython-312.pyc,, +flash_attn/ops/__pycache__/activations.cpython-312.pyc,, +flash_attn/ops/__pycache__/fused_dense.cpython-312.pyc,, +flash_attn/ops/__pycache__/layer_norm.cpython-312.pyc,, +flash_attn/ops/__pycache__/rms_norm.cpython-312.pyc,, +flash_attn/ops/activations.py,sha256=t5lzNg1In8LP6bKeTnyeMizwqjv27JGbJ6ylPdGvZYg,3939 +flash_attn/ops/fused_dense.py,sha256=ACJKqkIfxZibxI3nb5ycb3pXBKaL_CM63rUUyQYNAUE,27907 +flash_attn/ops/layer_norm.py,sha256=zr7NXIm-2mtEynTp1CS0fbFGI2Mqdp41dY4AfDWF6EQ,22443 +flash_attn/ops/rms_norm.py,sha256=XEnihcj0a4aSz4LO55m5iKGVn4HKTeKN8TIyHjuDgxI,3988 +flash_attn/ops/triton/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1 +flash_attn/ops/triton/__pycache__/__init__.cpython-312.pyc,, +flash_attn/ops/triton/__pycache__/cross_entropy.cpython-312.pyc,, +flash_attn/ops/triton/__pycache__/k_activations.cpython-312.pyc,, +flash_attn/ops/triton/__pycache__/layer_norm.cpython-312.pyc,, +flash_attn/ops/triton/__pycache__/linear.cpython-312.pyc,, +flash_attn/ops/triton/__pycache__/mlp.cpython-312.pyc,, +flash_attn/ops/triton/__pycache__/rotary.cpython-312.pyc,, +flash_attn/ops/triton/cross_entropy.py,sha256=hjSfLhv4cKt-N8hTfpgkGMFdxhs8B4II6VIkEtck8EM,12845 +flash_attn/ops/triton/k_activations.py,sha256=-Z3vIyO4JkqBMipKsPvhzmxljtBdIhJCsl_M-_ESqBo,4034 +flash_attn/ops/triton/layer_norm.py,sha256=rNJwuijsZ6sKDtKHlkbT0qDzbi6vetVjjibpy9YRHFQ,35715 +flash_attn/ops/triton/linear.py,sha256=OtRvKz8xdpl-7v3q_ZTaS9fdBt9XrzMyapgRr50uBbM,20841 +flash_attn/ops/triton/mlp.py,sha256=_5lbZJFZg_pXeXYITGt4V_6LkB_yddClB_jt-diCOdw,6068 +flash_attn/ops/triton/rotary.py,sha256=WH7tELBLZ23znuxnYUAzP7YWqwMXJmRgUQ8B64Vjdn4,8583 +flash_attn/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +flash_attn/utils/__pycache__/__init__.cpython-312.pyc,, +flash_attn/utils/__pycache__/benchmark.cpython-312.pyc,, +flash_attn/utils/__pycache__/distributed.cpython-312.pyc,, +flash_attn/utils/__pycache__/generation.cpython-312.pyc,, +flash_attn/utils/__pycache__/pretrained.cpython-312.pyc,, +flash_attn/utils/benchmark.py,sha256=JDtzdVhFyMIQqs3edbcXdXnmDf-O7RVpmZmn2ZFCvI0,7369 +flash_attn/utils/distributed.py,sha256=qhcybRXtslssuV9LYaQy37haPaPtklM4YUMDx9UvnnQ,5825 +flash_attn/utils/generation.py,sha256=9IVPvkf_hlbsxCWgECUA03293qHxXzWDFCTAOdqbAVo,30694 +flash_attn/utils/pretrained.py,sha256=VZ6qk90sBJA7M86gRzPsNc_CkQXkj5HyrJvwl0I355k,3246 +flash_attn_2_cuda.cpython-312-x86_64-linux-gnu.so,sha256=1uZvW8kceC51hh3aO-S0KKHpHrYWhY1QVy46MKl6Buo,603679192 +hopper/__init__.py,sha256=tFIhpQJfQnBMRzNaynKKbeNwvSPrBsV5DFEN9zDcGqc,25 +hopper/__pycache__/__init__.cpython-312.pyc,, +hopper/__pycache__/benchmark_attn.cpython-312.pyc,, +hopper/__pycache__/benchmark_flash_attention_fp8.cpython-312.pyc,, +hopper/__pycache__/benchmark_split_kv.cpython-312.pyc,, +hopper/__pycache__/flash_attn_interface.cpython-312.pyc,, +hopper/__pycache__/generate_kernels.cpython-312.pyc,, +hopper/__pycache__/padding.cpython-312.pyc,, +hopper/__pycache__/setup.cpython-312.pyc,, +hopper/__pycache__/test_attn_kvcache.cpython-312.pyc,, +hopper/__pycache__/test_flash_attn.cpython-312.pyc,, +hopper/__pycache__/test_kvcache.cpython-312.pyc,, +hopper/__pycache__/test_util.cpython-312.pyc,, +hopper/benchmark_attn.py,sha256=bXURzEauK79WJ10XeHlFX9I5ETISolUXddztvTzhNzs,21721 +hopper/benchmark_flash_attention_fp8.py,sha256=42sXQefWHnrL0dqdaal3wQUKqa8_fIAt7sS5u2qmjgo,13271 +hopper/benchmark_split_kv.py,sha256=A8boLlJuhCDhRxz3leVaBEwjIgxGeJAqJYoGp4mpdR4,13183 +hopper/flash_attn_interface.py,sha256=pQ9WT6H8TN2F_6YfeekTpGPCBm5Rn5xei8Llx0RX-jg,26902 +hopper/generate_kernels.py,sha256=AgdeDyd9UgbHEdB-IU0yavzSkowUVzQatzhw0tYWrzk,8891 +hopper/padding.py,sha256=gokls1Xz2QGUPqm9WMR2WG2IQ2PbtjdVpJUPN6ZpVVk,2681 +hopper/setup.py,sha256=wHRQgP24M08kgPborr7yz1E-9U2lECXFrKkU_4S-C8k,28511 +hopper/test_attn_kvcache.py,sha256=IDeHeyWpnb659RZgnY3seo1hZBLmrouNSdUFLgeeOMA,19539 +hopper/test_flash_attn.py,sha256=4wFeR15FOj4GYiuAKTdyo4l9ivYcFAuz7pNtbd0g30k,45019 +hopper/test_kvcache.py,sha256=R7sk9mdth2R-7BUstzPk9N4BI9cS2JNb1oJgF-uxGXQ,7612 +hopper/test_util.py,sha256=fvYBtjpzAuf8jJP1akzS0Qq4mhV0SnweosyA8mf9H6I,11314 diff --git a/lib/python3.12/site-packages/flash_attn-2.7.4.post1.dist-info/REQUESTED b/lib/python3.12/site-packages/flash_attn-2.7.4.post1.dist-info/REQUESTED new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/lib/python3.12/site-packages/flash_attn-2.7.4.post1.dist-info/WHEEL b/lib/python3.12/site-packages/flash_attn-2.7.4.post1.dist-info/WHEEL new file mode 100644 index 0000000000000000000000000000000000000000..5b87859d16e8f3962c98089aebacbb6a0a0c0759 --- /dev/null +++ b/lib/python3.12/site-packages/flash_attn-2.7.4.post1.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: setuptools (75.8.0) +Root-Is-Purelib: false +Tag: cp312-cp312-linux_x86_64 + diff --git a/lib/python3.12/site-packages/flash_attn-2.7.4.post1.dist-info/top_level.txt b/lib/python3.12/site-packages/flash_attn-2.7.4.post1.dist-info/top_level.txt new file mode 100644 index 0000000000000000000000000000000000000000..f96246cc319a2b0232155dc96f7e6e1e3b7622f4 --- /dev/null +++ b/lib/python3.12/site-packages/flash_attn-2.7.4.post1.dist-info/top_level.txt @@ -0,0 +1,3 @@ +flash_attn +flash_attn_2_cuda +hopper diff --git a/lib/python3.12/site-packages/huggingface_hub-0.31.4.dist-info/INSTALLER b/lib/python3.12/site-packages/huggingface_hub-0.31.4.dist-info/INSTALLER new file mode 100644 index 0000000000000000000000000000000000000000..a1b589e38a32041e49332e5e81c2d363dc418d68 --- /dev/null +++ b/lib/python3.12/site-packages/huggingface_hub-0.31.4.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/lib/python3.12/site-packages/huggingface_hub-0.31.4.dist-info/LICENSE b/lib/python3.12/site-packages/huggingface_hub-0.31.4.dist-info/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..261eeb9e9f8b2b4b0d119366dda99c6fd7d35c64 --- /dev/null +++ b/lib/python3.12/site-packages/huggingface_hub-0.31.4.dist-info/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/lib/python3.12/site-packages/huggingface_hub-0.31.4.dist-info/METADATA b/lib/python3.12/site-packages/huggingface_hub-0.31.4.dist-info/METADATA new file mode 100644 index 0000000000000000000000000000000000000000..487bdbe6218946f7a432bc594c6d820128756518 --- /dev/null +++ b/lib/python3.12/site-packages/huggingface_hub-0.31.4.dist-info/METADATA @@ -0,0 +1,310 @@ +Metadata-Version: 2.1 +Name: huggingface-hub +Version: 0.31.4 +Summary: Client library to download and publish models, datasets and other repos on the huggingface.co hub +Home-page: https://github.com/huggingface/huggingface_hub +Author: Hugging Face, Inc. +Author-email: julien@huggingface.co +License: Apache +Keywords: model-hub machine-learning models natural-language-processing deep-learning pytorch pretrained-models +Platform: UNKNOWN +Classifier: Intended Audience :: Developers +Classifier: Intended Audience :: Education +Classifier: Intended Audience :: Science/Research +Classifier: License :: OSI Approved :: Apache Software License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +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: Topic :: Scientific/Engineering :: Artificial Intelligence +Requires-Python: >=3.8.0 +Description-Content-Type: text/markdown +License-File: LICENSE +Requires-Dist: filelock +Requires-Dist: fsspec>=2023.5.0 +Requires-Dist: packaging>=20.9 +Requires-Dist: pyyaml>=5.1 +Requires-Dist: requests +Requires-Dist: tqdm>=4.42.1 +Requires-Dist: typing-extensions>=3.7.4.3 +Provides-Extra: all +Requires-Dist: InquirerPy==0.3.4; extra == "all" +Requires-Dist: aiohttp; extra == "all" +Requires-Dist: jedi; extra == "all" +Requires-Dist: Jinja2; extra == "all" +Requires-Dist: pytest<8.2.2,>=8.1.1; extra == "all" +Requires-Dist: pytest-cov; extra == "all" +Requires-Dist: pytest-env; extra == "all" +Requires-Dist: pytest-xdist; extra == "all" +Requires-Dist: pytest-vcr; extra == "all" +Requires-Dist: pytest-asyncio; extra == "all" +Requires-Dist: pytest-rerunfailures; extra == "all" +Requires-Dist: pytest-mock; extra == "all" +Requires-Dist: urllib3<2.0; extra == "all" +Requires-Dist: soundfile; extra == "all" +Requires-Dist: Pillow; extra == "all" +Requires-Dist: gradio>=4.0.0; extra == "all" +Requires-Dist: numpy; extra == "all" +Requires-Dist: fastapi; extra == "all" +Requires-Dist: ruff>=0.9.0; extra == "all" +Requires-Dist: mypy==1.5.1; extra == "all" +Requires-Dist: libcst==1.4.0; extra == "all" +Requires-Dist: typing-extensions>=4.8.0; extra == "all" +Requires-Dist: types-PyYAML; extra == "all" +Requires-Dist: types-requests; extra == "all" +Requires-Dist: types-simplejson; extra == "all" +Requires-Dist: types-toml; extra == "all" +Requires-Dist: types-tqdm; extra == "all" +Requires-Dist: types-urllib3; extra == "all" +Provides-Extra: cli +Requires-Dist: InquirerPy==0.3.4; extra == "cli" +Provides-Extra: dev +Requires-Dist: InquirerPy==0.3.4; extra == "dev" +Requires-Dist: aiohttp; extra == "dev" +Requires-Dist: jedi; extra == "dev" +Requires-Dist: Jinja2; extra == "dev" +Requires-Dist: pytest<8.2.2,>=8.1.1; extra == "dev" +Requires-Dist: pytest-cov; extra == "dev" +Requires-Dist: pytest-env; extra == "dev" +Requires-Dist: pytest-xdist; extra == "dev" +Requires-Dist: pytest-vcr; extra == "dev" +Requires-Dist: pytest-asyncio; extra == "dev" +Requires-Dist: pytest-rerunfailures; extra == "dev" +Requires-Dist: pytest-mock; extra == "dev" +Requires-Dist: urllib3<2.0; extra == "dev" +Requires-Dist: soundfile; extra == "dev" +Requires-Dist: Pillow; extra == "dev" +Requires-Dist: gradio>=4.0.0; extra == "dev" +Requires-Dist: numpy; extra == "dev" +Requires-Dist: fastapi; extra == "dev" +Requires-Dist: ruff>=0.9.0; extra == "dev" +Requires-Dist: mypy==1.5.1; extra == "dev" +Requires-Dist: libcst==1.4.0; extra == "dev" +Requires-Dist: typing-extensions>=4.8.0; extra == "dev" +Requires-Dist: types-PyYAML; extra == "dev" +Requires-Dist: types-requests; extra == "dev" +Requires-Dist: types-simplejson; extra == "dev" +Requires-Dist: types-toml; extra == "dev" +Requires-Dist: types-tqdm; extra == "dev" +Requires-Dist: types-urllib3; extra == "dev" +Provides-Extra: fastai +Requires-Dist: toml; extra == "fastai" +Requires-Dist: fastai>=2.4; extra == "fastai" +Requires-Dist: fastcore>=1.3.27; extra == "fastai" +Provides-Extra: hf_transfer +Requires-Dist: hf-transfer>=0.1.4; extra == "hf-transfer" +Provides-Extra: hf_xet +Requires-Dist: hf-xet<2.0.0,>=1.1.1; extra == "hf-xet" +Provides-Extra: inference +Requires-Dist: aiohttp; extra == "inference" +Provides-Extra: quality +Requires-Dist: ruff>=0.9.0; extra == "quality" +Requires-Dist: mypy==1.5.1; extra == "quality" +Requires-Dist: libcst==1.4.0; extra == "quality" +Provides-Extra: tensorflow +Requires-Dist: tensorflow; extra == "tensorflow" +Requires-Dist: pydot; extra == "tensorflow" +Requires-Dist: graphviz; extra == "tensorflow" +Provides-Extra: tensorflow-testing +Requires-Dist: tensorflow; extra == "tensorflow-testing" +Requires-Dist: keras<3.0; extra == "tensorflow-testing" +Provides-Extra: testing +Requires-Dist: InquirerPy==0.3.4; extra == "testing" +Requires-Dist: aiohttp; extra == "testing" +Requires-Dist: jedi; extra == "testing" +Requires-Dist: Jinja2; extra == "testing" +Requires-Dist: pytest<8.2.2,>=8.1.1; extra == "testing" +Requires-Dist: pytest-cov; extra == "testing" +Requires-Dist: pytest-env; extra == "testing" +Requires-Dist: pytest-xdist; extra == "testing" +Requires-Dist: pytest-vcr; extra == "testing" +Requires-Dist: pytest-asyncio; extra == "testing" +Requires-Dist: pytest-rerunfailures; extra == "testing" +Requires-Dist: pytest-mock; extra == "testing" +Requires-Dist: urllib3<2.0; extra == "testing" +Requires-Dist: soundfile; extra == "testing" +Requires-Dist: Pillow; extra == "testing" +Requires-Dist: gradio>=4.0.0; extra == "testing" +Requires-Dist: numpy; extra == "testing" +Requires-Dist: fastapi; extra == "testing" +Provides-Extra: torch +Requires-Dist: torch; extra == "torch" +Requires-Dist: safetensors[torch]; extra == "torch" +Provides-Extra: typing +Requires-Dist: typing-extensions>=4.8.0; extra == "typing" +Requires-Dist: types-PyYAML; extra == "typing" +Requires-Dist: types-requests; extra == "typing" +Requires-Dist: types-simplejson; extra == "typing" +Requires-Dist: types-toml; extra == "typing" +Requires-Dist: types-tqdm; extra == "typing" +Requires-Dist: types-urllib3; extra == "typing" + +

+ + + + huggingface_hub library logo + +
+
+

+ +

+ The official Python client for the Huggingface Hub. +

+ +

+ Documentation + GitHub release + PyPi version + PyPI - Downloads + Code coverage +

+ +

+

+ English | + Deutsch | + हिंदी | + 한국어 | + 中文(简体) +

+

+ +--- + +**Documentation**: https://hf.co/docs/huggingface_hub + +**Source Code**: https://github.com/huggingface/huggingface_hub + +--- + +## Welcome to the huggingface_hub library + +The `huggingface_hub` library allows you to interact with the [Hugging Face Hub](https://huggingface.co/), a platform democratizing open-source Machine Learning for creators and collaborators. Discover pre-trained models and datasets for your projects or play with the thousands of machine learning apps hosted on the Hub. You can also create and share your own models, datasets and demos with the community. The `huggingface_hub` library provides a simple way to do all these things with Python. + +## Key features + +- [Download files](https://huggingface.co/docs/huggingface_hub/en/guides/download) from the Hub. +- [Upload files](https://huggingface.co/docs/huggingface_hub/en/guides/upload) to the Hub. +- [Manage your repositories](https://huggingface.co/docs/huggingface_hub/en/guides/repository). +- [Run Inference](https://huggingface.co/docs/huggingface_hub/en/guides/inference) on deployed models. +- [Search](https://huggingface.co/docs/huggingface_hub/en/guides/search) for models, datasets and Spaces. +- [Share Model Cards](https://huggingface.co/docs/huggingface_hub/en/guides/model-cards) to document your models. +- [Engage with the community](https://huggingface.co/docs/huggingface_hub/en/guides/community) through PRs and comments. + +## Installation + +Install the `huggingface_hub` package with [pip](https://pypi.org/project/huggingface-hub/): + +```bash +pip install huggingface_hub +``` + +If you prefer, you can also install it with [conda](https://huggingface.co/docs/huggingface_hub/en/installation#install-with-conda). + +In order to keep the package minimal by default, `huggingface_hub` comes with optional dependencies useful for some use cases. For example, if you want have a complete experience for Inference, run: + +```bash +pip install huggingface_hub[inference] +``` + +To learn more installation and optional dependencies, check out the [installation guide](https://huggingface.co/docs/huggingface_hub/en/installation). + +## Quick start + +### Download files + +Download a single file + +```py +from huggingface_hub import hf_hub_download + +hf_hub_download(repo_id="tiiuae/falcon-7b-instruct", filename="config.json") +``` + +Or an entire repository + +```py +from huggingface_hub import snapshot_download + +snapshot_download("stabilityai/stable-diffusion-2-1") +``` + +Files will be downloaded in a local cache folder. More details in [this guide](https://huggingface.co/docs/huggingface_hub/en/guides/manage-cache). + +### Login + +The Hugging Face Hub uses tokens to authenticate applications (see [docs](https://huggingface.co/docs/hub/security-tokens)). To log in your machine, run the following CLI: + +```bash +huggingface-cli login +# or using an environment variable +huggingface-cli login --token $HUGGINGFACE_TOKEN +``` + +### Create a repository + +```py +from huggingface_hub import create_repo + +create_repo(repo_id="super-cool-model") +``` + +### Upload files + +Upload a single file + +```py +from huggingface_hub import upload_file + +upload_file( + path_or_fileobj="/home/lysandre/dummy-test/README.md", + path_in_repo="README.md", + repo_id="lysandre/test-model", +) +``` + +Or an entire folder + +```py +from huggingface_hub import upload_folder + +upload_folder( + folder_path="/path/to/local/space", + repo_id="username/my-cool-space", + repo_type="space", +) +``` + +For details in the [upload guide](https://huggingface.co/docs/huggingface_hub/en/guides/upload). + +## Integrating to the Hub. + +We're partnering with cool open source ML libraries to provide free model hosting and versioning. You can find the existing integrations [here](https://huggingface.co/docs/hub/libraries). + +The advantages are: + +- Free model or dataset hosting for libraries and their users. +- Built-in file versioning, even with very large files, thanks to a git-based approach. +- Serverless inference API for all models publicly available. +- In-browser widgets to play with the uploaded models. +- Anyone can upload a new model for your library, they just need to add the corresponding tag for the model to be discoverable. +- Fast downloads! We use Cloudfront (a CDN) to geo-replicate downloads so they're blazing fast from anywhere on the globe. +- Usage stats and more features to come. + +If you would like to integrate your library, feel free to open an issue to begin the discussion. We wrote a [step-by-step guide](https://huggingface.co/docs/hub/adding-a-library) with ❤️ showing how to do this integration. + +## Contributions (feature requests, bugs, etc.) are super welcome 💙💚💛💜🧡❤️ + +Everyone is welcome to contribute, and we value everybody's contribution. Code is not the only way to help the community. +Answering questions, helping others, reaching out and improving the documentations are immensely valuable to the community. +We wrote a [contribution guide](https://github.com/huggingface/huggingface_hub/blob/main/CONTRIBUTING.md) to summarize +how to get started to contribute to this repository. + + diff --git a/lib/python3.12/site-packages/huggingface_hub-0.31.4.dist-info/RECORD b/lib/python3.12/site-packages/huggingface_hub-0.31.4.dist-info/RECORD new file mode 100644 index 0000000000000000000000000000000000000000..60d50e79e6b42b1e089a0ae69012655bdb286689 --- /dev/null +++ b/lib/python3.12/site-packages/huggingface_hub-0.31.4.dist-info/RECORD @@ -0,0 +1,266 @@ +../../../bin/huggingface-cli,sha256=hZRmroX_jaIvduxyQMT8QWqTBMi1HLZ4RIuHMC28P_Y,275 +huggingface_hub-0.31.4.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +huggingface_hub-0.31.4.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357 +huggingface_hub-0.31.4.dist-info/METADATA,sha256=naPG2RYybD8kKAtDWELKWHNXZteaBG1RNXe-X9Jt98A,13558 +huggingface_hub-0.31.4.dist-info/RECORD,, +huggingface_hub-0.31.4.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +huggingface_hub-0.31.4.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92 +huggingface_hub-0.31.4.dist-info/entry_points.txt,sha256=Y3Z2L02rBG7va_iE6RPXolIgwOdwUFONyRN3kXMxZ0g,131 +huggingface_hub-0.31.4.dist-info/top_level.txt,sha256=8KzlQJAY4miUvjAssOAJodqKOw3harNzuiwGQ9qLSSk,16 +huggingface_hub/__init__.py,sha256=2VWIBxiEg99DVr7A7njOdsnJPgm3Y1Jb_4YePTfA1dk,49368 +huggingface_hub/__pycache__/__init__.cpython-312.pyc,, +huggingface_hub/__pycache__/_commit_api.cpython-312.pyc,, +huggingface_hub/__pycache__/_commit_scheduler.cpython-312.pyc,, +huggingface_hub/__pycache__/_inference_endpoints.cpython-312.pyc,, +huggingface_hub/__pycache__/_local_folder.cpython-312.pyc,, +huggingface_hub/__pycache__/_login.cpython-312.pyc,, +huggingface_hub/__pycache__/_snapshot_download.cpython-312.pyc,, +huggingface_hub/__pycache__/_space_api.cpython-312.pyc,, +huggingface_hub/__pycache__/_tensorboard_logger.cpython-312.pyc,, +huggingface_hub/__pycache__/_upload_large_folder.cpython-312.pyc,, +huggingface_hub/__pycache__/_webhooks_payload.cpython-312.pyc,, +huggingface_hub/__pycache__/_webhooks_server.cpython-312.pyc,, +huggingface_hub/__pycache__/community.cpython-312.pyc,, +huggingface_hub/__pycache__/constants.cpython-312.pyc,, +huggingface_hub/__pycache__/dataclasses.cpython-312.pyc,, +huggingface_hub/__pycache__/errors.cpython-312.pyc,, +huggingface_hub/__pycache__/fastai_utils.cpython-312.pyc,, +huggingface_hub/__pycache__/file_download.cpython-312.pyc,, +huggingface_hub/__pycache__/hf_api.cpython-312.pyc,, +huggingface_hub/__pycache__/hf_file_system.cpython-312.pyc,, +huggingface_hub/__pycache__/hub_mixin.cpython-312.pyc,, +huggingface_hub/__pycache__/inference_api.cpython-312.pyc,, +huggingface_hub/__pycache__/keras_mixin.cpython-312.pyc,, +huggingface_hub/__pycache__/lfs.cpython-312.pyc,, +huggingface_hub/__pycache__/repocard.cpython-312.pyc,, +huggingface_hub/__pycache__/repocard_data.cpython-312.pyc,, +huggingface_hub/__pycache__/repository.cpython-312.pyc,, +huggingface_hub/_commit_api.py,sha256=ZbmuIhFdF8B3F_cvGtxorka7MmIQOk8oBkCtYltnCvI,39456 +huggingface_hub/_commit_scheduler.py,sha256=tfIoO1xWHjTJ6qy6VS6HIoymDycFPg0d6pBSZprrU2U,14679 +huggingface_hub/_inference_endpoints.py,sha256=qXR0utAYRaEWTI8EXzAsDpVDcYpp8bJPEBbcOxRS52E,17413 +huggingface_hub/_local_folder.py,sha256=ScpCJUITFC0LMkiebyaGiBhAU6fvQK8w7pVV6L8rhmc,16575 +huggingface_hub/_login.py,sha256=ssf4viT5BhHI2ZidnSuAZcrwSxzaLOrf8xgRVKuvu_A,20298 +huggingface_hub/_snapshot_download.py,sha256=oL2TgO0RpH_KJOQpKF-ttvPRDldeFc7JYvBPktXb_ps,15015 +huggingface_hub/_space_api.py,sha256=jb6rF8qLtjaNU12D-8ygAPM26xDiHCu8CHXHowhGTmg,5470 +huggingface_hub/_tensorboard_logger.py,sha256=ZkYcAUiRC8RGL214QUYtp58O8G5tn-HF6DCWha9imcA,8358 +huggingface_hub/_upload_large_folder.py,sha256=mDKZv7MIieKlTCbTv0jccHIM4smCqK-Y0ZDfMXsXTqo,24685 +huggingface_hub/_webhooks_payload.py,sha256=Xm3KaK7tCOGBlXkuZvbym6zjHXrT1XCrbUFWuXiBmNY,3617 +huggingface_hub/_webhooks_server.py,sha256=5J63wk9MUGKBNJVsOD9i60mJ-VMp0YYmlf87vQsl-L8,15767 +huggingface_hub/commands/__init__.py,sha256=AkbM2a-iGh0Vq_xAWhK3mu3uZ44km8-X5uWjKcvcrUQ,928 +huggingface_hub/commands/__pycache__/__init__.cpython-312.pyc,, +huggingface_hub/commands/__pycache__/_cli_utils.cpython-312.pyc,, +huggingface_hub/commands/__pycache__/delete_cache.cpython-312.pyc,, +huggingface_hub/commands/__pycache__/download.cpython-312.pyc,, +huggingface_hub/commands/__pycache__/env.cpython-312.pyc,, +huggingface_hub/commands/__pycache__/huggingface_cli.cpython-312.pyc,, +huggingface_hub/commands/__pycache__/lfs.cpython-312.pyc,, +huggingface_hub/commands/__pycache__/repo_files.cpython-312.pyc,, +huggingface_hub/commands/__pycache__/scan_cache.cpython-312.pyc,, +huggingface_hub/commands/__pycache__/tag.cpython-312.pyc,, +huggingface_hub/commands/__pycache__/upload.cpython-312.pyc,, +huggingface_hub/commands/__pycache__/upload_large_folder.cpython-312.pyc,, +huggingface_hub/commands/__pycache__/user.cpython-312.pyc,, +huggingface_hub/commands/__pycache__/version.cpython-312.pyc,, +huggingface_hub/commands/_cli_utils.py,sha256=Nt6CjbkYqQQRuh70bUXVA6rZpbZt_Sa1WqBUxjQLu6g,2095 +huggingface_hub/commands/delete_cache.py,sha256=6UahqCex_3qyxDltn4GcaiuwzxfInPlXCK10o33eZVU,17623 +huggingface_hub/commands/download.py,sha256=1YXKttB8YBX7SJ0Jxg0t1n8yp2BUZXtY0ck6DhCg-XE,8183 +huggingface_hub/commands/env.py,sha256=yYl4DSS14V8t244nAi0t77Izx5LIdgS_dy6xiV5VQME,1226 +huggingface_hub/commands/huggingface_cli.py,sha256=ZwW_nwgppyj-GA6iM3mgmbXMZ63bgtpGl_yIQDyWS4A,2414 +huggingface_hub/commands/lfs.py,sha256=xdbnNRO04UuQemEhUGT809jFgQn9Rj-SnyT_0Ph-VYg,7342 +huggingface_hub/commands/repo_files.py,sha256=Nfv8TjuaZVOrj7TZjrojtjdD8Wf54aZvYPDEOevh7tA,4923 +huggingface_hub/commands/scan_cache.py,sha256=xdD_zRKd49hRuATyptG-zaY08h1f9CAjB5zZBKe0YEo,8563 +huggingface_hub/commands/tag.py,sha256=0LNQZyK-WKi0VIL9i1xWzKxJ1ILw1jxMF_E6t2weJss,6288 +huggingface_hub/commands/upload.py,sha256=3mcBBo2pNO99NHzNu6o-VcEHjDp7mtyQYeKE9eVao0w,14453 +huggingface_hub/commands/upload_large_folder.py,sha256=P-EO44JWVl39Ax4b0E0Z873d0a6S38Qas8P6DaL1EwI,6129 +huggingface_hub/commands/user.py,sha256=M6Ef045YcyV4mFCbLaTRPciQDC6xtV9MMheeen69D0E,11168 +huggingface_hub/commands/version.py,sha256=vfCJn7GO1m-DtDmbdsty8_RTVtnZ7lX6MJsx0Bf4e-s,1266 +huggingface_hub/community.py,sha256=4MtcoxEI9_0lmmilBEnvUEi8_O1Ivfa8p6eKxYU5-ts,12198 +huggingface_hub/constants.py,sha256=ZYu0fEhPhrs12BVCZ_ygCdVvil6Sz4DLq9oMFsDkzNg,9766 +huggingface_hub/dataclasses.py,sha256=gegl8I9N8SbGoCUAVbQejQ6CNImwzYbbDwlua5m-PH8,17170 +huggingface_hub/errors.py,sha256=D7Lw0Jjrf8vfmD0B26LEvg-JWkU8Zq0KDPJOzFY4QLw,11201 +huggingface_hub/fastai_utils.py,sha256=DpeH9d-6ut2k_nCAAwglM51XmRmgfbRe2SPifpVL5Yk,16745 +huggingface_hub/file_download.py,sha256=Kh7Lg7C-Zn2U-zhF_mLw75ifiM28b9lSIODPvO4hGlA,78453 +huggingface_hub/hf_api.py,sha256=RB9LjZjncC9Ox1TZPuYUL_X8-YnDhzWdg3gP0oNenb4,441435 +huggingface_hub/hf_file_system.py,sha256=U6IY_QLNzZfvpsbvKEiakOBS2U6cduZw5t0x8wBPUn4,47531 +huggingface_hub/hub_mixin.py,sha256=fdAhdDujpUBZPUB6AfzzMRBeQ_Ua9tgQkhHE_ao5n2k,38062 +huggingface_hub/inference/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +huggingface_hub/inference/__pycache__/__init__.cpython-312.pyc,, +huggingface_hub/inference/__pycache__/_client.cpython-312.pyc,, +huggingface_hub/inference/__pycache__/_common.cpython-312.pyc,, +huggingface_hub/inference/_client.py,sha256=Wb3ZKdJpNCCwN80mOZF3RK5eLBq6FWW1_vgYDVuAN9M,161386 +huggingface_hub/inference/_common.py,sha256=iwCkq2fWE1MVoPTeeXN7UN5FZi7g5fZ3K8PHSOCi5dU,14591 +huggingface_hub/inference/_generated/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +huggingface_hub/inference/_generated/__pycache__/__init__.cpython-312.pyc,, +huggingface_hub/inference/_generated/__pycache__/_async_client.cpython-312.pyc,, +huggingface_hub/inference/_generated/_async_client.py,sha256=lFukg5o-hOr2EurwsLYTyKElo9N71y6ekyh2ODZcIdM,167546 +huggingface_hub/inference/_generated/types/__init__.py,sha256=--r3nBmBRtgyQR9TkdQl53_crcKYJhWfTkFK2VE2gUk,6307 +huggingface_hub/inference/_generated/types/__pycache__/__init__.cpython-312.pyc,, +huggingface_hub/inference/_generated/types/__pycache__/audio_classification.cpython-312.pyc,, +huggingface_hub/inference/_generated/types/__pycache__/audio_to_audio.cpython-312.pyc,, +huggingface_hub/inference/_generated/types/__pycache__/automatic_speech_recognition.cpython-312.pyc,, +huggingface_hub/inference/_generated/types/__pycache__/base.cpython-312.pyc,, +huggingface_hub/inference/_generated/types/__pycache__/chat_completion.cpython-312.pyc,, +huggingface_hub/inference/_generated/types/__pycache__/depth_estimation.cpython-312.pyc,, +huggingface_hub/inference/_generated/types/__pycache__/document_question_answering.cpython-312.pyc,, +huggingface_hub/inference/_generated/types/__pycache__/feature_extraction.cpython-312.pyc,, +huggingface_hub/inference/_generated/types/__pycache__/fill_mask.cpython-312.pyc,, +huggingface_hub/inference/_generated/types/__pycache__/image_classification.cpython-312.pyc,, +huggingface_hub/inference/_generated/types/__pycache__/image_segmentation.cpython-312.pyc,, +huggingface_hub/inference/_generated/types/__pycache__/image_to_image.cpython-312.pyc,, +huggingface_hub/inference/_generated/types/__pycache__/image_to_text.cpython-312.pyc,, +huggingface_hub/inference/_generated/types/__pycache__/object_detection.cpython-312.pyc,, +huggingface_hub/inference/_generated/types/__pycache__/question_answering.cpython-312.pyc,, +huggingface_hub/inference/_generated/types/__pycache__/sentence_similarity.cpython-312.pyc,, +huggingface_hub/inference/_generated/types/__pycache__/summarization.cpython-312.pyc,, +huggingface_hub/inference/_generated/types/__pycache__/table_question_answering.cpython-312.pyc,, +huggingface_hub/inference/_generated/types/__pycache__/text2text_generation.cpython-312.pyc,, +huggingface_hub/inference/_generated/types/__pycache__/text_classification.cpython-312.pyc,, +huggingface_hub/inference/_generated/types/__pycache__/text_generation.cpython-312.pyc,, +huggingface_hub/inference/_generated/types/__pycache__/text_to_audio.cpython-312.pyc,, +huggingface_hub/inference/_generated/types/__pycache__/text_to_image.cpython-312.pyc,, +huggingface_hub/inference/_generated/types/__pycache__/text_to_speech.cpython-312.pyc,, +huggingface_hub/inference/_generated/types/__pycache__/text_to_video.cpython-312.pyc,, +huggingface_hub/inference/_generated/types/__pycache__/token_classification.cpython-312.pyc,, +huggingface_hub/inference/_generated/types/__pycache__/translation.cpython-312.pyc,, +huggingface_hub/inference/_generated/types/__pycache__/video_classification.cpython-312.pyc,, +huggingface_hub/inference/_generated/types/__pycache__/visual_question_answering.cpython-312.pyc,, +huggingface_hub/inference/_generated/types/__pycache__/zero_shot_classification.cpython-312.pyc,, +huggingface_hub/inference/_generated/types/__pycache__/zero_shot_image_classification.cpython-312.pyc,, +huggingface_hub/inference/_generated/types/__pycache__/zero_shot_object_detection.cpython-312.pyc,, +huggingface_hub/inference/_generated/types/audio_classification.py,sha256=Jg3mzfGhCSH6CfvVvgJSiFpkz6v4nNA0G4LJXacEgNc,1573 +huggingface_hub/inference/_generated/types/audio_to_audio.py,sha256=2Ep4WkePL7oJwcp5nRJqApwviumGHbft9HhXE9XLHj4,891 +huggingface_hub/inference/_generated/types/automatic_speech_recognition.py,sha256=8CEphr6rvRHgq1L5Md3tq14V0tEAmzJkemh1_7gSswo,5515 +huggingface_hub/inference/_generated/types/base.py,sha256=4XG49q0-2SOftYQ8HXQnWLxiJktou-a7IoG3kdOv-kg,6751 +huggingface_hub/inference/_generated/types/chat_completion.py,sha256=V_C-fCHBycTWlwqFsTk4KGyBQTGRbyMykyIKOh1cTfg,10242 +huggingface_hub/inference/_generated/types/depth_estimation.py,sha256=rcpe9MhYMeLjflOwBs3KMZPr6WjOH3FYEThStG-FJ3M,929 +huggingface_hub/inference/_generated/types/document_question_answering.py,sha256=6BEYGwJcqGlah4RBJDAvWFTEXkO0mosBiMy82432nAM,3202 +huggingface_hub/inference/_generated/types/feature_extraction.py,sha256=NMWVL_TLSG5SS5bdt1-fflkZ75UMlMKeTMtmdnUTADc,1537 +huggingface_hub/inference/_generated/types/fill_mask.py,sha256=OrTgQ7Ndn0_dWK5thQhZwTOHbQni8j0iJcx9llyhRds,1708 +huggingface_hub/inference/_generated/types/image_classification.py,sha256=A-Y024o8723_n8mGVos4TwdAkVL62McGeL1iIo4VzNs,1585 +huggingface_hub/inference/_generated/types/image_segmentation.py,sha256=vrkI4SuP1Iq_iLXc-2pQhYY3SHN4gzvFBoZqbUHxU7o,1950 +huggingface_hub/inference/_generated/types/image_to_image.py,sha256=HPz1uKXk_9xvgNUi3GV6n4lw-J3G6cdGTcW3Ou_N0l8,2044 +huggingface_hub/inference/_generated/types/image_to_text.py,sha256=OaFEBAfgT-fOVzJ7xVermGf7VODhrc9-Jg38WrM7-2o,4810 +huggingface_hub/inference/_generated/types/object_detection.py,sha256=VuFlb1281qTXoSgJDmquGz-VNfEZLo2H0Rh_F6MF6ts,2000 +huggingface_hub/inference/_generated/types/question_answering.py,sha256=zw38a9_9l2k1ifYZefjkioqZ4asfSRM9M4nU3gSCmAQ,2898 +huggingface_hub/inference/_generated/types/sentence_similarity.py,sha256=w5Nj1g18eBzopZwxuDLI-fEsyaCK2KrHA5yf_XfSjgo,1052 +huggingface_hub/inference/_generated/types/summarization.py,sha256=WGGr8uDLrZg8JQgF9ZMUP9euw6uZo6zwkVZ-IfvCFI0,1487 +huggingface_hub/inference/_generated/types/table_question_answering.py,sha256=cJnIPA2fIbQP2Ejn7X_esY48qGWoXg30fnNOqCXiOVQ,2293 +huggingface_hub/inference/_generated/types/text2text_generation.py,sha256=v-418w1JNNSZ2tuW9DUl6a36TQQCADa438A3ufvcbOw,1609 +huggingface_hub/inference/_generated/types/text_classification.py,sha256=FarAjygLEfPofLfKeabzJ7PKEBItlHGoUNUOzyLRpL4,1445 +huggingface_hub/inference/_generated/types/text_generation.py,sha256=28u-1zU7elk2teP3y4u1VAtDDHzY0JZ2KEEJe5d5uvg,5922 +huggingface_hub/inference/_generated/types/text_to_audio.py,sha256=1HR9Q6s9MXqtKGTvHPLGVMum5-eg7O-Pgv6Nd0v8_HU,4741 +huggingface_hub/inference/_generated/types/text_to_image.py,sha256=sGGi1Fa0n5Pmd6G3I-F2SBJcJ1M7Gmqnng6sfi0AVzs,1903 +huggingface_hub/inference/_generated/types/text_to_speech.py,sha256=ROFuR32ijROCeqbv81Jos0lmaA8SRWyIUsWrdD4yWow,4760 +huggingface_hub/inference/_generated/types/text_to_video.py,sha256=yHXVNs3t6aYO7visrBlB5cH7kjoysxF9510aofcf_18,1790 +huggingface_hub/inference/_generated/types/token_classification.py,sha256=iblAcgfxXeaLYJ14NdiiCMIQuBlarUknLkXUklhvcLI,1915 +huggingface_hub/inference/_generated/types/translation.py,sha256=xww4X5cfCYv_F0oINWLwqJRPCT6SV3VBAJuPjTs_j7o,1763 +huggingface_hub/inference/_generated/types/video_classification.py,sha256=TyydjQw2NRLK9sDGzJUVnkDeo848ebmCx588Ur8I9q0,1680 +huggingface_hub/inference/_generated/types/visual_question_answering.py,sha256=AWrQ6qo4gZa3PGedaNpzDFqx5yOYyjhnUB6iuZEj_uo,1673 +huggingface_hub/inference/_generated/types/zero_shot_classification.py,sha256=BAiebPjsqoNa8EU35Dx0pfIv8W2c4GSl-TJckV1MaxQ,1738 +huggingface_hub/inference/_generated/types/zero_shot_image_classification.py,sha256=8J9n6VqFARkWvPfAZNWEG70AlrMGldU95EGQQwn06zI,1487 +huggingface_hub/inference/_generated/types/zero_shot_object_detection.py,sha256=GUd81LIV7oEbRWayDlAVgyLmY596r1M3AW0jXDp1yTA,1630 +huggingface_hub/inference/_providers/__init__.py,sha256=duCzIuoRy6YiJXlO37xXASuJiEpcccplN_69b9nANUs,7351 +huggingface_hub/inference/_providers/__pycache__/__init__.cpython-312.pyc,, +huggingface_hub/inference/_providers/__pycache__/_common.cpython-312.pyc,, +huggingface_hub/inference/_providers/__pycache__/black_forest_labs.cpython-312.pyc,, +huggingface_hub/inference/_providers/__pycache__/cerebras.cpython-312.pyc,, +huggingface_hub/inference/_providers/__pycache__/cohere.cpython-312.pyc,, +huggingface_hub/inference/_providers/__pycache__/fal_ai.cpython-312.pyc,, +huggingface_hub/inference/_providers/__pycache__/fireworks_ai.cpython-312.pyc,, +huggingface_hub/inference/_providers/__pycache__/hf_inference.cpython-312.pyc,, +huggingface_hub/inference/_providers/__pycache__/hyperbolic.cpython-312.pyc,, +huggingface_hub/inference/_providers/__pycache__/nebius.cpython-312.pyc,, +huggingface_hub/inference/_providers/__pycache__/novita.cpython-312.pyc,, +huggingface_hub/inference/_providers/__pycache__/openai.cpython-312.pyc,, +huggingface_hub/inference/_providers/__pycache__/replicate.cpython-312.pyc,, +huggingface_hub/inference/_providers/__pycache__/sambanova.cpython-312.pyc,, +huggingface_hub/inference/_providers/__pycache__/together.cpython-312.pyc,, +huggingface_hub/inference/_providers/_common.py,sha256=YPt96TdVnUNrZOfe3e1wdVm6hjmH5ivz_Lg_4iU58R8,10113 +huggingface_hub/inference/_providers/black_forest_labs.py,sha256=wO7qgRyNyrIKlZtvL3vJEbS4-D19kfoXZk6PDh1dTis,2842 +huggingface_hub/inference/_providers/cerebras.py,sha256=YT1yFhXvDJiKZcqcJcA_7VZJFZVkABnv6QiEb0S90rE,246 +huggingface_hub/inference/_providers/cohere.py,sha256=GkFsuKSaqsyfeerPx0ewv-EX44MtJ8a3XXEfmAiTpb0,419 +huggingface_hub/inference/_providers/fal_ai.py,sha256=gGWPsvQIsuk3kTIXHwpOqA0R1ZsPEo5MYc7OwUoFjxY,7162 +huggingface_hub/inference/_providers/fireworks_ai.py,sha256=6uDsaxJRaN2xWNQX8u1bvF8zO-8J31TAnHdsrf_TO5g,337 +huggingface_hub/inference/_providers/hf_inference.py,sha256=NaCS6Q7cGjxYn61km_UBgRVuFRzZIGsnNiBZuxZPGjg,8062 +huggingface_hub/inference/_providers/hyperbolic.py,sha256=OQIBi2j3aNvuaSQ8BUK1K1PVeRXdrxc80G-6YmBa-ns,1985 +huggingface_hub/inference/_providers/nebius.py,sha256=9X5Er-M29sjJpeFAVuegjnd4ssRe8GQH5iAKMCkeL_E,2141 +huggingface_hub/inference/_providers/novita.py,sha256=HGVC8wPraRQUuI5uBoye1Y4Wqe4X116B71GhhbWy5yM,2514 +huggingface_hub/inference/_providers/openai.py,sha256=2TJPEwcbq1DKPYKB8roJKnMDiXTcCEquSqGPmibc6tQ,1048 +huggingface_hub/inference/_providers/replicate.py,sha256=zFQnnAaNmRruqTvZUG_8It8xkKePHLGKRomSkwjrUuk,3157 +huggingface_hub/inference/_providers/sambanova.py,sha256=yDPORdQnkGKSkbgrOLQEz_kGv5ntp_k0lonsKX3TIeM,1284 +huggingface_hub/inference/_providers/together.py,sha256=5p-HUKzNXlA4r2dD_TMnQlo8Mq_ZUGz_4p9fscC3hGQ,2661 +huggingface_hub/inference_api.py,sha256=b4-NhPSn9b44nYKV8tDKXodmE4JVdEymMWL4CVGkzlE,8323 +huggingface_hub/keras_mixin.py,sha256=3d2oW35SALXHq-WHoLD_tbq0UrcabGKj3HidtPRx51U,19574 +huggingface_hub/lfs.py,sha256=n-TIjK7J7aXG3zi__0nkd6aNkE4djOf9CD6dYQOQ5P8,16649 +huggingface_hub/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +huggingface_hub/repocard.py,sha256=ihFBKYqPNaWw9rWMUvcaRKxrooL32NA4fAlrwzXk9LY,34733 +huggingface_hub/repocard_data.py,sha256=hr4ReFpEQMNdh_9Dx-L-IJoI1ElHyk-h-8ZRqwVYYOE,34082 +huggingface_hub/repository.py,sha256=xVQR-MRKNDfJ_Z_99DwtXZB3xNO06eYG_GvRM4fLiTU,54557 +huggingface_hub/serialization/__init__.py,sha256=kn-Fa-m4FzMnN8lNsF-SwFcfzug4CucexybGKyvZ8S0,1041 +huggingface_hub/serialization/__pycache__/__init__.cpython-312.pyc,, +huggingface_hub/serialization/__pycache__/_base.cpython-312.pyc,, +huggingface_hub/serialization/__pycache__/_dduf.cpython-312.pyc,, +huggingface_hub/serialization/__pycache__/_tensorflow.cpython-312.pyc,, +huggingface_hub/serialization/__pycache__/_torch.cpython-312.pyc,, +huggingface_hub/serialization/_base.py,sha256=Df3GwGR9NzeK_SD75prXLucJAzPiNPgHbgXSw-_LTk8,8126 +huggingface_hub/serialization/_dduf.py,sha256=s42239rLiHwaJE36QDEmS5GH7DSmQ__BffiHJO5RjIg,15424 +huggingface_hub/serialization/_tensorflow.py,sha256=zHOvEMg-JHC55Fm4roDT3LUCDO5zB9qtXZffG065RAM,3625 +huggingface_hub/serialization/_torch.py,sha256=hJglq5F56s3k06GfLuYKQV4bSSjXQLuk1CC9l1M3Fmo,45191 +huggingface_hub/templates/datasetcard_template.md,sha256=W-EMqR6wndbrnZorkVv56URWPG49l7MATGeI015kTvs,5503 +huggingface_hub/templates/modelcard_template.md,sha256=4AqArS3cqdtbit5Bo-DhjcnDFR-pza5hErLLTPM4Yuc,6870 +huggingface_hub/utils/__init__.py,sha256=ORfVkn5D0wuLIq12jjhTzn5_c4F8fRPxB7TG-iednuQ,3722 +huggingface_hub/utils/__pycache__/__init__.cpython-312.pyc,, +huggingface_hub/utils/__pycache__/_auth.cpython-312.pyc,, +huggingface_hub/utils/__pycache__/_cache_assets.cpython-312.pyc,, +huggingface_hub/utils/__pycache__/_cache_manager.cpython-312.pyc,, +huggingface_hub/utils/__pycache__/_chunk_utils.cpython-312.pyc,, +huggingface_hub/utils/__pycache__/_datetime.cpython-312.pyc,, +huggingface_hub/utils/__pycache__/_deprecation.cpython-312.pyc,, +huggingface_hub/utils/__pycache__/_experimental.cpython-312.pyc,, +huggingface_hub/utils/__pycache__/_fixes.cpython-312.pyc,, +huggingface_hub/utils/__pycache__/_git_credential.cpython-312.pyc,, +huggingface_hub/utils/__pycache__/_headers.cpython-312.pyc,, +huggingface_hub/utils/__pycache__/_hf_folder.cpython-312.pyc,, +huggingface_hub/utils/__pycache__/_http.cpython-312.pyc,, +huggingface_hub/utils/__pycache__/_lfs.cpython-312.pyc,, +huggingface_hub/utils/__pycache__/_pagination.cpython-312.pyc,, +huggingface_hub/utils/__pycache__/_paths.cpython-312.pyc,, +huggingface_hub/utils/__pycache__/_runtime.cpython-312.pyc,, +huggingface_hub/utils/__pycache__/_safetensors.cpython-312.pyc,, +huggingface_hub/utils/__pycache__/_subprocess.cpython-312.pyc,, +huggingface_hub/utils/__pycache__/_telemetry.cpython-312.pyc,, +huggingface_hub/utils/__pycache__/_typing.cpython-312.pyc,, +huggingface_hub/utils/__pycache__/_validators.cpython-312.pyc,, +huggingface_hub/utils/__pycache__/_xet.cpython-312.pyc,, +huggingface_hub/utils/__pycache__/endpoint_helpers.cpython-312.pyc,, +huggingface_hub/utils/__pycache__/insecure_hashlib.cpython-312.pyc,, +huggingface_hub/utils/__pycache__/logging.cpython-312.pyc,, +huggingface_hub/utils/__pycache__/sha.cpython-312.pyc,, +huggingface_hub/utils/__pycache__/tqdm.cpython-312.pyc,, +huggingface_hub/utils/_auth.py,sha256=-9p3SSOtWKMMCDKlsM_-ebsIGX0sSgKTSnC-_O4kTxg,8294 +huggingface_hub/utils/_cache_assets.py,sha256=kai77HPQMfYpROouMBQCr_gdBCaeTm996Sqj0dExbNg,5728 +huggingface_hub/utils/_cache_manager.py,sha256=GhiuVQsEkWU55uYkkgiGJV1_naeciyk8u4qb4WTIVyw,34531 +huggingface_hub/utils/_chunk_utils.py,sha256=kRCaj5228_vKcyLWspd8Xq01f17Jz6ds5Sr9ed5d_RU,2130 +huggingface_hub/utils/_datetime.py,sha256=kCS5jaKV25kOncX1xujbXsz5iDLcjLcLw85semGNzxQ,2770 +huggingface_hub/utils/_deprecation.py,sha256=HZhRGGUX_QMKBBBwHHlffLtmCSK01TOpeXHefZbPfwI,4872 +huggingface_hub/utils/_experimental.py,sha256=crCPH6k6-11wwH2GZuZzZzZbjUotay49ywV1SSJhMHM,2395 +huggingface_hub/utils/_fixes.py,sha256=xQV1QkUn2WpLqLjtXNiyn9gh-454K6AF-Q3kwkYAQD8,4437 +huggingface_hub/utils/_git_credential.py,sha256=SDdsiREr1TcAR2Ze2TB0E5cYzVJgvDZrs60od9lAsMc,4596 +huggingface_hub/utils/_headers.py,sha256=3tKQN5ciAt1683nZXEpPyQOS7oWnfYI0t_N_aJU-bms,8876 +huggingface_hub/utils/_hf_folder.py,sha256=WNjTnu0Q7tqcSS9EsP4ssCJrrJMcCvAt8P_-LEtmOU8,2487 +huggingface_hub/utils/_http.py,sha256=her7UZ0KRo9WYDArpqVFyEXTusOGUECj5HNS8Eahqm8,25531 +huggingface_hub/utils/_lfs.py,sha256=EC0Oz6Wiwl8foRNkUOzrETXzAWlbgpnpxo5a410ovFY,3957 +huggingface_hub/utils/_pagination.py,sha256=EX5tRasSuQDaKbXuGYbInBK2odnSWNHgzw2tSgqeBRI,1906 +huggingface_hub/utils/_paths.py,sha256=w1ZhFmmD5ykWjp_hAvhjtOoa2ZUcOXJrF4a6O3QpAWo,5042 +huggingface_hub/utils/_runtime.py,sha256=uzBNsuyNd2QtWzMgEwSoJNUtW24iqNjA-ZNDG1fc9i4,11616 +huggingface_hub/utils/_safetensors.py,sha256=GW3nyv7xQcuwObKYeYoT9VhURVzG1DZTbKBKho8Bbos,4458 +huggingface_hub/utils/_subprocess.py,sha256=u9FFUDE7TrzQTiuEzlUnHx7S2P57GbYRV8u16GJwrFw,4625 +huggingface_hub/utils/_telemetry.py,sha256=54LXeIJU5pEGghPAh06gqNAR-UoxOjVLvKqAQscwqZs,4890 +huggingface_hub/utils/_typing.py,sha256=Dgp6TQUlpzStfVLoSvXHCBP4b3NzHZ8E0Gg9mYAoDS4,2903 +huggingface_hub/utils/_validators.py,sha256=dDsVG31iooTYrIyi5Vwr1DukL0fEmJwu3ceVNduhsuE,9204 +huggingface_hub/utils/_xet.py,sha256=JXgVCli8lD7O1MsvkgqnWY6S9giq1XMrHmtOPPeLmDQ,7020 +huggingface_hub/utils/endpoint_helpers.py,sha256=9VtIAlxQ5H_4y30sjCAgbu7XCqAtNLC7aRYxaNn0hLI,2366 +huggingface_hub/utils/insecure_hashlib.py,sha256=OjxlvtSQHpbLp9PWSrXBDJ0wHjxCBU-SQJgucEEXDbU,1058 +huggingface_hub/utils/logging.py,sha256=0A8fF1yh3L9Ka_bCDX2ml4U5Ht0tY8Dr3JcbRvWFuwo,4909 +huggingface_hub/utils/sha.py,sha256=OFnNGCba0sNcT2gUwaVCJnldxlltrHHe0DS_PCpV3C4,2134 +huggingface_hub/utils/tqdm.py,sha256=xAKcyfnNHsZ7L09WuEM5Ew5-MDhiahLACbbN2zMmcLs,10671 diff --git a/lib/python3.12/site-packages/huggingface_hub-0.31.4.dist-info/REQUESTED b/lib/python3.12/site-packages/huggingface_hub-0.31.4.dist-info/REQUESTED new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/lib/python3.12/site-packages/huggingface_hub-0.31.4.dist-info/WHEEL b/lib/python3.12/site-packages/huggingface_hub-0.31.4.dist-info/WHEEL new file mode 100644 index 0000000000000000000000000000000000000000..79d5c89a71989389294854aa34e329701325f8b0 --- /dev/null +++ b/lib/python3.12/site-packages/huggingface_hub-0.31.4.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.45.1) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/lib/python3.12/site-packages/huggingface_hub-0.31.4.dist-info/entry_points.txt b/lib/python3.12/site-packages/huggingface_hub-0.31.4.dist-info/entry_points.txt new file mode 100644 index 0000000000000000000000000000000000000000..eb3dafd90f19de60b3e520aeaf8132402980214d --- /dev/null +++ b/lib/python3.12/site-packages/huggingface_hub-0.31.4.dist-info/entry_points.txt @@ -0,0 +1,6 @@ +[console_scripts] +huggingface-cli = huggingface_hub.commands.huggingface_cli:main + +[fsspec.specs] +hf=huggingface_hub.HfFileSystem + diff --git a/lib/python3.12/site-packages/huggingface_hub-0.31.4.dist-info/top_level.txt b/lib/python3.12/site-packages/huggingface_hub-0.31.4.dist-info/top_level.txt new file mode 100644 index 0000000000000000000000000000000000000000..6b964ccca3c1b6766042b3fe3b2707ba25372924 --- /dev/null +++ b/lib/python3.12/site-packages/huggingface_hub-0.31.4.dist-info/top_level.txt @@ -0,0 +1 @@ +huggingface_hub diff --git a/lib/python3.12/site-packages/idna-3.11.dist-info/INSTALLER b/lib/python3.12/site-packages/idna-3.11.dist-info/INSTALLER new file mode 100644 index 0000000000000000000000000000000000000000..a1b589e38a32041e49332e5e81c2d363dc418d68 --- /dev/null +++ b/lib/python3.12/site-packages/idna-3.11.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/lib/python3.12/site-packages/idna-3.11.dist-info/METADATA b/lib/python3.12/site-packages/idna-3.11.dist-info/METADATA new file mode 100644 index 0000000000000000000000000000000000000000..7a4a4b7a7e9adbe4542d98f6b2f40e65ee64b182 --- /dev/null +++ b/lib/python3.12/site-packages/idna-3.11.dist-info/METADATA @@ -0,0 +1,209 @@ +Metadata-Version: 2.4 +Name: idna +Version: 3.11 +Summary: Internationalized Domain Names in Applications (IDNA) +Author-email: Kim Davies +Requires-Python: >=3.8 +Description-Content-Type: text/x-rst +License-Expression: BSD-3-Clause +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: Intended Audience :: System Administrators +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +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.14 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Internet :: Name Service (DNS) +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Topic :: Utilities +License-File: LICENSE.md +Requires-Dist: ruff >= 0.6.2 ; extra == "all" +Requires-Dist: mypy >= 1.11.2 ; extra == "all" +Requires-Dist: pytest >= 8.3.2 ; extra == "all" +Requires-Dist: flake8 >= 7.1.1 ; extra == "all" +Project-URL: Changelog, https://github.com/kjd/idna/blob/master/HISTORY.rst +Project-URL: Issue tracker, https://github.com/kjd/idna/issues +Project-URL: Source, https://github.com/kjd/idna +Provides-Extra: all + +Internationalized Domain Names in Applications (IDNA) +===================================================== + +Support for `Internationalized Domain Names in +Applications (IDNA) `_ +and `Unicode IDNA Compatibility Processing +`_. + +The latest versions of these standards supplied here provide +more comprehensive language coverage and reduce the potential of +allowing domains with known security vulnerabilities. This library +is a suitable replacement for the “encodings.idna” +module that comes with the Python standard library, but which +only supports an older superseded IDNA specification from 2003. + +Basic functions are simply executed: + +.. code-block:: pycon + + >>> import idna + >>> idna.encode('ドメイン.テスト') + b'xn--eckwd4c7c.xn--zckzah' + >>> print(idna.decode('xn--eckwd4c7c.xn--zckzah')) + ドメイン.テスト + + +Installation +------------ + +This package is available for installation from PyPI via the +typical mechanisms, such as: + +.. code-block:: bash + + $ python3 -m pip install idna + + +Usage +----- + +For typical usage, the ``encode`` and ``decode`` functions will take a +domain name argument and perform a conversion to ASCII compatible encoding +(known as A-labels), or to Unicode strings (known as U-labels) +respectively. + +.. code-block:: pycon + + >>> import idna + >>> idna.encode('ドメイン.テスト') + b'xn--eckwd4c7c.xn--zckzah' + >>> print(idna.decode('xn--eckwd4c7c.xn--zckzah')) + ドメイン.テスト + +Conversions can be applied at a per-label basis using the ``ulabel`` or +``alabel`` functions if necessary: + +.. code-block:: pycon + + >>> idna.alabel('测试') + b'xn--0zwm56d' + + +Compatibility Mapping (UTS #46) ++++++++++++++++++++++++++++++++ + +This library provides support for `Unicode IDNA Compatibility +Processing `_ which normalizes input from +different potential ways a user may input a domain prior to performing the IDNA +conversion operations. This functionality, known as a +`mapping `_, is considered by the +specification to be a local user-interface issue distinct from IDNA +conversion functionality. + +For example, “Königsgäßchen” is not a permissible label as *LATIN +CAPITAL LETTER K* is not allowed (nor are capital letters in general). +UTS 46 will convert this into lower case prior to applying the IDNA +conversion. + +.. code-block:: pycon + + >>> import idna + >>> idna.encode('Königsgäßchen') + ... + idna.core.InvalidCodepoint: Codepoint U+004B at position 1 of 'Königsgäßchen' not allowed + >>> idna.encode('Königsgäßchen', uts46=True) + b'xn--knigsgchen-b4a3dun' + >>> print(idna.decode('xn--knigsgchen-b4a3dun')) + königsgäßchen + + +Exceptions +---------- + +All errors raised during the conversion following the specification +should raise an exception derived from the ``idna.IDNAError`` base +class. + +More specific exceptions that may be generated as ``idna.IDNABidiError`` +when the error reflects an illegal combination of left-to-right and +right-to-left characters in a label; ``idna.InvalidCodepoint`` when +a specific codepoint is an illegal character in an IDN label (i.e. +INVALID); and ``idna.InvalidCodepointContext`` when the codepoint is +illegal based on its position in the string (i.e. it is CONTEXTO or CONTEXTJ +but the contextual requirements are not satisfied.) + +Building and Diagnostics +------------------------ + +The IDNA and UTS 46 functionality relies upon pre-calculated lookup +tables for performance. These tables are derived from computing against +eligibility criteria in the respective standards using the command-line +script ``tools/idna-data``. + +This tool will fetch relevant codepoint data from the Unicode repository +and perform the required calculations to identify eligibility. There are +three main modes: + +* ``idna-data make-libdata``. Generates ``idnadata.py`` and + ``uts46data.py``, the pre-calculated lookup tables used for IDNA and + UTS 46 conversions. Implementers who wish to track this library against + a different Unicode version may use this tool to manually generate a + different version of the ``idnadata.py`` and ``uts46data.py`` files. + +* ``idna-data make-table``. Generate a table of the IDNA disposition + (e.g. PVALID, CONTEXTJ, CONTEXTO) in the format found in Appendix + B.1 of RFC 5892 and the pre-computed tables published by `IANA + `_. + +* ``idna-data U+0061``. Prints debugging output on the various + properties associated with an individual Unicode codepoint (in this + case, U+0061), that are used to assess the IDNA and UTS 46 status of a + codepoint. This is helpful in debugging or analysis. + +The tool accepts a number of arguments, described using ``idna-data +-h``. Most notably, the ``--version`` argument allows the specification +of the version of Unicode to be used in computing the table data. For +example, ``idna-data --version 9.0.0 make-libdata`` will generate +library data against Unicode 9.0.0. + + +Additional Notes +---------------- + +* **Packages**. The latest tagged release version is published in the + `Python Package Index `_. + +* **Version support**. This library supports Python 3.8 and higher. + As this library serves as a low-level toolkit for a variety of + applications, many of which strive for broad compatibility with older + Python versions, there is no rush to remove older interpreter support. + Support for older versions are likely to be removed from new releases + as automated tests can no longer easily be run, i.e. once the Python + version is officially end-of-life. + +* **Testing**. The library has a test suite based on each rule of the + IDNA specification, as well as tests that are provided as part of the + Unicode Technical Standard 46, `Unicode IDNA Compatibility Processing + `_. + +* **Emoji**. It is an occasional request to support emoji domains in + this library. Encoding of symbols like emoji is expressly prohibited by + the technical standard IDNA 2008 and emoji domains are broadly phased + out across the domain industry due to associated security risks. For + now, applications that need to support these non-compliant labels + may wish to consider trying the encode/decode operation in this library + first, and then falling back to using `encodings.idna`. See `the Github + project `_ for more discussion. + +* **Transitional processing**. Unicode 16.0.0 removed transitional + processing so the `transitional` argument for the encode() method + no longer has any effect and will be removed at a later date. + diff --git a/lib/python3.12/site-packages/idna-3.11.dist-info/RECORD b/lib/python3.12/site-packages/idna-3.11.dist-info/RECORD new file mode 100644 index 0000000000000000000000000000000000000000..8525b6daf9b59dd5e02fdf5797c63da32f780ee8 --- /dev/null +++ b/lib/python3.12/site-packages/idna-3.11.dist-info/RECORD @@ -0,0 +1,22 @@ +idna-3.11.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +idna-3.11.dist-info/METADATA,sha256=fCwSww9SuiN8TIHllFSASUQCW55hAs8dzKnr9RaEEbA,8378 +idna-3.11.dist-info/RECORD,, +idna-3.11.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82 +idna-3.11.dist-info/licenses/LICENSE.md,sha256=t6M2q_OwThgOwGXN0W5wXQeeHMehT5EKpukYfza5zYc,1541 +idna/__init__.py,sha256=MPqNDLZbXqGaNdXxAFhiqFPKEQXju2jNQhCey6-5eJM,868 +idna/__pycache__/__init__.cpython-312.pyc,, +idna/__pycache__/codec.cpython-312.pyc,, +idna/__pycache__/compat.cpython-312.pyc,, +idna/__pycache__/core.cpython-312.pyc,, +idna/__pycache__/idnadata.cpython-312.pyc,, +idna/__pycache__/intranges.cpython-312.pyc,, +idna/__pycache__/package_data.cpython-312.pyc,, +idna/__pycache__/uts46data.cpython-312.pyc,, +idna/codec.py,sha256=M2SGWN7cs_6B32QmKTyTN6xQGZeYQgQ2wiX3_DR6loE,3438 +idna/compat.py,sha256=RzLy6QQCdl9784aFhb2EX9EKGCJjg0P3PilGdeXXcx8,316 +idna/core.py,sha256=P26_XVycuMTZ1R2mNK1ZREVzM5mvTzdabBXfyZVU1Lc,13246 +idna/idnadata.py,sha256=SG8jhaGE53iiD6B49pt2pwTv_UvClciWE-N54oR2p4U,79623 +idna/intranges.py,sha256=amUtkdhYcQG8Zr-CoMM_kVRacxkivC1WgxN1b63KKdU,1898 +idna/package_data.py,sha256=_CUavOxobnbyNG2FLyHoN8QHP3QM9W1tKuw7eq9QwBk,21 +idna/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +idna/uts46data.py,sha256=H9J35VkD0F9L9mKOqjeNGd2A-Va6FlPoz6Jz4K7h-ps,243725 diff --git a/lib/python3.12/site-packages/idna-3.11.dist-info/WHEEL b/lib/python3.12/site-packages/idna-3.11.dist-info/WHEEL new file mode 100644 index 0000000000000000000000000000000000000000..d8b9936dad9ab2513fa6979f411560d3b6b57e37 --- /dev/null +++ b/lib/python3.12/site-packages/idna-3.11.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: flit 3.12.0 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/lib/python3.12/site-packages/idna-3.11.dist-info/licenses/LICENSE.md b/lib/python3.12/site-packages/idna-3.11.dist-info/licenses/LICENSE.md new file mode 100644 index 0000000000000000000000000000000000000000..256ba90cd91190a6c980bd44663dc51c201c14d3 --- /dev/null +++ b/lib/python3.12/site-packages/idna-3.11.dist-info/licenses/LICENSE.md @@ -0,0 +1,31 @@ +BSD 3-Clause License + +Copyright (c) 2013-2025, Kim Davies and contributors. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/lib/python3.12/site-packages/jiter-0.13.0.dist-info/INSTALLER b/lib/python3.12/site-packages/jiter-0.13.0.dist-info/INSTALLER new file mode 100644 index 0000000000000000000000000000000000000000..a1b589e38a32041e49332e5e81c2d363dc418d68 --- /dev/null +++ b/lib/python3.12/site-packages/jiter-0.13.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/lib/python3.12/site-packages/jiter-0.13.0.dist-info/METADATA b/lib/python3.12/site-packages/jiter-0.13.0.dist-info/METADATA new file mode 100644 index 0000000000000000000000000000000000000000..b0979798678956beda0cfb815ce329fcebf4c24b --- /dev/null +++ b/lib/python3.12/site-packages/jiter-0.13.0.dist-info/METADATA @@ -0,0 +1,145 @@ +Metadata-Version: 2.4 +Name: jiter +Version: 0.13.0 +Classifier: Development Status :: 4 - Beta +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +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.14 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: GraalPy +Classifier: Intended Audience :: Developers +Classifier: Intended Audience :: Information Technology +Classifier: Intended Audience :: System Administrators +Classifier: License :: OSI Approved :: MIT License +Classifier: Operating System :: Unix +Classifier: Operating System :: POSIX :: Linux +Classifier: Environment :: Console +Classifier: Environment :: MacOS X +Classifier: Topic :: File Formats :: JSON +Classifier: Framework :: Pydantic :: 2 +License-File: LICENSE +Summary: Fast iterable JSON parser. +Home-Page: https://github.com/pydantic/jiter/ +Author-email: Samuel Colvin +Requires-Python: >=3.9 +Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM + +# jiter + +[![CI](https://github.com/pydantic/jiter/workflows/CI/badge.svg?event=push)](https://github.com/pydantic/jiter/actions?query=event%3Apush+branch%3Amain+workflow%3ACI) +[![pypi](https://img.shields.io/pypi/v/jiter.svg)](https://pypi.python.org/pypi/jiter) +[![versions](https://img.shields.io/pypi/pyversions/jiter.svg)](https://github.com/pydantic/jiter) +[![license](https://img.shields.io/github/license/pydantic/jiter.svg)](https://github.com/pydantic/jiter/blob/main/LICENSE) + +This is a standalone version of the JSON parser used in `pydantic-core`. The recommendation is to only use this package directly if you do not use `pydantic`. + +The API is extremely minimal: + +```python +def from_json( + json_data: bytes, + /, + *, + allow_inf_nan: bool = True, + cache_mode: Literal[True, False, "all", "keys", "none"] = "all", + partial_mode: Literal[True, False, "off", "on", "trailing-strings"] = False, + catch_duplicate_keys: bool = False, + float_mode: Literal["float", "decimal", "lossless-float"] = "float", +) -> Any: + """ + Parse input bytes into a JSON object. + + Arguments: + json_data: The JSON data to parse + allow_inf_nan: Whether to allow infinity (`Infinity` an `-Infinity`) and `NaN` values to float fields. + Defaults to True. + cache_mode: cache Python strings to improve performance at the cost of some memory usage + - True / 'all' - cache all strings + - 'keys' - cache only object keys + - False / 'none' - cache nothing + partial_mode: How to handle incomplete strings: + - False / 'off' - raise an exception if the input is incomplete + - True / 'on' - allow incomplete JSON but discard the last string if it is incomplete + - 'trailing-strings' - allow incomplete JSON, and include the last incomplete string in the output + catch_duplicate_keys: if True, raise an exception if objects contain the same key multiple times + float_mode: How to return floats: as a `float`, `Decimal` or `LosslessFloat` + + Returns: + Python object built from the JSON input. + """ + +def cache_clear() -> None: + """ + Reset the string cache. + """ + +def cache_usage() -> int: + """ + get the size of the string cache. + + Returns: + Size of the string cache in bytes. + """ +``` +## Examples + +The main function provided by Jiter is `from_json()`, which accepts a bytes object containing JSON and returns a Python dictionary, list or other value. + +```python +import jiter + +json_data = b'{"name": "John", "age": 30}' +parsed_data = jiter.from_json(json_data) +print(parsed_data) # Output: {'name': 'John', 'age': 30} +``` + +### Handling Partial JSON + +Incomplete JSON objects can be parsed using the `partial_mode=` parameter. + +```python +import jiter + +partial_json = b'{"name": "John", "age": 30, "city": "New Yor' + +# Raise error on incomplete JSON +try: + jiter.from_json(partial_json, partial_mode=False) +except ValueError as e: + print(f"Error: {e}") + +# Parse incomplete JSON, discarding incomplete last field +result = jiter.from_json(partial_json, partial_mode=True) +print(result) # Output: {'name': 'John', 'age': 30} + +# Parse incomplete JSON, including incomplete last field +result = jiter.from_json(partial_json, partial_mode='trailing-strings') +print(result) # Output: {'name': 'John', 'age': 30, 'city': 'New Yor'} +``` + +### Catching Duplicate Keys + +The `catch_duplicate_keys=True` option can be used to raise a `ValueError` if an object contains duplicate keys. + +```python +import jiter + +json_with_dupes = b'{"foo": 1, "foo": 2}' + +# Default behavior (last value wins) +result = jiter.from_json(json_with_dupes) +print(result) # Output: {'foo': 2} + +# Catch duplicate keys +try: + jiter.from_json(json_with_dupes, catch_duplicate_keys=True) +except ValueError as e: + print(f"Error: {e}") +``` + diff --git a/lib/python3.12/site-packages/jiter-0.13.0.dist-info/RECORD b/lib/python3.12/site-packages/jiter-0.13.0.dist-info/RECORD new file mode 100644 index 0000000000000000000000000000000000000000..fe5bd959f7cb2305d1143cacc393fe26c47361d8 --- /dev/null +++ b/lib/python3.12/site-packages/jiter-0.13.0.dist-info/RECORD @@ -0,0 +1,10 @@ +jiter-0.13.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +jiter-0.13.0.dist-info/METADATA,sha256=DxWMRpWhEPb8FNo2SrzLQv8lFgFFsuW5_jX5rlrvkCk,5236 +jiter-0.13.0.dist-info/RECORD,, +jiter-0.13.0.dist-info/WHEEL,sha256=OBGLPVRY2sSpDmotBA08gVH1DnnkHcjJ2yKGiD3ATcs,147 +jiter-0.13.0.dist-info/licenses/LICENSE,sha256=fHE0ufe5eMA_yodVF885jbkfGbu4EJtmhedCqj9XRo4,1091 +jiter/__init__.py,sha256=Fp9HkOixiYYDSiC_80vmiJ_sCoCGT8OAh48yltm0lP0,103 +jiter/__init__.pyi,sha256=IXJ5QM8oWahamu-erWyWfk4hDX-K7ZJvyoZi8jLV0nQ,2365 +jiter/__pycache__/__init__.cpython-312.pyc,, +jiter/jiter.cpython-312-x86_64-linux-gnu.so,sha256=IbVXpeYxbpVIn51H9rU-_KEOolBtjqsrvoRvpN4ucxU,834000 +jiter/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 diff --git a/lib/python3.12/site-packages/jiter-0.13.0.dist-info/WHEEL b/lib/python3.12/site-packages/jiter-0.13.0.dist-info/WHEEL new file mode 100644 index 0000000000000000000000000000000000000000..3a7ba2d0e2c6ded518a7d7c0fa58128925e06e23 --- /dev/null +++ b/lib/python3.12/site-packages/jiter-0.13.0.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: maturin (1.11.5) +Root-Is-Purelib: false +Tag: cp312-cp312-manylinux_2_17_x86_64 +Tag: cp312-cp312-manylinux2014_x86_64 diff --git a/lib/python3.12/site-packages/jiter-0.13.0.dist-info/licenses/LICENSE b/lib/python3.12/site-packages/jiter-0.13.0.dist-info/licenses/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..47de40dc06ba673d480a12ef728ce62fe43e3151 --- /dev/null +++ b/lib/python3.12/site-packages/jiter-0.13.0.dist-info/licenses/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2022 to present Samuel Colvin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/lib/python3.12/site-packages/jsonschema/__init__.py b/lib/python3.12/site-packages/jsonschema/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..d8dec8cfacd0fbf085eeb0851272ddfef2bda9d3 --- /dev/null +++ b/lib/python3.12/site-packages/jsonschema/__init__.py @@ -0,0 +1,120 @@ +""" +An implementation of JSON Schema for Python. + +The main functionality is provided by the validator classes for each of the +supported JSON Schema versions. + +Most commonly, `jsonschema.validators.validate` is the quickest way to simply +validate a given instance under a schema, and will create a validator +for you. +""" +import warnings + +from jsonschema._format import FormatChecker +from jsonschema._types import TypeChecker +from jsonschema.exceptions import SchemaError, ValidationError +from jsonschema.validators import ( + Draft3Validator, + Draft4Validator, + Draft6Validator, + Draft7Validator, + Draft201909Validator, + Draft202012Validator, + validate, +) + + +def __getattr__(name): + if name == "__version__": + warnings.warn( + "Accessing jsonschema.__version__ is deprecated and will be " + "removed in a future release. Use importlib.metadata directly " + "to query for jsonschema's version.", + DeprecationWarning, + stacklevel=2, + ) + + from importlib import metadata + return metadata.version("jsonschema") + elif name == "RefResolver": + from jsonschema.validators import _RefResolver + warnings.warn( + _RefResolver._DEPRECATION_MESSAGE, + DeprecationWarning, + stacklevel=2, + ) + return _RefResolver + elif name == "ErrorTree": + warnings.warn( + "Importing ErrorTree directly from the jsonschema package " + "is deprecated and will become an ImportError. Import it from " + "jsonschema.exceptions instead.", + DeprecationWarning, + stacklevel=2, + ) + from jsonschema.exceptions import ErrorTree + return ErrorTree + elif name == "FormatError": + warnings.warn( + "Importing FormatError directly from the jsonschema package " + "is deprecated and will become an ImportError. Import it from " + "jsonschema.exceptions instead.", + DeprecationWarning, + stacklevel=2, + ) + from jsonschema.exceptions import FormatError + return FormatError + elif name == "Validator": + warnings.warn( + "Importing Validator directly from the jsonschema package " + "is deprecated and will become an ImportError. Import it from " + "jsonschema.protocols instead.", + DeprecationWarning, + stacklevel=2, + ) + from jsonschema.protocols import Validator + return Validator + elif name == "RefResolutionError": + from jsonschema.exceptions import _RefResolutionError + warnings.warn( + _RefResolutionError._DEPRECATION_MESSAGE, + DeprecationWarning, + stacklevel=2, + ) + return _RefResolutionError + + format_checkers = { + "draft3_format_checker": Draft3Validator, + "draft4_format_checker": Draft4Validator, + "draft6_format_checker": Draft6Validator, + "draft7_format_checker": Draft7Validator, + "draft201909_format_checker": Draft201909Validator, + "draft202012_format_checker": Draft202012Validator, + } + ValidatorForFormat = format_checkers.get(name) + if ValidatorForFormat is not None: + warnings.warn( + f"Accessing jsonschema.{name} is deprecated and will be " + "removed in a future release. Instead, use the FORMAT_CHECKER " + "attribute on the corresponding Validator.", + DeprecationWarning, + stacklevel=2, + ) + return ValidatorForFormat.FORMAT_CHECKER + + raise AttributeError(f"module {__name__} has no attribute {name}") + + +__all__ = [ + "Draft3Validator", + "Draft4Validator", + "Draft6Validator", + "Draft7Validator", + "Draft201909Validator", + "Draft202012Validator", + "FormatChecker", + "SchemaError", + "TypeChecker", + "ValidationError", + "validate", +] diff --git a/lib/python3.12/site-packages/jsonschema/__main__.py b/lib/python3.12/site-packages/jsonschema/__main__.py new file mode 100644 index 0000000000000000000000000000000000000000..fb260ae145d7518f8e8fa16ed13cc2c6173e7404 --- /dev/null +++ b/lib/python3.12/site-packages/jsonschema/__main__.py @@ -0,0 +1,6 @@ +""" +The jsonschema CLI is now deprecated in favor of check-jsonschema. +""" +from jsonschema.cli import main + +main() diff --git a/lib/python3.12/site-packages/jsonschema/__pycache__/__init__.cpython-312.pyc b/lib/python3.12/site-packages/jsonschema/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4502c3f836d5aa7353e34569c20c86febd78ebe7 Binary files /dev/null and b/lib/python3.12/site-packages/jsonschema/__pycache__/__init__.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/jsonschema/__pycache__/__main__.cpython-312.pyc b/lib/python3.12/site-packages/jsonschema/__pycache__/__main__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..778ea6591194a74cf12dee408e6903a4c4c73d7c Binary files /dev/null and b/lib/python3.12/site-packages/jsonschema/__pycache__/__main__.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/jsonschema/__pycache__/_format.cpython-312.pyc b/lib/python3.12/site-packages/jsonschema/__pycache__/_format.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c1eb82d4728e6bb9fdfda573c23bd5555adce5e8 Binary files /dev/null and b/lib/python3.12/site-packages/jsonschema/__pycache__/_format.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/jsonschema/__pycache__/_keywords.cpython-312.pyc b/lib/python3.12/site-packages/jsonschema/__pycache__/_keywords.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..912c88c6eb0990202ab7dfecfe43422bd266ca09 Binary files /dev/null and b/lib/python3.12/site-packages/jsonschema/__pycache__/_keywords.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/jsonschema/__pycache__/_legacy_keywords.cpython-312.pyc b/lib/python3.12/site-packages/jsonschema/__pycache__/_legacy_keywords.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..428ab885add09cdc98cf2e420077c4b285b726dc Binary files /dev/null and b/lib/python3.12/site-packages/jsonschema/__pycache__/_legacy_keywords.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/jsonschema/__pycache__/_types.cpython-312.pyc b/lib/python3.12/site-packages/jsonschema/__pycache__/_types.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9515bd41d8ab37d221d93831c3783888e534362c Binary files /dev/null and b/lib/python3.12/site-packages/jsonschema/__pycache__/_types.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/jsonschema/__pycache__/_typing.cpython-312.pyc b/lib/python3.12/site-packages/jsonschema/__pycache__/_typing.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0a5ce5a85aa9f64343b1231d672182c1707009e5 Binary files /dev/null and b/lib/python3.12/site-packages/jsonschema/__pycache__/_typing.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/jsonschema/__pycache__/_utils.cpython-312.pyc b/lib/python3.12/site-packages/jsonschema/__pycache__/_utils.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2f87357bf3f69e84ae3905e35dbd338e3422710a Binary files /dev/null and b/lib/python3.12/site-packages/jsonschema/__pycache__/_utils.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/jsonschema/__pycache__/cli.cpython-312.pyc b/lib/python3.12/site-packages/jsonschema/__pycache__/cli.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d07a82fa066dac4d537aef3e093ec8653ecda812 Binary files /dev/null and b/lib/python3.12/site-packages/jsonschema/__pycache__/cli.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/jsonschema/__pycache__/exceptions.cpython-312.pyc b/lib/python3.12/site-packages/jsonschema/__pycache__/exceptions.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..98175a2a664f5bae125b7467bfa86814ce82f20e Binary files /dev/null and b/lib/python3.12/site-packages/jsonschema/__pycache__/exceptions.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/jsonschema/__pycache__/protocols.cpython-312.pyc b/lib/python3.12/site-packages/jsonschema/__pycache__/protocols.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..03364ee974185c7d1a2f003d97908ef09211c58e Binary files /dev/null and b/lib/python3.12/site-packages/jsonschema/__pycache__/protocols.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/jsonschema/__pycache__/validators.cpython-312.pyc b/lib/python3.12/site-packages/jsonschema/__pycache__/validators.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..afbc302d54c91f652afa99f0e049734d5a0c8057 Binary files /dev/null and b/lib/python3.12/site-packages/jsonschema/__pycache__/validators.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/jsonschema/_format.py b/lib/python3.12/site-packages/jsonschema/_format.py new file mode 100644 index 0000000000000000000000000000000000000000..5efda86e6fda76fb7a9c226299eb6b843f40d4c0 --- /dev/null +++ b/lib/python3.12/site-packages/jsonschema/_format.py @@ -0,0 +1,546 @@ +from __future__ import annotations + +from contextlib import suppress +from datetime import date, datetime +from uuid import UUID +import ipaddress +import re +import typing +import warnings + +from jsonschema.exceptions import FormatError + +_FormatCheckCallable = typing.Callable[[object], bool] +#: A format checker callable. +_F = typing.TypeVar("_F", bound=_FormatCheckCallable) +_RaisesType = type[Exception] | tuple[type[Exception], ...] + +_RE_DATE = re.compile(r"^\d{4}-\d{2}-\d{2}$", re.ASCII) + + +class FormatChecker: + """ + A ``format`` property checker. + + JSON Schema does not mandate that the ``format`` property actually do any + validation. If validation is desired however, instances of this class can + be hooked into validators to enable format validation. + + `FormatChecker` objects always return ``True`` when asked about + formats that they do not know how to validate. + + To add a check for a custom format use the `FormatChecker.checks` + decorator. + + Arguments: + + formats: + + The known formats to validate. This argument can be used to + limit which formats will be used during validation. + + """ + + checkers: dict[ + str, + tuple[_FormatCheckCallable, _RaisesType], + ] = {} # noqa: RUF012 + + def __init__(self, formats: typing.Iterable[str] | None = None): + if formats is None: + formats = self.checkers.keys() + self.checkers = {k: self.checkers[k] for k in formats} + + def __repr__(self): + return f"" + + def checks( + self, format: str, raises: _RaisesType = (), + ) -> typing.Callable[[_F], _F]: + """ + Register a decorated function as validating a new format. + + Arguments: + + format: + + The format that the decorated function will check. + + raises: + + The exception(s) raised by the decorated function when an + invalid instance is found. + + The exception object will be accessible as the + `jsonschema.exceptions.ValidationError.cause` attribute of the + resulting validation error. + + """ + + def _checks(func: _F) -> _F: + self.checkers[format] = (func, raises) + return func + + return _checks + + @classmethod + def cls_checks( + cls, format: str, raises: _RaisesType = (), + ) -> typing.Callable[[_F], _F]: + warnings.warn( + ( + "FormatChecker.cls_checks is deprecated. Call " + "FormatChecker.checks on a specific FormatChecker instance " + "instead." + ), + DeprecationWarning, + stacklevel=2, + ) + return cls._cls_checks(format=format, raises=raises) + + @classmethod + def _cls_checks( + cls, format: str, raises: _RaisesType = (), + ) -> typing.Callable[[_F], _F]: + def _checks(func: _F) -> _F: + cls.checkers[format] = (func, raises) + return func + + return _checks + + def check(self, instance: object, format: str) -> None: + """ + Check whether the instance conforms to the given format. + + Arguments: + + instance (*any primitive type*, i.e. str, number, bool): + + The instance to check + + format: + + The format that instance should conform to + + Raises: + + FormatError: + + if the instance does not conform to ``format`` + + """ + if format not in self.checkers: + return + + func, raises = self.checkers[format] + result, cause = None, None + try: + result = func(instance) + except raises as e: + cause = e + if not result: + raise FormatError(f"{instance!r} is not a {format!r}", cause=cause) + + def conforms(self, instance: object, format: str) -> bool: + """ + Check whether the instance conforms to the given format. + + Arguments: + + instance (*any primitive type*, i.e. str, number, bool): + + The instance to check + + format: + + The format that instance should conform to + + Returns: + + bool: whether it conformed + + """ + try: + self.check(instance, format) + except FormatError: + return False + else: + return True + + +draft3_format_checker = FormatChecker() +draft4_format_checker = FormatChecker() +draft6_format_checker = FormatChecker() +draft7_format_checker = FormatChecker() +draft201909_format_checker = FormatChecker() +draft202012_format_checker = FormatChecker() + +_draft_checkers: dict[str, FormatChecker] = dict( + draft3=draft3_format_checker, + draft4=draft4_format_checker, + draft6=draft6_format_checker, + draft7=draft7_format_checker, + draft201909=draft201909_format_checker, + draft202012=draft202012_format_checker, +) + + +def _checks_drafts( + name=None, + draft3=None, + draft4=None, + draft6=None, + draft7=None, + draft201909=None, + draft202012=None, + raises=(), +) -> typing.Callable[[_F], _F]: + draft3 = draft3 or name + draft4 = draft4 or name + draft6 = draft6 or name + draft7 = draft7 or name + draft201909 = draft201909 or name + draft202012 = draft202012 or name + + def wrap(func: _F) -> _F: + if draft3: + func = _draft_checkers["draft3"].checks(draft3, raises)(func) + if draft4: + func = _draft_checkers["draft4"].checks(draft4, raises)(func) + if draft6: + func = _draft_checkers["draft6"].checks(draft6, raises)(func) + if draft7: + func = _draft_checkers["draft7"].checks(draft7, raises)(func) + if draft201909: + func = _draft_checkers["draft201909"].checks(draft201909, raises)( + func, + ) + if draft202012: + func = _draft_checkers["draft202012"].checks(draft202012, raises)( + func, + ) + + # Oy. This is bad global state, but relied upon for now, until + # deprecation. See #519 and test_format_checkers_come_with_defaults + FormatChecker._cls_checks( + draft202012 or draft201909 or draft7 or draft6 or draft4 or draft3, + raises, + )(func) + return func + + return wrap + + +@_checks_drafts(name="idn-email") +@_checks_drafts(name="email") +def is_email(instance: object) -> bool: + if not isinstance(instance, str): + return True + return "@" in instance + + +@_checks_drafts( + draft3="ip-address", + draft4="ipv4", + draft6="ipv4", + draft7="ipv4", + draft201909="ipv4", + draft202012="ipv4", + raises=ipaddress.AddressValueError, +) +def is_ipv4(instance: object) -> bool: + if not isinstance(instance, str): + return True + return bool(ipaddress.IPv4Address(instance)) + + +@_checks_drafts(name="ipv6", raises=ipaddress.AddressValueError) +def is_ipv6(instance: object) -> bool: + if not isinstance(instance, str): + return True + address = ipaddress.IPv6Address(instance) + return not getattr(address, "scope_id", "") + + +with suppress(ImportError): + from fqdn import FQDN + + @_checks_drafts( + draft3="host-name", + draft4="hostname", + draft6="hostname", + draft7="hostname", + draft201909="hostname", + draft202012="hostname", + # fqdn.FQDN("") raises a ValueError due to a bug + # however, it's not clear when or if that will be fixed, so catch it + # here for now + raises=ValueError, + ) + def is_host_name(instance: object) -> bool: + if not isinstance(instance, str): + return True + return FQDN(instance, min_labels=1).is_valid + + +with suppress(ImportError): + # The built-in `idna` codec only implements RFC 3890, so we go elsewhere. + import idna + + @_checks_drafts( + draft7="idn-hostname", + draft201909="idn-hostname", + draft202012="idn-hostname", + raises=(idna.IDNAError, UnicodeError), + ) + def is_idn_host_name(instance: object) -> bool: + if not isinstance(instance, str): + return True + idna.encode(instance) + return True + + +try: + import rfc3987 +except ImportError: + with suppress(ImportError): + from rfc3986_validator import validate_rfc3986 + + @_checks_drafts(name="uri") + def is_uri(instance: object) -> bool: + if not isinstance(instance, str): + return True + return validate_rfc3986(instance, rule="URI") + + @_checks_drafts( + draft6="uri-reference", + draft7="uri-reference", + draft201909="uri-reference", + draft202012="uri-reference", + raises=ValueError, + ) + def is_uri_reference(instance: object) -> bool: + if not isinstance(instance, str): + return True + return validate_rfc3986(instance, rule="URI_reference") + + with suppress(ImportError): + from rfc3987_syntax import is_valid_syntax as _rfc3987_is_valid_syntax + + @_checks_drafts( + draft7="iri", + draft201909="iri", + draft202012="iri", + raises=ValueError, + ) + def is_iri(instance: object) -> bool: + if not isinstance(instance, str): + return True + return _rfc3987_is_valid_syntax("iri", instance) + + @_checks_drafts( + draft7="iri-reference", + draft201909="iri-reference", + draft202012="iri-reference", + raises=ValueError, + ) + def is_iri_reference(instance: object) -> bool: + if not isinstance(instance, str): + return True + return _rfc3987_is_valid_syntax("iri_reference", instance) + +else: + + @_checks_drafts( + draft7="iri", + draft201909="iri", + draft202012="iri", + raises=ValueError, + ) + def is_iri(instance: object) -> bool: + if not isinstance(instance, str): + return True + return rfc3987.parse(instance, rule="IRI") + + @_checks_drafts( + draft7="iri-reference", + draft201909="iri-reference", + draft202012="iri-reference", + raises=ValueError, + ) + def is_iri_reference(instance: object) -> bool: + if not isinstance(instance, str): + return True + return rfc3987.parse(instance, rule="IRI_reference") + + @_checks_drafts(name="uri", raises=ValueError) + def is_uri(instance: object) -> bool: + if not isinstance(instance, str): + return True + return rfc3987.parse(instance, rule="URI") + + @_checks_drafts( + draft6="uri-reference", + draft7="uri-reference", + draft201909="uri-reference", + draft202012="uri-reference", + raises=ValueError, + ) + def is_uri_reference(instance: object) -> bool: + if not isinstance(instance, str): + return True + return rfc3987.parse(instance, rule="URI_reference") + + +with suppress(ImportError): + from rfc3339_validator import validate_rfc3339 + + @_checks_drafts(name="date-time") + def is_datetime(instance: object) -> bool: + if not isinstance(instance, str): + return True + return validate_rfc3339(instance.upper()) + + @_checks_drafts( + draft7="time", + draft201909="time", + draft202012="time", + ) + def is_time(instance: object) -> bool: + if not isinstance(instance, str): + return True + return is_datetime("1970-01-01T" + instance) + + +@_checks_drafts(name="regex", raises=re.error) +def is_regex(instance: object) -> bool: + if not isinstance(instance, str): + return True + return bool(re.compile(instance)) + + +@_checks_drafts( + draft3="date", + draft7="date", + draft201909="date", + draft202012="date", + raises=ValueError, +) +def is_date(instance: object) -> bool: + if not isinstance(instance, str): + return True + return bool(_RE_DATE.fullmatch(instance) and date.fromisoformat(instance)) + + +@_checks_drafts(draft3="time", raises=ValueError) +def is_draft3_time(instance: object) -> bool: + if not isinstance(instance, str): + return True + return bool(datetime.strptime(instance, "%H:%M:%S")) # noqa: DTZ007 + + +with suppress(ImportError): + import webcolors + + @_checks_drafts(draft3="color", raises=(ValueError, TypeError)) + def is_css21_color(instance: object) -> bool: + if isinstance(instance, str): + try: + webcolors.name_to_hex(instance) + except ValueError: + webcolors.normalize_hex(instance.lower()) + return True + + +with suppress(ImportError): + import jsonpointer + + @_checks_drafts( + draft6="json-pointer", + draft7="json-pointer", + draft201909="json-pointer", + draft202012="json-pointer", + raises=jsonpointer.JsonPointerException, + ) + def is_json_pointer(instance: object) -> bool: + if not isinstance(instance, str): + return True + return bool(jsonpointer.JsonPointer(instance)) + + # TODO: I don't want to maintain this, so it + # needs to go either into jsonpointer (pending + # https://github.com/stefankoegl/python-json-pointer/issues/34) or + # into a new external library. + @_checks_drafts( + draft7="relative-json-pointer", + draft201909="relative-json-pointer", + draft202012="relative-json-pointer", + raises=jsonpointer.JsonPointerException, + ) + def is_relative_json_pointer(instance: object) -> bool: + # Definition taken from: + # https://tools.ietf.org/html/draft-handrews-relative-json-pointer-01#section-3 + if not isinstance(instance, str): + return True + if not instance: + return False + + non_negative_integer, rest = [], "" + for i, character in enumerate(instance): + if character.isdigit(): + # digits with a leading "0" are not allowed + if i > 0 and int(instance[i - 1]) == 0: + return False + + non_negative_integer.append(character) + continue + + if not non_negative_integer: + return False + + rest = instance[i:] + break + return (rest == "#") or bool(jsonpointer.JsonPointer(rest)) + + +with suppress(ImportError): + import uri_template + + @_checks_drafts( + draft6="uri-template", + draft7="uri-template", + draft201909="uri-template", + draft202012="uri-template", + ) + def is_uri_template(instance: object) -> bool: + if not isinstance(instance, str): + return True + return uri_template.validate(instance) + + +with suppress(ImportError): + import isoduration + + @_checks_drafts( + draft201909="duration", + draft202012="duration", + raises=isoduration.DurationParsingException, + ) + def is_duration(instance: object) -> bool: + if not isinstance(instance, str): + return True + isoduration.parse_duration(instance) + # FIXME: See bolsote/isoduration#25 and bolsote/isoduration#21 + return instance.endswith(tuple("DMYWHMS")) + + +@_checks_drafts( + draft201909="uuid", + draft202012="uuid", + raises=ValueError, +) +def is_uuid(instance: object) -> bool: + if not isinstance(instance, str): + return True + UUID(instance) + return all(instance[position] == "-" for position in (8, 13, 18, 23)) diff --git a/lib/python3.12/site-packages/jsonschema/_keywords.py b/lib/python3.12/site-packages/jsonschema/_keywords.py new file mode 100644 index 0000000000000000000000000000000000000000..f30f954192abeef3a031c37ddcfd6a9831e4e247 --- /dev/null +++ b/lib/python3.12/site-packages/jsonschema/_keywords.py @@ -0,0 +1,449 @@ +from fractions import Fraction +import re + +from jsonschema._utils import ( + ensure_list, + equal, + extras_msg, + find_additional_properties, + find_evaluated_item_indexes_by_schema, + find_evaluated_property_keys_by_schema, + uniq, +) +from jsonschema.exceptions import FormatError, ValidationError + + +def patternProperties(validator, patternProperties, instance, schema): + if not validator.is_type(instance, "object"): + return + + for pattern, subschema in patternProperties.items(): + for k, v in instance.items(): + if re.search(pattern, k): + yield from validator.descend( + v, subschema, path=k, schema_path=pattern, + ) + + +def propertyNames(validator, propertyNames, instance, schema): + if not validator.is_type(instance, "object"): + return + + for property in instance: + yield from validator.descend(instance=property, schema=propertyNames) + + +def additionalProperties(validator, aP, instance, schema): + if not validator.is_type(instance, "object"): + return + + extras = set(find_additional_properties(instance, schema)) + + if validator.is_type(aP, "object"): + for extra in extras: + yield from validator.descend(instance[extra], aP, path=extra) + elif not aP and extras: + if "patternProperties" in schema: + verb = "does" if len(extras) == 1 else "do" + joined = ", ".join(repr(each) for each in sorted(extras)) + patterns = ", ".join( + repr(each) for each in sorted(schema["patternProperties"]) + ) + error = f"{joined} {verb} not match any of the regexes: {patterns}" + yield ValidationError(error) + else: + error = "Additional properties are not allowed (%s %s unexpected)" + yield ValidationError(error % extras_msg(sorted(extras, key=str))) + + +def items(validator, items, instance, schema): + if not validator.is_type(instance, "array"): + return + + prefix = len(schema.get("prefixItems", [])) + total = len(instance) + extra = total - prefix + if extra <= 0: + return + + if items is False: + rest = instance[prefix:] if extra != 1 else instance[prefix] + item = "items" if prefix != 1 else "item" + yield ValidationError( + f"Expected at most {prefix} {item} but found {extra} " + f"extra: {rest!r}", + ) + else: + for index in range(prefix, total): + yield from validator.descend( + instance=instance[index], + schema=items, + path=index, + ) + + +def const(validator, const, instance, schema): + if not equal(instance, const): + yield ValidationError(f"{const!r} was expected") + + +def contains(validator, contains, instance, schema): + if not validator.is_type(instance, "array"): + return + + matches = 0 + min_contains = schema.get("minContains", 1) + max_contains = schema.get("maxContains", len(instance)) + + contains_validator = validator.evolve(schema=contains) + + for each in instance: + if contains_validator.is_valid(each): + matches += 1 + if matches > max_contains: + yield ValidationError( + "Too many items match the given schema " + f"(expected at most {max_contains})", + validator="maxContains", + validator_value=max_contains, + ) + return + + if matches < min_contains: + if not matches: + yield ValidationError( + f"{instance!r} does not contain items " + "matching the given schema", + ) + else: + yield ValidationError( + "Too few items match the given schema (expected at least " + f"{min_contains} but only {matches} matched)", + validator="minContains", + validator_value=min_contains, + ) + + +def exclusiveMinimum(validator, minimum, instance, schema): + if not validator.is_type(instance, "number"): + return + + if instance <= minimum: + yield ValidationError( + f"{instance!r} is less than or equal to " + f"the minimum of {minimum!r}", + ) + + +def exclusiveMaximum(validator, maximum, instance, schema): + if not validator.is_type(instance, "number"): + return + + if instance >= maximum: + yield ValidationError( + f"{instance!r} is greater than or equal " + f"to the maximum of {maximum!r}", + ) + + +def minimum(validator, minimum, instance, schema): + if not validator.is_type(instance, "number"): + return + + if instance < minimum: + message = f"{instance!r} is less than the minimum of {minimum!r}" + yield ValidationError(message) + + +def maximum(validator, maximum, instance, schema): + if not validator.is_type(instance, "number"): + return + + if instance > maximum: + message = f"{instance!r} is greater than the maximum of {maximum!r}" + yield ValidationError(message) + + +def multipleOf(validator, dB, instance, schema): + if not validator.is_type(instance, "number"): + return + + if isinstance(dB, float): + quotient = instance / dB + try: + failed = int(quotient) != quotient + except OverflowError: + # When `instance` is large and `dB` is less than one, + # quotient can overflow to infinity; and then casting to int + # raises an error. + # + # In this case we fall back to Fraction logic, which is + # exact and cannot overflow. The performance is also + # acceptable: we try the fast all-float option first, and + # we know that fraction(dB) can have at most a few hundred + # digits in each part. The worst-case slowdown is therefore + # for already-slow enormous integers or Decimals. + failed = (Fraction(instance) / Fraction(dB)).denominator != 1 + else: + failed = instance % dB + + if failed: + yield ValidationError(f"{instance!r} is not a multiple of {dB}") + + +def minItems(validator, mI, instance, schema): + if validator.is_type(instance, "array") and len(instance) < mI: + message = "should be non-empty" if mI == 1 else "is too short" + yield ValidationError(f"{instance!r} {message}") + + +def maxItems(validator, mI, instance, schema): + if validator.is_type(instance, "array") and len(instance) > mI: + message = "is expected to be empty" if mI == 0 else "is too long" + yield ValidationError(f"{instance!r} {message}") + + +def uniqueItems(validator, uI, instance, schema): + if ( + uI + and validator.is_type(instance, "array") + and not uniq(instance) + ): + yield ValidationError(f"{instance!r} has non-unique elements") + + +def pattern(validator, patrn, instance, schema): + if ( + validator.is_type(instance, "string") + and not re.search(patrn, instance) + ): + yield ValidationError(f"{instance!r} does not match {patrn!r}") + + +def format(validator, format, instance, schema): + if validator.format_checker is not None: + try: + validator.format_checker.check(instance, format) + except FormatError as error: + yield ValidationError(error.message, cause=error.cause) + + +def minLength(validator, mL, instance, schema): + if validator.is_type(instance, "string") and len(instance) < mL: + message = "should be non-empty" if mL == 1 else "is too short" + yield ValidationError(f"{instance!r} {message}") + + +def maxLength(validator, mL, instance, schema): + if validator.is_type(instance, "string") and len(instance) > mL: + message = "is expected to be empty" if mL == 0 else "is too long" + yield ValidationError(f"{instance!r} {message}") + + +def dependentRequired(validator, dependentRequired, instance, schema): + if not validator.is_type(instance, "object"): + return + + for property, dependency in dependentRequired.items(): + if property not in instance: + continue + + for each in dependency: + if each not in instance: + message = f"{each!r} is a dependency of {property!r}" + yield ValidationError(message) + + +def dependentSchemas(validator, dependentSchemas, instance, schema): + if not validator.is_type(instance, "object"): + return + + for property, dependency in dependentSchemas.items(): + if property not in instance: + continue + yield from validator.descend( + instance, dependency, schema_path=property, + ) + + +def enum(validator, enums, instance, schema): + if all(not equal(each, instance) for each in enums): + yield ValidationError(f"{instance!r} is not one of {enums!r}") + + +def ref(validator, ref, instance, schema): + yield from validator._validate_reference(ref=ref, instance=instance) + + +def dynamicRef(validator, dynamicRef, instance, schema): + yield from validator._validate_reference(ref=dynamicRef, instance=instance) + + +def type(validator, types, instance, schema): + types = ensure_list(types) + + if not any(validator.is_type(instance, type) for type in types): + reprs = ", ".join(repr(type) for type in types) + yield ValidationError(f"{instance!r} is not of type {reprs}") + + +def properties(validator, properties, instance, schema): + if not validator.is_type(instance, "object"): + return + + for property, subschema in properties.items(): + if property in instance: + yield from validator.descend( + instance[property], + subschema, + path=property, + schema_path=property, + ) + + +def required(validator, required, instance, schema): + if not validator.is_type(instance, "object"): + return + for property in required: + if property not in instance: + yield ValidationError(f"{property!r} is a required property") + + +def minProperties(validator, mP, instance, schema): + if validator.is_type(instance, "object") and len(instance) < mP: + message = ( + "should be non-empty" if mP == 1 + else "does not have enough properties" + ) + yield ValidationError(f"{instance!r} {message}") + + +def maxProperties(validator, mP, instance, schema): + if not validator.is_type(instance, "object"): + return + if validator.is_type(instance, "object") and len(instance) > mP: + message = ( + "is expected to be empty" if mP == 0 + else "has too many properties" + ) + yield ValidationError(f"{instance!r} {message}") + + +def allOf(validator, allOf, instance, schema): + for index, subschema in enumerate(allOf): + yield from validator.descend(instance, subschema, schema_path=index) + + +def anyOf(validator, anyOf, instance, schema): + all_errors = [] + for index, subschema in enumerate(anyOf): + errs = list(validator.descend(instance, subschema, schema_path=index)) + if not errs: + break + all_errors.extend(errs) + else: + yield ValidationError( + f"{instance!r} is not valid under any of the given schemas", + context=all_errors, + ) + + +def oneOf(validator, oneOf, instance, schema): + subschemas = enumerate(oneOf) + all_errors = [] + for index, subschema in subschemas: + errs = list(validator.descend(instance, subschema, schema_path=index)) + if not errs: + first_valid = subschema + break + all_errors.extend(errs) + else: + yield ValidationError( + f"{instance!r} is not valid under any of the given schemas", + context=all_errors, + ) + + more_valid = [ + each for _, each in subschemas + if validator.evolve(schema=each).is_valid(instance) + ] + if more_valid: + more_valid.append(first_valid) + reprs = ", ".join(repr(schema) for schema in more_valid) + yield ValidationError(f"{instance!r} is valid under each of {reprs}") + + +def not_(validator, not_schema, instance, schema): + if validator.evolve(schema=not_schema).is_valid(instance): + message = f"{instance!r} should not be valid under {not_schema!r}" + yield ValidationError(message) + + +def if_(validator, if_schema, instance, schema): + if validator.evolve(schema=if_schema).is_valid(instance): + if "then" in schema: + then = schema["then"] + yield from validator.descend(instance, then, schema_path="then") + elif "else" in schema: + else_ = schema["else"] + yield from validator.descend(instance, else_, schema_path="else") + + +def unevaluatedItems(validator, unevaluatedItems, instance, schema): + if not validator.is_type(instance, "array"): + return + evaluated_item_indexes = find_evaluated_item_indexes_by_schema( + validator, instance, schema, + ) + unevaluated_items = [ + item for index, item in enumerate(instance) + if index not in evaluated_item_indexes + ] + if unevaluated_items: + error = "Unevaluated items are not allowed (%s %s unexpected)" + yield ValidationError(error % extras_msg(unevaluated_items)) + + +def unevaluatedProperties(validator, unevaluatedProperties, instance, schema): + if not validator.is_type(instance, "object"): + return + evaluated_keys = find_evaluated_property_keys_by_schema( + validator, instance, schema, + ) + unevaluated_keys = [] + for property in instance: + if property not in evaluated_keys: + for _ in validator.descend( + instance[property], + unevaluatedProperties, + path=property, + schema_path=property, + ): + # FIXME: Include context for each unevaluated property + # indicating why it's invalid under the subschema. + unevaluated_keys.append(property) # noqa: PERF401 + + if unevaluated_keys: + if unevaluatedProperties is False: + error = "Unevaluated properties are not allowed (%s %s unexpected)" + extras = sorted(unevaluated_keys, key=str) + yield ValidationError(error % extras_msg(extras)) + else: + error = ( + "Unevaluated properties are not valid under " + "the given schema (%s %s unevaluated and invalid)" + ) + yield ValidationError(error % extras_msg(unevaluated_keys)) + + +def prefixItems(validator, prefixItems, instance, schema): + if not validator.is_type(instance, "array"): + return + + for (index, item), subschema in zip(enumerate(instance), prefixItems): + yield from validator.descend( + instance=item, + schema=subschema, + schema_path=index, + path=index, + ) diff --git a/lib/python3.12/site-packages/jsonschema/_legacy_keywords.py b/lib/python3.12/site-packages/jsonschema/_legacy_keywords.py new file mode 100644 index 0000000000000000000000000000000000000000..c691589f8f69fdbbc0152728121361665de08f00 --- /dev/null +++ b/lib/python3.12/site-packages/jsonschema/_legacy_keywords.py @@ -0,0 +1,449 @@ +import re + +from referencing.jsonschema import lookup_recursive_ref + +from jsonschema import _utils +from jsonschema.exceptions import ValidationError + + +def ignore_ref_siblings(schema): + """ + Ignore siblings of ``$ref`` if it is present. + + Otherwise, return all keywords. + + Suitable for use with `create`'s ``applicable_validators`` argument. + """ + ref = schema.get("$ref") + if ref is not None: + return [("$ref", ref)] + else: + return schema.items() + + +def dependencies_draft3(validator, dependencies, instance, schema): + if not validator.is_type(instance, "object"): + return + + for property, dependency in dependencies.items(): + if property not in instance: + continue + + if validator.is_type(dependency, "object"): + yield from validator.descend( + instance, dependency, schema_path=property, + ) + elif validator.is_type(dependency, "string"): + if dependency not in instance: + message = f"{dependency!r} is a dependency of {property!r}" + yield ValidationError(message) + else: + for each in dependency: + if each not in instance: + message = f"{each!r} is a dependency of {property!r}" + yield ValidationError(message) + + +def dependencies_draft4_draft6_draft7( + validator, + dependencies, + instance, + schema, +): + """ + Support for the ``dependencies`` keyword from pre-draft 2019-09. + + In later drafts, the keyword was split into separate + ``dependentRequired`` and ``dependentSchemas`` validators. + """ + if not validator.is_type(instance, "object"): + return + + for property, dependency in dependencies.items(): + if property not in instance: + continue + + if validator.is_type(dependency, "array"): + for each in dependency: + if each not in instance: + message = f"{each!r} is a dependency of {property!r}" + yield ValidationError(message) + else: + yield from validator.descend( + instance, dependency, schema_path=property, + ) + + +def disallow_draft3(validator, disallow, instance, schema): + for disallowed in _utils.ensure_list(disallow): + if validator.evolve(schema={"type": [disallowed]}).is_valid(instance): + message = f"{disallowed!r} is disallowed for {instance!r}" + yield ValidationError(message) + + +def extends_draft3(validator, extends, instance, schema): + if validator.is_type(extends, "object"): + yield from validator.descend(instance, extends) + return + for index, subschema in enumerate(extends): + yield from validator.descend(instance, subschema, schema_path=index) + + +def items_draft3_draft4(validator, items, instance, schema): + if not validator.is_type(instance, "array"): + return + + if validator.is_type(items, "object"): + for index, item in enumerate(instance): + yield from validator.descend(item, items, path=index) + else: + for (index, item), subschema in zip(enumerate(instance), items): + yield from validator.descend( + item, subschema, path=index, schema_path=index, + ) + + +def additionalItems(validator, aI, instance, schema): + if ( + not validator.is_type(instance, "array") + or validator.is_type(schema.get("items", {}), "object") + ): + return + + len_items = len(schema.get("items", [])) + if validator.is_type(aI, "object"): + for index, item in enumerate(instance[len_items:], start=len_items): + yield from validator.descend(item, aI, path=index) + elif not aI and len(instance) > len(schema.get("items", [])): + error = "Additional items are not allowed (%s %s unexpected)" + yield ValidationError( + error % _utils.extras_msg(instance[len(schema.get("items", [])):]), + ) + + +def items_draft6_draft7_draft201909(validator, items, instance, schema): + if not validator.is_type(instance, "array"): + return + + if validator.is_type(items, "array"): + for (index, item), subschema in zip(enumerate(instance), items): + yield from validator.descend( + item, subschema, path=index, schema_path=index, + ) + else: + for index, item in enumerate(instance): + yield from validator.descend(item, items, path=index) + + +def minimum_draft3_draft4(validator, minimum, instance, schema): + if not validator.is_type(instance, "number"): + return + + if schema.get("exclusiveMinimum", False): + failed = instance <= minimum + cmp = "less than or equal to" + else: + failed = instance < minimum + cmp = "less than" + + if failed: + message = f"{instance!r} is {cmp} the minimum of {minimum!r}" + yield ValidationError(message) + + +def maximum_draft3_draft4(validator, maximum, instance, schema): + if not validator.is_type(instance, "number"): + return + + if schema.get("exclusiveMaximum", False): + failed = instance >= maximum + cmp = "greater than or equal to" + else: + failed = instance > maximum + cmp = "greater than" + + if failed: + message = f"{instance!r} is {cmp} the maximum of {maximum!r}" + yield ValidationError(message) + + +def properties_draft3(validator, properties, instance, schema): + if not validator.is_type(instance, "object"): + return + + for property, subschema in properties.items(): + if property in instance: + yield from validator.descend( + instance[property], + subschema, + path=property, + schema_path=property, + ) + elif subschema.get("required", False): + error = ValidationError(f"{property!r} is a required property") + error._set( + validator="required", + validator_value=subschema["required"], + instance=instance, + schema=schema, + ) + error.path.appendleft(property) + error.schema_path.extend([property, "required"]) + yield error + + +def type_draft3(validator, types, instance, schema): + types = _utils.ensure_list(types) + + all_errors = [] + for index, type in enumerate(types): + if validator.is_type(type, "object"): + errors = list(validator.descend(instance, type, schema_path=index)) + if not errors: + return + all_errors.extend(errors) + elif validator.is_type(instance, type): + return + + reprs = [] + for type in types: + try: + reprs.append(repr(type["name"])) + except Exception: # noqa: BLE001 + reprs.append(repr(type)) + yield ValidationError( + f"{instance!r} is not of type {', '.join(reprs)}", + context=all_errors, + ) + + +def contains_draft6_draft7(validator, contains, instance, schema): + if not validator.is_type(instance, "array"): + return + + if not any( + validator.evolve(schema=contains).is_valid(element) + for element in instance + ): + yield ValidationError( + f"None of {instance!r} are valid under the given schema", + ) + + +def recursiveRef(validator, recursiveRef, instance, schema): + resolved = lookup_recursive_ref(validator._resolver) + yield from validator.descend( + instance, + resolved.contents, + resolver=resolved.resolver, + ) + + +def find_evaluated_item_indexes_by_schema(validator, instance, schema): + """ + Get all indexes of items that get evaluated under the current schema. + + Covers all keywords related to unevaluatedItems: items, prefixItems, if, + then, else, contains, unevaluatedItems, allOf, oneOf, anyOf + """ + if validator.is_type(schema, "boolean"): + return [] + evaluated_indexes = [] + + ref = schema.get("$ref") + if ref is not None: + resolved = validator._resolver.lookup(ref) + evaluated_indexes.extend( + find_evaluated_item_indexes_by_schema( + validator.evolve( + schema=resolved.contents, + _resolver=resolved.resolver, + ), + instance, + resolved.contents, + ), + ) + + if "$recursiveRef" in schema: + resolved = lookup_recursive_ref(validator._resolver) + evaluated_indexes.extend( + find_evaluated_item_indexes_by_schema( + validator.evolve( + schema=resolved.contents, + _resolver=resolved.resolver, + ), + instance, + resolved.contents, + ), + ) + + if "items" in schema: + if "additionalItems" in schema: + return list(range(len(instance))) + + if validator.is_type(schema["items"], "object"): + return list(range(len(instance))) + evaluated_indexes += list(range(len(schema["items"]))) + + if "if" in schema: + if validator.evolve(schema=schema["if"]).is_valid(instance): + evaluated_indexes += find_evaluated_item_indexes_by_schema( + validator, instance, schema["if"], + ) + if "then" in schema: + evaluated_indexes += find_evaluated_item_indexes_by_schema( + validator, instance, schema["then"], + ) + elif "else" in schema: + evaluated_indexes += find_evaluated_item_indexes_by_schema( + validator, instance, schema["else"], + ) + + for keyword in ["contains", "unevaluatedItems"]: + if keyword in schema: + for k, v in enumerate(instance): + if validator.evolve(schema=schema[keyword]).is_valid(v): + evaluated_indexes.append(k) + + for keyword in ["allOf", "oneOf", "anyOf"]: + if keyword in schema: + for subschema in schema[keyword]: + errs = next(validator.descend(instance, subschema), None) + if errs is None: + evaluated_indexes += find_evaluated_item_indexes_by_schema( + validator, instance, subschema, + ) + + return evaluated_indexes + + +def unevaluatedItems_draft2019(validator, unevaluatedItems, instance, schema): + if not validator.is_type(instance, "array"): + return + evaluated_item_indexes = find_evaluated_item_indexes_by_schema( + validator, instance, schema, + ) + unevaluated_items = [ + item for index, item in enumerate(instance) + if index not in evaluated_item_indexes + ] + if unevaluated_items: + error = "Unevaluated items are not allowed (%s %s unexpected)" + yield ValidationError(error % _utils.extras_msg(unevaluated_items)) + + +def find_evaluated_property_keys_by_schema(validator, instance, schema): + if validator.is_type(schema, "boolean"): + return [] + evaluated_keys = [] + + ref = schema.get("$ref") + if ref is not None: + resolved = validator._resolver.lookup(ref) + evaluated_keys.extend( + find_evaluated_property_keys_by_schema( + validator.evolve( + schema=resolved.contents, + _resolver=resolved.resolver, + ), + instance, + resolved.contents, + ), + ) + + if "$recursiveRef" in schema: + resolved = lookup_recursive_ref(validator._resolver) + evaluated_keys.extend( + find_evaluated_property_keys_by_schema( + validator.evolve( + schema=resolved.contents, + _resolver=resolved.resolver, + ), + instance, + resolved.contents, + ), + ) + + for keyword in [ + "properties", "additionalProperties", "unevaluatedProperties", + ]: + if keyword in schema: + schema_value = schema[keyword] + if validator.is_type(schema_value, "boolean") and schema_value: + evaluated_keys += instance.keys() + + elif validator.is_type(schema_value, "object"): + for property in schema_value: + if property in instance: + evaluated_keys.append(property) + + if "patternProperties" in schema: + for property in instance: + for pattern in schema["patternProperties"]: + if re.search(pattern, property): + evaluated_keys.append(property) + + if "dependentSchemas" in schema: + for property, subschema in schema["dependentSchemas"].items(): + if property not in instance: + continue + evaluated_keys += find_evaluated_property_keys_by_schema( + validator, instance, subschema, + ) + + for keyword in ["allOf", "oneOf", "anyOf"]: + if keyword in schema: + for subschema in schema[keyword]: + errs = next(validator.descend(instance, subschema), None) + if errs is None: + evaluated_keys += find_evaluated_property_keys_by_schema( + validator, instance, subschema, + ) + + if "if" in schema: + if validator.evolve(schema=schema["if"]).is_valid(instance): + evaluated_keys += find_evaluated_property_keys_by_schema( + validator, instance, schema["if"], + ) + if "then" in schema: + evaluated_keys += find_evaluated_property_keys_by_schema( + validator, instance, schema["then"], + ) + elif "else" in schema: + evaluated_keys += find_evaluated_property_keys_by_schema( + validator, instance, schema["else"], + ) + + return evaluated_keys + + +def unevaluatedProperties_draft2019(validator, uP, instance, schema): + if not validator.is_type(instance, "object"): + return + evaluated_keys = find_evaluated_property_keys_by_schema( + validator, instance, schema, + ) + unevaluated_keys = [] + for property in instance: + if property not in evaluated_keys: + for _ in validator.descend( + instance[property], + uP, + path=property, + schema_path=property, + ): + # FIXME: Include context for each unevaluated property + # indicating why it's invalid under the subschema. + unevaluated_keys.append(property) # noqa: PERF401 + + if unevaluated_keys: + if uP is False: + error = "Unevaluated properties are not allowed (%s %s unexpected)" + extras = sorted(unevaluated_keys, key=str) + yield ValidationError(error % _utils.extras_msg(extras)) + else: + error = ( + "Unevaluated properties are not valid under " + "the given schema (%s %s unevaluated and invalid)" + ) + yield ValidationError(error % _utils.extras_msg(unevaluated_keys)) diff --git a/lib/python3.12/site-packages/jsonschema/_types.py b/lib/python3.12/site-packages/jsonschema/_types.py new file mode 100644 index 0000000000000000000000000000000000000000..fcfe3f09ccab28c462f27630c519bed2408bdc18 --- /dev/null +++ b/lib/python3.12/site-packages/jsonschema/_types.py @@ -0,0 +1,204 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING +import numbers + +from attrs import evolve, field, frozen +from rpds import HashTrieMap + +from jsonschema.exceptions import UndefinedTypeCheck + +if TYPE_CHECKING: + from collections.abc import Callable, Mapping + from typing import Any + + +# unfortunately, the type of HashTrieMap is generic, and if used as an attrs +# converter, the generic type is presented to mypy, which then fails to match +# the concrete type of a type checker mapping +# this "do nothing" wrapper presents the correct information to mypy +def _typed_map_converter( + init_val: Mapping[str, Callable[[TypeChecker, Any], bool]], +) -> HashTrieMap[str, Callable[[TypeChecker, Any], bool]]: + return HashTrieMap.convert(init_val) + + +def is_array(checker, instance): + return isinstance(instance, list) + + +def is_bool(checker, instance): + return isinstance(instance, bool) + + +def is_integer(checker, instance): + # bool inherits from int, so ensure bools aren't reported as ints + if isinstance(instance, bool): + return False + return isinstance(instance, int) + + +def is_null(checker, instance): + return instance is None + + +def is_number(checker, instance): + # bool inherits from int, so ensure bools aren't reported as ints + if isinstance(instance, bool): + return False + return isinstance(instance, numbers.Number) + + +def is_object(checker, instance): + return isinstance(instance, dict) + + +def is_string(checker, instance): + return isinstance(instance, str) + + +def is_any(checker, instance): + return True + + +@frozen(repr=False) +class TypeChecker: + """ + A :kw:`type` property checker. + + A `TypeChecker` performs type checking for a `Validator`, converting + between the defined JSON Schema types and some associated Python types or + objects. + + Modifying the behavior just mentioned by redefining which Python objects + are considered to be of which JSON Schema types can be done using + `TypeChecker.redefine` or `TypeChecker.redefine_many`, and types can be + removed via `TypeChecker.remove`. Each of these return a new `TypeChecker`. + + Arguments: + + type_checkers: + + The initial mapping of types to their checking functions. + + """ + + _type_checkers: HashTrieMap[ + str, Callable[[TypeChecker, Any], bool], + ] = field(default=HashTrieMap(), converter=_typed_map_converter) + + def __repr__(self): + types = ", ".join(repr(k) for k in sorted(self._type_checkers)) + return f"<{self.__class__.__name__} types={{{types}}}>" + + def is_type(self, instance, type: str) -> bool: + """ + Check if the instance is of the appropriate type. + + Arguments: + + instance: + + The instance to check + + type: + + The name of the type that is expected. + + Raises: + + `jsonschema.exceptions.UndefinedTypeCheck`: + + if ``type`` is unknown to this object. + + """ + try: + fn = self._type_checkers[type] + except KeyError: + raise UndefinedTypeCheck(type) from None + + return fn(self, instance) + + def redefine(self, type: str, fn) -> TypeChecker: + """ + Produce a new checker with the given type redefined. + + Arguments: + + type: + + The name of the type to check. + + fn (collections.abc.Callable): + + A callable taking exactly two parameters - the type + checker calling the function and the instance to check. + The function should return true if instance is of this + type and false otherwise. + + """ + return self.redefine_many({type: fn}) + + def redefine_many(self, definitions=()) -> TypeChecker: + """ + Produce a new checker with the given types redefined. + + Arguments: + + definitions (dict): + + A dictionary mapping types to their checking functions. + + """ + type_checkers = self._type_checkers.update(definitions) + return evolve(self, type_checkers=type_checkers) + + def remove(self, *types) -> TypeChecker: + """ + Produce a new checker with the given types forgotten. + + Arguments: + + types: + + the names of the types to remove. + + Raises: + + `jsonschema.exceptions.UndefinedTypeCheck`: + + if any given type is unknown to this object + + """ + type_checkers = self._type_checkers + for each in types: + try: + type_checkers = type_checkers.remove(each) + except KeyError: + raise UndefinedTypeCheck(each) from None + return evolve(self, type_checkers=type_checkers) + + +draft3_type_checker = TypeChecker( + { + "any": is_any, + "array": is_array, + "boolean": is_bool, + "integer": is_integer, + "object": is_object, + "null": is_null, + "number": is_number, + "string": is_string, + }, +) +draft4_type_checker = draft3_type_checker.remove("any") +draft6_type_checker = draft4_type_checker.redefine( + "integer", + lambda checker, instance: ( + is_integer(checker, instance) + or (isinstance(instance, float) and instance.is_integer()) + ), +) +draft7_type_checker = draft6_type_checker +draft201909_type_checker = draft7_type_checker +draft202012_type_checker = draft201909_type_checker diff --git a/lib/python3.12/site-packages/jsonschema/_typing.py b/lib/python3.12/site-packages/jsonschema/_typing.py new file mode 100644 index 0000000000000000000000000000000000000000..f8dda63a775bbc092d2a430524086f05e6e15670 --- /dev/null +++ b/lib/python3.12/site-packages/jsonschema/_typing.py @@ -0,0 +1,29 @@ +""" +Some (initially private) typing helpers for jsonschema's types. +""" +from collections.abc import Callable, Iterable +from typing import Any, Protocol + +import referencing.jsonschema + +from jsonschema.protocols import Validator + + +class SchemaKeywordValidator(Protocol): + def __call__( + self, + validator: Validator, + value: Any, + instance: Any, + schema: referencing.jsonschema.Schema, + ) -> None: + ... + + +id_of = Callable[[referencing.jsonschema.Schema], str | None] + + +ApplicableValidators = Callable[ + [referencing.jsonschema.Schema], + Iterable[tuple[str, Any]], +] diff --git a/lib/python3.12/site-packages/jsonschema/_utils.py b/lib/python3.12/site-packages/jsonschema/_utils.py new file mode 100644 index 0000000000000000000000000000000000000000..84a0965e50aef3106c8dfce8762cb75297855d82 --- /dev/null +++ b/lib/python3.12/site-packages/jsonschema/_utils.py @@ -0,0 +1,355 @@ +from collections.abc import Mapping, MutableMapping, Sequence +from urllib.parse import urlsplit +import itertools +import re + + +class URIDict(MutableMapping): + """ + Dictionary which uses normalized URIs as keys. + """ + + def normalize(self, uri): + return urlsplit(uri).geturl() + + def __init__(self, *args, **kwargs): + self.store = dict() + self.store.update(*args, **kwargs) + + def __getitem__(self, uri): + return self.store[self.normalize(uri)] + + def __setitem__(self, uri, value): + self.store[self.normalize(uri)] = value + + def __delitem__(self, uri): + del self.store[self.normalize(uri)] + + def __iter__(self): + return iter(self.store) + + def __len__(self): # pragma: no cover -- untested, but to be removed + return len(self.store) + + def __repr__(self): # pragma: no cover -- untested, but to be removed + return repr(self.store) + + +class Unset: + """ + An as-of-yet unset attribute or unprovided default parameter. + """ + + def __repr__(self): # pragma: no cover + return "" + + +def format_as_index(container, indices): + """ + Construct a single string containing indexing operations for the indices. + + For example for a container ``bar``, [1, 2, "foo"] -> bar[1][2]["foo"] + + Arguments: + + container (str): + + A word to use for the thing being indexed + + indices (sequence): + + The indices to format. + + """ + if not indices: + return container + return f"{container}[{']['.join(repr(index) for index in indices)}]" + + +def find_additional_properties(instance, schema): + """ + Return the set of additional properties for the given ``instance``. + + Weeds out properties that should have been validated by ``properties`` and + / or ``patternProperties``. + + Assumes ``instance`` is dict-like already. + """ + properties = schema.get("properties", {}) + patterns = "|".join(schema.get("patternProperties", {})) + for property in instance: + if property not in properties: + if patterns and re.search(patterns, property): + continue + yield property + + +def extras_msg(extras): + """ + Create an error message for extra items or properties. + """ + verb = "was" if len(extras) == 1 else "were" + return ", ".join(repr(extra) for extra in extras), verb + + +def ensure_list(thing): + """ + Wrap ``thing`` in a list if it's a single str. + + Otherwise, return it unchanged. + """ + if isinstance(thing, str): + return [thing] + return thing + + +def _mapping_equal(one, two): + """ + Check if two mappings are equal using the semantics of `equal`. + """ + if len(one) != len(two): + return False + return all( + key in two and equal(value, two[key]) + for key, value in one.items() + ) + + +def _sequence_equal(one, two): + """ + Check if two sequences are equal using the semantics of `equal`. + """ + if len(one) != len(two): + return False + return all(equal(i, j) for i, j in zip(one, two)) + + +def equal(one, two): + """ + Check if two things are equal evading some Python type hierarchy semantics. + + Specifically in JSON Schema, evade `bool` inheriting from `int`, + recursing into sequences to do the same. + """ + if one is two: + return True + if isinstance(one, str) or isinstance(two, str): + return one == two + if isinstance(one, Sequence) and isinstance(two, Sequence): + return _sequence_equal(one, two) + if isinstance(one, Mapping) and isinstance(two, Mapping): + return _mapping_equal(one, two) + return unbool(one) == unbool(two) + + +def unbool(element, true=object(), false=object()): + """ + A hack to make True and 1 and False and 0 unique for ``uniq``. + """ + if element is True: + return true + elif element is False: + return false + return element + + +def uniq(container): + """ + Check if all of a container's elements are unique. + + Tries to rely on the container being recursively sortable, or otherwise + falls back on (slow) brute force. + """ + try: + sort = sorted(unbool(i) for i in container) + sliced = itertools.islice(sort, 1, None) + + for i, j in zip(sort, sliced): + if equal(i, j): + return False + + except (NotImplementedError, TypeError): + seen = [] + for e in container: + e = unbool(e) + + for i in seen: + if equal(i, e): + return False + + seen.append(e) + return True + + +def find_evaluated_item_indexes_by_schema(validator, instance, schema): + """ + Get all indexes of items that get evaluated under the current schema. + + Covers all keywords related to unevaluatedItems: items, prefixItems, if, + then, else, contains, unevaluatedItems, allOf, oneOf, anyOf + """ + if validator.is_type(schema, "boolean"): + return [] + evaluated_indexes = [] + + if "items" in schema: + return list(range(len(instance))) + + ref = schema.get("$ref") + if ref is not None: + resolved = validator._resolver.lookup(ref) + evaluated_indexes.extend( + find_evaluated_item_indexes_by_schema( + validator.evolve( + schema=resolved.contents, + _resolver=resolved.resolver, + ), + instance, + resolved.contents, + ), + ) + + dynamicRef = schema.get("$dynamicRef") + if dynamicRef is not None: + resolved = validator._resolver.lookup(dynamicRef) + evaluated_indexes.extend( + find_evaluated_item_indexes_by_schema( + validator.evolve( + schema=resolved.contents, + _resolver=resolved.resolver, + ), + instance, + resolved.contents, + ), + ) + + if "prefixItems" in schema: + evaluated_indexes += list(range(len(schema["prefixItems"]))) + + if "if" in schema: + if validator.evolve(schema=schema["if"]).is_valid(instance): + evaluated_indexes += find_evaluated_item_indexes_by_schema( + validator, instance, schema["if"], + ) + if "then" in schema: + evaluated_indexes += find_evaluated_item_indexes_by_schema( + validator, instance, schema["then"], + ) + elif "else" in schema: + evaluated_indexes += find_evaluated_item_indexes_by_schema( + validator, instance, schema["else"], + ) + + for keyword in ["contains", "unevaluatedItems"]: + if keyword in schema: + for k, v in enumerate(instance): + if validator.evolve(schema=schema[keyword]).is_valid(v): + evaluated_indexes.append(k) + + for keyword in ["allOf", "oneOf", "anyOf"]: + if keyword in schema: + for subschema in schema[keyword]: + errs = next(validator.descend(instance, subschema), None) + if errs is None: + evaluated_indexes += find_evaluated_item_indexes_by_schema( + validator, instance, subschema, + ) + + return evaluated_indexes + + +def find_evaluated_property_keys_by_schema(validator, instance, schema): + """ + Get all keys of items that get evaluated under the current schema. + + Covers all keywords related to unevaluatedProperties: properties, + additionalProperties, unevaluatedProperties, patternProperties, + dependentSchemas, allOf, oneOf, anyOf, if, then, else + """ + if validator.is_type(schema, "boolean"): + return [] + evaluated_keys = [] + + ref = schema.get("$ref") + if ref is not None: + resolved = validator._resolver.lookup(ref) + evaluated_keys.extend( + find_evaluated_property_keys_by_schema( + validator.evolve( + schema=resolved.contents, + _resolver=resolved.resolver, + ), + instance, + resolved.contents, + ), + ) + + dynamicRef = schema.get("$dynamicRef") + if dynamicRef is not None: + resolved = validator._resolver.lookup(dynamicRef) + evaluated_keys.extend( + find_evaluated_property_keys_by_schema( + validator.evolve( + schema=resolved.contents, + _resolver=resolved.resolver, + ), + instance, + resolved.contents, + ), + ) + + properties = schema.get("properties") + if validator.is_type(properties, "object"): + evaluated_keys += properties.keys() & instance.keys() + + for keyword in ["additionalProperties", "unevaluatedProperties"]: + if (subschema := schema.get(keyword)) is None: + continue + evaluated_keys += ( + key + for key, value in instance.items() + if is_valid(validator.descend(value, subschema)) + ) + + if "patternProperties" in schema: + for property in instance: + for pattern in schema["patternProperties"]: + if re.search(pattern, property): + evaluated_keys.append(property) + + if "dependentSchemas" in schema: + for property, subschema in schema["dependentSchemas"].items(): + if property not in instance: + continue + evaluated_keys += find_evaluated_property_keys_by_schema( + validator, instance, subschema, + ) + + for keyword in ["allOf", "oneOf", "anyOf"]: + for subschema in schema.get(keyword, []): + if not is_valid(validator.descend(instance, subschema)): + continue + evaluated_keys += find_evaluated_property_keys_by_schema( + validator, instance, subschema, + ) + + if "if" in schema: + if validator.evolve(schema=schema["if"]).is_valid(instance): + evaluated_keys += find_evaluated_property_keys_by_schema( + validator, instance, schema["if"], + ) + if "then" in schema: + evaluated_keys += find_evaluated_property_keys_by_schema( + validator, instance, schema["then"], + ) + elif "else" in schema: + evaluated_keys += find_evaluated_property_keys_by_schema( + validator, instance, schema["else"], + ) + + return evaluated_keys + + +def is_valid(errs_it): + """Whether there are no errors in the given iterator.""" + return next(errs_it, None) is None diff --git a/lib/python3.12/site-packages/jsonschema/benchmarks/__init__.py b/lib/python3.12/site-packages/jsonschema/benchmarks/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e3dcc689930da95198c251ce806637d6413c8b1e --- /dev/null +++ b/lib/python3.12/site-packages/jsonschema/benchmarks/__init__.py @@ -0,0 +1,5 @@ +""" +Benchmarks for validation. + +This package is *not* public API. +""" diff --git a/lib/python3.12/site-packages/jsonschema/benchmarks/__pycache__/__init__.cpython-312.pyc b/lib/python3.12/site-packages/jsonschema/benchmarks/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..59a80243947a37b4d92fb25fc0473709cfcec0fa Binary files /dev/null and b/lib/python3.12/site-packages/jsonschema/benchmarks/__pycache__/__init__.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/jsonschema/benchmarks/__pycache__/const_vs_enum.cpython-312.pyc b/lib/python3.12/site-packages/jsonschema/benchmarks/__pycache__/const_vs_enum.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bb5343a062ba06adb9af7cd89bf329edd018b935 Binary files /dev/null and b/lib/python3.12/site-packages/jsonschema/benchmarks/__pycache__/const_vs_enum.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/jsonschema/benchmarks/__pycache__/contains.cpython-312.pyc b/lib/python3.12/site-packages/jsonschema/benchmarks/__pycache__/contains.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..080f72ccb7e25a98754e893f3559d5d2c432299d Binary files /dev/null and b/lib/python3.12/site-packages/jsonschema/benchmarks/__pycache__/contains.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/jsonschema/benchmarks/__pycache__/import_benchmark.cpython-312.pyc b/lib/python3.12/site-packages/jsonschema/benchmarks/__pycache__/import_benchmark.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4564e3aa910c380b73e5478f5febbb84f36f0d1e Binary files /dev/null and b/lib/python3.12/site-packages/jsonschema/benchmarks/__pycache__/import_benchmark.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/jsonschema/benchmarks/__pycache__/issue232.cpython-312.pyc b/lib/python3.12/site-packages/jsonschema/benchmarks/__pycache__/issue232.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8c7037ab5dd18fd24c1922396955b5cd031bb6c5 Binary files /dev/null and b/lib/python3.12/site-packages/jsonschema/benchmarks/__pycache__/issue232.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/jsonschema/benchmarks/__pycache__/json_schema_test_suite.cpython-312.pyc b/lib/python3.12/site-packages/jsonschema/benchmarks/__pycache__/json_schema_test_suite.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a6af047d33863815d71205aa78c20ac81daf772d Binary files /dev/null and b/lib/python3.12/site-packages/jsonschema/benchmarks/__pycache__/json_schema_test_suite.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/jsonschema/benchmarks/__pycache__/nested_schemas.cpython-312.pyc b/lib/python3.12/site-packages/jsonschema/benchmarks/__pycache__/nested_schemas.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..aeac1d2d07d8a339354a102c70545c54c02dad57 Binary files /dev/null and b/lib/python3.12/site-packages/jsonschema/benchmarks/__pycache__/nested_schemas.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/jsonschema/benchmarks/__pycache__/subcomponents.cpython-312.pyc b/lib/python3.12/site-packages/jsonschema/benchmarks/__pycache__/subcomponents.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3f34c0032292bdf0d3bf4b8fd6c018c00b86450e Binary files /dev/null and b/lib/python3.12/site-packages/jsonschema/benchmarks/__pycache__/subcomponents.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/jsonschema/benchmarks/__pycache__/unused_registry.cpython-312.pyc b/lib/python3.12/site-packages/jsonschema/benchmarks/__pycache__/unused_registry.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6f9ede4d2066aca38d6e82ee06e98c82b58bef85 Binary files /dev/null and b/lib/python3.12/site-packages/jsonschema/benchmarks/__pycache__/unused_registry.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/jsonschema/benchmarks/__pycache__/useless_applicator_schemas.cpython-312.pyc b/lib/python3.12/site-packages/jsonschema/benchmarks/__pycache__/useless_applicator_schemas.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3aa9495807ff938ca12df0139a2268cca38922bd Binary files /dev/null and b/lib/python3.12/site-packages/jsonschema/benchmarks/__pycache__/useless_applicator_schemas.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/jsonschema/benchmarks/__pycache__/useless_keywords.cpython-312.pyc b/lib/python3.12/site-packages/jsonschema/benchmarks/__pycache__/useless_keywords.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..194ac14798c32565ac9cb67b099d0735470652fd Binary files /dev/null and b/lib/python3.12/site-packages/jsonschema/benchmarks/__pycache__/useless_keywords.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/jsonschema/benchmarks/__pycache__/validator_creation.cpython-312.pyc b/lib/python3.12/site-packages/jsonschema/benchmarks/__pycache__/validator_creation.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d4153a61883cc4e1afc5e4874182b38d08088ac7 Binary files /dev/null and b/lib/python3.12/site-packages/jsonschema/benchmarks/__pycache__/validator_creation.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/jsonschema/benchmarks/const_vs_enum.py b/lib/python3.12/site-packages/jsonschema/benchmarks/const_vs_enum.py new file mode 100644 index 0000000000000000000000000000000000000000..c6fecd10f6d8b845c675be9c19e9b504b08d30b9 --- /dev/null +++ b/lib/python3.12/site-packages/jsonschema/benchmarks/const_vs_enum.py @@ -0,0 +1,30 @@ +""" +A benchmark for comparing equivalent validation of `const` and `enum`. +""" + +from pyperf import Runner + +from jsonschema import Draft202012Validator + +value = [37] * 100 +const_schema = {"const": list(value)} +enum_schema = {"enum": [list(value)]} + +valid = list(value) +invalid = [*valid, 73] + +const = Draft202012Validator(const_schema) +enum = Draft202012Validator(enum_schema) + +assert const.is_valid(valid) +assert enum.is_valid(valid) +assert not const.is_valid(invalid) +assert not enum.is_valid(invalid) + + +if __name__ == "__main__": + runner = Runner() + runner.bench_func("const valid", lambda: const.is_valid(valid)) + runner.bench_func("const invalid", lambda: const.is_valid(invalid)) + runner.bench_func("enum valid", lambda: enum.is_valid(valid)) + runner.bench_func("enum invalid", lambda: enum.is_valid(invalid)) diff --git a/lib/python3.12/site-packages/jsonschema/benchmarks/contains.py b/lib/python3.12/site-packages/jsonschema/benchmarks/contains.py new file mode 100644 index 0000000000000000000000000000000000000000..739cd044cceb807b4029dca9447e954214a24809 --- /dev/null +++ b/lib/python3.12/site-packages/jsonschema/benchmarks/contains.py @@ -0,0 +1,28 @@ +""" +A benchmark for validation of the `contains` keyword. +""" + +from pyperf import Runner + +from jsonschema import Draft202012Validator + +schema = { + "type": "array", + "contains": {"const": 37}, +} +validator = Draft202012Validator(schema) + +size = 1000 +beginning = [37] + [0] * (size - 1) +middle = [0] * (size // 2) + [37] + [0] * (size // 2) +end = [0] * (size - 1) + [37] +invalid = [0] * size + + +if __name__ == "__main__": + runner = Runner() + runner.bench_func("baseline", lambda: validator.is_valid([])) + runner.bench_func("beginning", lambda: validator.is_valid(beginning)) + runner.bench_func("middle", lambda: validator.is_valid(middle)) + runner.bench_func("end", lambda: validator.is_valid(end)) + runner.bench_func("invalid", lambda: validator.is_valid(invalid)) diff --git a/lib/python3.12/site-packages/jsonschema/benchmarks/import_benchmark.py b/lib/python3.12/site-packages/jsonschema/benchmarks/import_benchmark.py new file mode 100644 index 0000000000000000000000000000000000000000..59fa921dd8c871b6eb3d13ccd6e04c9df4312a4a --- /dev/null +++ b/lib/python3.12/site-packages/jsonschema/benchmarks/import_benchmark.py @@ -0,0 +1,31 @@ +""" +A benchmark which measures the import time of jsonschema +""" + +import subprocess +import sys + + +def import_time(loops): + total_us = 0 + for _ in range(loops): + p = subprocess.run( # noqa: S603 (arguments are static) + [sys.executable, "-X", "importtime", "-c", "import jsonschema"], + stderr=subprocess.PIPE, + stdout=subprocess.DEVNULL, + check=True, + ) + + line = p.stderr.splitlines()[-1] + field = line.split(b"|")[-2].strip() + us = int(field) # microseconds + total_us += us + + # pyperf expects seconds + return total_us / 1_000_000.0 + +if __name__ == "__main__": + from pyperf import Runner + runner = Runner() + + runner.bench_time_func("Import time (cumulative)", import_time) diff --git a/lib/python3.12/site-packages/jsonschema/benchmarks/issue232.py b/lib/python3.12/site-packages/jsonschema/benchmarks/issue232.py new file mode 100644 index 0000000000000000000000000000000000000000..efd07154822e4b0609900482eb26636fc3c100eb --- /dev/null +++ b/lib/python3.12/site-packages/jsonschema/benchmarks/issue232.py @@ -0,0 +1,25 @@ +""" +A performance benchmark using the example from issue #232. + +See https://github.com/python-jsonschema/jsonschema/pull/232. +""" +from pathlib import Path + +from pyperf import Runner +from referencing import Registry + +from jsonschema.tests._suite import Version +import jsonschema + +issue232 = Version( + path=Path(__file__).parent / "issue232", + remotes=Registry(), + name="issue232", +) + + +if __name__ == "__main__": + issue232.benchmark( + runner=Runner(), + Validator=jsonschema.Draft4Validator, + ) diff --git a/lib/python3.12/site-packages/jsonschema/benchmarks/issue232/issue.json b/lib/python3.12/site-packages/jsonschema/benchmarks/issue232/issue.json new file mode 100644 index 0000000000000000000000000000000000000000..804c340845fa0694e8db1083cfe7c290c04f36b1 --- /dev/null +++ b/lib/python3.12/site-packages/jsonschema/benchmarks/issue232/issue.json @@ -0,0 +1,2653 @@ +[ + { + "description": "Petstore", + "schema": { + "title": "A JSON Schema for Swagger 2.0 API.", + "id": "http://swagger.io/v2/schema.json#", + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "required": [ + "swagger", + "info", + "paths" + ], + "additionalProperties": false, + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + }, + "properties": { + "swagger": { + "type": "string", + "enum": [ + "2.0" + ], + "description": "The Swagger version of this document." + }, + "info": { + "$ref": "#/definitions/info" + }, + "host": { + "type": "string", + "pattern": "^[^{}/ :\\\\]+(?::\\d+)?$", + "description": "The host (name or ip) of the API. Example: 'swagger.io'" + }, + "basePath": { + "type": "string", + "pattern": "^/", + "description": "The base path to the API. Example: '/api'." + }, + "schemes": { + "$ref": "#/definitions/schemesList" + }, + "consumes": { + "description": "A list of MIME types accepted by the API.", + "allOf": [ + { + "$ref": "#/definitions/mediaTypeList" + } + ] + }, + "produces": { + "description": "A list of MIME types the API can produce.", + "allOf": [ + { + "$ref": "#/definitions/mediaTypeList" + } + ] + }, + "paths": { + "$ref": "#/definitions/paths" + }, + "definitions": { + "$ref": "#/definitions/definitions" + }, + "parameters": { + "$ref": "#/definitions/parameterDefinitions" + }, + "responses": { + "$ref": "#/definitions/responseDefinitions" + }, + "security": { + "$ref": "#/definitions/security" + }, + "securityDefinitions": { + "$ref": "#/definitions/securityDefinitions" + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/definitions/tag" + }, + "uniqueItems": true + }, + "externalDocs": { + "$ref": "#/definitions/externalDocs" + } + }, + "definitions": { + "info": { + "type": "object", + "description": "General information about the API.", + "required": [ + "version", + "title" + ], + "additionalProperties": false, + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + }, + "properties": { + "title": { + "type": "string", + "description": "A unique and precise title of the API." + }, + "version": { + "type": "string", + "description": "A semantic version number of the API." + }, + "description": { + "type": "string", + "description": "A longer description of the API. Should be different from the title. GitHub Flavored Markdown is allowed." + }, + "termsOfService": { + "type": "string", + "description": "The terms of service for the API." + }, + "contact": { + "$ref": "#/definitions/contact" + }, + "license": { + "$ref": "#/definitions/license" + } + } + }, + "contact": { + "type": "object", + "description": "Contact information for the owners of the API.", + "additionalProperties": false, + "properties": { + "name": { + "type": "string", + "description": "The identifying name of the contact person/organization." + }, + "url": { + "type": "string", + "description": "The URL pointing to the contact information.", + "format": "uri" + }, + "email": { + "type": "string", + "description": "The email address of the contact person/organization.", + "format": "email" + } + }, + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + } + }, + "license": { + "type": "object", + "required": [ + "name" + ], + "additionalProperties": false, + "properties": { + "name": { + "type": "string", + "description": "The name of the license type. It's encouraged to use an OSI compatible license." + }, + "url": { + "type": "string", + "description": "The URL pointing to the license.", + "format": "uri" + } + }, + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + } + }, + "paths": { + "type": "object", + "description": "Relative paths to the individual endpoints. They must be relative to the 'basePath'.", + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + }, + "^/": { + "$ref": "#/definitions/pathItem" + } + }, + "additionalProperties": false + }, + "definitions": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/schema" + }, + "description": "One or more JSON objects describing the schemas being consumed and produced by the API." + }, + "parameterDefinitions": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/parameter" + }, + "description": "One or more JSON representations for parameters" + }, + "responseDefinitions": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/response" + }, + "description": "One or more JSON representations for parameters" + }, + "externalDocs": { + "type": "object", + "additionalProperties": false, + "description": "information about external documentation", + "required": [ + "url" + ], + "properties": { + "description": { + "type": "string" + }, + "url": { + "type": "string", + "format": "uri" + } + }, + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + } + }, + "examples": { + "type": "object", + "additionalProperties": true + }, + "mimeType": { + "type": "string", + "description": "The MIME type of the HTTP message." + }, + "operation": { + "type": "object", + "required": [ + "responses" + ], + "additionalProperties": false, + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + }, + "properties": { + "tags": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + }, + "summary": { + "type": "string", + "description": "A brief summary of the operation." + }, + "description": { + "type": "string", + "description": "A longer description of the operation, GitHub Flavored Markdown is allowed." + }, + "externalDocs": { + "$ref": "#/definitions/externalDocs" + }, + "operationId": { + "type": "string", + "description": "A unique identifier of the operation." + }, + "produces": { + "description": "A list of MIME types the API can produce.", + "allOf": [ + { + "$ref": "#/definitions/mediaTypeList" + } + ] + }, + "consumes": { + "description": "A list of MIME types the API can consume.", + "allOf": [ + { + "$ref": "#/definitions/mediaTypeList" + } + ] + }, + "parameters": { + "$ref": "#/definitions/parametersList" + }, + "responses": { + "$ref": "#/definitions/responses" + }, + "schemes": { + "$ref": "#/definitions/schemesList" + }, + "deprecated": { + "type": "boolean", + "default": false + }, + "security": { + "$ref": "#/definitions/security" + } + } + }, + "pathItem": { + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + }, + "properties": { + "$ref": { + "type": "string" + }, + "get": { + "$ref": "#/definitions/operation" + }, + "put": { + "$ref": "#/definitions/operation" + }, + "post": { + "$ref": "#/definitions/operation" + }, + "delete": { + "$ref": "#/definitions/operation" + }, + "options": { + "$ref": "#/definitions/operation" + }, + "head": { + "$ref": "#/definitions/operation" + }, + "patch": { + "$ref": "#/definitions/operation" + }, + "parameters": { + "$ref": "#/definitions/parametersList" + } + } + }, + "responses": { + "type": "object", + "description": "Response objects names can either be any valid HTTP status code or 'default'.", + "minProperties": 1, + "additionalProperties": false, + "patternProperties": { + "^([0-9]{3})$|^(default)$": { + "$ref": "#/definitions/responseValue" + }, + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + }, + "not": { + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + } + } + }, + "responseValue": { + "oneOf": [ + { + "$ref": "#/definitions/response" + }, + { + "$ref": "#/definitions/jsonReference" + } + ] + }, + "response": { + "type": "object", + "required": [ + "description" + ], + "properties": { + "description": { + "type": "string" + }, + "schema": { + "oneOf": [ + { + "$ref": "#/definitions/schema" + }, + { + "$ref": "#/definitions/fileSchema" + } + ] + }, + "headers": { + "$ref": "#/definitions/headers" + }, + "examples": { + "$ref": "#/definitions/examples" + } + }, + "additionalProperties": false, + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + } + }, + "headers": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/header" + } + }, + "header": { + "type": "object", + "additionalProperties": false, + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "string", + "number", + "integer", + "boolean", + "array" + ] + }, + "format": { + "type": "string" + }, + "items": { + "$ref": "#/definitions/primitivesItems" + }, + "collectionFormat": { + "$ref": "#/definitions/collectionFormat" + }, + "default": { + "$ref": "#/definitions/default" + }, + "maximum": { + "$ref": "#/definitions/maximum" + }, + "exclusiveMaximum": { + "$ref": "#/definitions/exclusiveMaximum" + }, + "minimum": { + "$ref": "#/definitions/minimum" + }, + "exclusiveMinimum": { + "$ref": "#/definitions/exclusiveMinimum" + }, + "maxLength": { + "$ref": "#/definitions/maxLength" + }, + "minLength": { + "$ref": "#/definitions/minLength" + }, + "pattern": { + "$ref": "#/definitions/pattern" + }, + "maxItems": { + "$ref": "#/definitions/maxItems" + }, + "minItems": { + "$ref": "#/definitions/minItems" + }, + "uniqueItems": { + "$ref": "#/definitions/uniqueItems" + }, + "enum": { + "$ref": "#/definitions/enum" + }, + "multipleOf": { + "$ref": "#/definitions/multipleOf" + }, + "description": { + "type": "string" + } + }, + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + } + }, + "vendorExtension": { + "description": "Any property starting with x- is valid.", + "additionalProperties": true, + "additionalItems": true + }, + "bodyParameter": { + "type": "object", + "required": [ + "name", + "in", + "schema" + ], + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + }, + "properties": { + "description": { + "type": "string", + "description": "A brief description of the parameter. This could contain examples of use. GitHub Flavored Markdown is allowed." + }, + "name": { + "type": "string", + "description": "The name of the parameter." + }, + "in": { + "type": "string", + "description": "Determines the location of the parameter.", + "enum": [ + "body" + ] + }, + "required": { + "type": "boolean", + "description": "Determines whether or not this parameter is required or optional.", + "default": false + }, + "schema": { + "$ref": "#/definitions/schema" + } + }, + "additionalProperties": false + }, + "headerParameterSubSchema": { + "additionalProperties": false, + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + }, + "properties": { + "required": { + "type": "boolean", + "description": "Determines whether or not this parameter is required or optional.", + "default": false + }, + "in": { + "type": "string", + "description": "Determines the location of the parameter.", + "enum": [ + "header" + ] + }, + "description": { + "type": "string", + "description": "A brief description of the parameter. This could contain examples of use. GitHub Flavored Markdown is allowed." + }, + "name": { + "type": "string", + "description": "The name of the parameter." + }, + "type": { + "type": "string", + "enum": [ + "string", + "number", + "boolean", + "integer", + "array" + ] + }, + "format": { + "type": "string" + }, + "items": { + "$ref": "#/definitions/primitivesItems" + }, + "collectionFormat": { + "$ref": "#/definitions/collectionFormat" + }, + "default": { + "$ref": "#/definitions/default" + }, + "maximum": { + "$ref": "#/definitions/maximum" + }, + "exclusiveMaximum": { + "$ref": "#/definitions/exclusiveMaximum" + }, + "minimum": { + "$ref": "#/definitions/minimum" + }, + "exclusiveMinimum": { + "$ref": "#/definitions/exclusiveMinimum" + }, + "maxLength": { + "$ref": "#/definitions/maxLength" + }, + "minLength": { + "$ref": "#/definitions/minLength" + }, + "pattern": { + "$ref": "#/definitions/pattern" + }, + "maxItems": { + "$ref": "#/definitions/maxItems" + }, + "minItems": { + "$ref": "#/definitions/minItems" + }, + "uniqueItems": { + "$ref": "#/definitions/uniqueItems" + }, + "enum": { + "$ref": "#/definitions/enum" + }, + "multipleOf": { + "$ref": "#/definitions/multipleOf" + } + } + }, + "queryParameterSubSchema": { + "additionalProperties": false, + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + }, + "properties": { + "required": { + "type": "boolean", + "description": "Determines whether or not this parameter is required or optional.", + "default": false + }, + "in": { + "type": "string", + "description": "Determines the location of the parameter.", + "enum": [ + "query" + ] + }, + "description": { + "type": "string", + "description": "A brief description of the parameter. This could contain examples of use. GitHub Flavored Markdown is allowed." + }, + "name": { + "type": "string", + "description": "The name of the parameter." + }, + "allowEmptyValue": { + "type": "boolean", + "default": false, + "description": "allows sending a parameter by name only or with an empty value." + }, + "type": { + "type": "string", + "enum": [ + "string", + "number", + "boolean", + "integer", + "array" + ] + }, + "format": { + "type": "string" + }, + "items": { + "$ref": "#/definitions/primitivesItems" + }, + "collectionFormat": { + "$ref": "#/definitions/collectionFormatWithMulti" + }, + "default": { + "$ref": "#/definitions/default" + }, + "maximum": { + "$ref": "#/definitions/maximum" + }, + "exclusiveMaximum": { + "$ref": "#/definitions/exclusiveMaximum" + }, + "minimum": { + "$ref": "#/definitions/minimum" + }, + "exclusiveMinimum": { + "$ref": "#/definitions/exclusiveMinimum" + }, + "maxLength": { + "$ref": "#/definitions/maxLength" + }, + "minLength": { + "$ref": "#/definitions/minLength" + }, + "pattern": { + "$ref": "#/definitions/pattern" + }, + "maxItems": { + "$ref": "#/definitions/maxItems" + }, + "minItems": { + "$ref": "#/definitions/minItems" + }, + "uniqueItems": { + "$ref": "#/definitions/uniqueItems" + }, + "enum": { + "$ref": "#/definitions/enum" + }, + "multipleOf": { + "$ref": "#/definitions/multipleOf" + } + } + }, + "formDataParameterSubSchema": { + "additionalProperties": false, + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + }, + "properties": { + "required": { + "type": "boolean", + "description": "Determines whether or not this parameter is required or optional.", + "default": false + }, + "in": { + "type": "string", + "description": "Determines the location of the parameter.", + "enum": [ + "formData" + ] + }, + "description": { + "type": "string", + "description": "A brief description of the parameter. This could contain examples of use. GitHub Flavored Markdown is allowed." + }, + "name": { + "type": "string", + "description": "The name of the parameter." + }, + "allowEmptyValue": { + "type": "boolean", + "default": false, + "description": "allows sending a parameter by name only or with an empty value." + }, + "type": { + "type": "string", + "enum": [ + "string", + "number", + "boolean", + "integer", + "array", + "file" + ] + }, + "format": { + "type": "string" + }, + "items": { + "$ref": "#/definitions/primitivesItems" + }, + "collectionFormat": { + "$ref": "#/definitions/collectionFormatWithMulti" + }, + "default": { + "$ref": "#/definitions/default" + }, + "maximum": { + "$ref": "#/definitions/maximum" + }, + "exclusiveMaximum": { + "$ref": "#/definitions/exclusiveMaximum" + }, + "minimum": { + "$ref": "#/definitions/minimum" + }, + "exclusiveMinimum": { + "$ref": "#/definitions/exclusiveMinimum" + }, + "maxLength": { + "$ref": "#/definitions/maxLength" + }, + "minLength": { + "$ref": "#/definitions/minLength" + }, + "pattern": { + "$ref": "#/definitions/pattern" + }, + "maxItems": { + "$ref": "#/definitions/maxItems" + }, + "minItems": { + "$ref": "#/definitions/minItems" + }, + "uniqueItems": { + "$ref": "#/definitions/uniqueItems" + }, + "enum": { + "$ref": "#/definitions/enum" + }, + "multipleOf": { + "$ref": "#/definitions/multipleOf" + } + } + }, + "pathParameterSubSchema": { + "additionalProperties": false, + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + }, + "required": [ + "required" + ], + "properties": { + "required": { + "type": "boolean", + "enum": [ + true + ], + "description": "Determines whether or not this parameter is required or optional." + }, + "in": { + "type": "string", + "description": "Determines the location of the parameter.", + "enum": [ + "path" + ] + }, + "description": { + "type": "string", + "description": "A brief description of the parameter. This could contain examples of use. GitHub Flavored Markdown is allowed." + }, + "name": { + "type": "string", + "description": "The name of the parameter." + }, + "type": { + "type": "string", + "enum": [ + "string", + "number", + "boolean", + "integer", + "array" + ] + }, + "format": { + "type": "string" + }, + "items": { + "$ref": "#/definitions/primitivesItems" + }, + "collectionFormat": { + "$ref": "#/definitions/collectionFormat" + }, + "default": { + "$ref": "#/definitions/default" + }, + "maximum": { + "$ref": "#/definitions/maximum" + }, + "exclusiveMaximum": { + "$ref": "#/definitions/exclusiveMaximum" + }, + "minimum": { + "$ref": "#/definitions/minimum" + }, + "exclusiveMinimum": { + "$ref": "#/definitions/exclusiveMinimum" + }, + "maxLength": { + "$ref": "#/definitions/maxLength" + }, + "minLength": { + "$ref": "#/definitions/minLength" + }, + "pattern": { + "$ref": "#/definitions/pattern" + }, + "maxItems": { + "$ref": "#/definitions/maxItems" + }, + "minItems": { + "$ref": "#/definitions/minItems" + }, + "uniqueItems": { + "$ref": "#/definitions/uniqueItems" + }, + "enum": { + "$ref": "#/definitions/enum" + }, + "multipleOf": { + "$ref": "#/definitions/multipleOf" + } + } + }, + "nonBodyParameter": { + "type": "object", + "required": [ + "name", + "in", + "type" + ], + "oneOf": [ + { + "$ref": "#/definitions/headerParameterSubSchema" + }, + { + "$ref": "#/definitions/formDataParameterSubSchema" + }, + { + "$ref": "#/definitions/queryParameterSubSchema" + }, + { + "$ref": "#/definitions/pathParameterSubSchema" + } + ] + }, + "parameter": { + "oneOf": [ + { + "$ref": "#/definitions/bodyParameter" + }, + { + "$ref": "#/definitions/nonBodyParameter" + } + ] + }, + "schema": { + "type": "object", + "description": "A deterministic version of a JSON Schema object.", + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + }, + "properties": { + "$ref": { + "type": "string" + }, + "format": { + "type": "string" + }, + "title": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/title" + }, + "description": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/description" + }, + "default": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/default" + }, + "multipleOf": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/multipleOf" + }, + "maximum": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/maximum" + }, + "exclusiveMaximum": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/exclusiveMaximum" + }, + "minimum": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/minimum" + }, + "exclusiveMinimum": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/exclusiveMinimum" + }, + "maxLength": { + "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveInteger" + }, + "minLength": { + "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveIntegerDefault0" + }, + "pattern": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/pattern" + }, + "maxItems": { + "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveInteger" + }, + "minItems": { + "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveIntegerDefault0" + }, + "uniqueItems": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/uniqueItems" + }, + "maxProperties": { + "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveInteger" + }, + "minProperties": { + "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveIntegerDefault0" + }, + "required": { + "$ref": "http://json-schema.org/draft-04/schema#/definitions/stringArray" + }, + "enum": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/enum" + }, + "additionalProperties": { + "anyOf": [ + { + "$ref": "#/definitions/schema" + }, + { + "type": "boolean" + } + ], + "default": {} + }, + "type": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/type" + }, + "items": { + "anyOf": [ + { + "$ref": "#/definitions/schema" + }, + { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/schema" + } + } + ], + "default": {} + }, + "allOf": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/schema" + } + }, + "properties": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/schema" + }, + "default": {} + }, + "discriminator": { + "type": "string" + }, + "readOnly": { + "type": "boolean", + "default": false + }, + "xml": { + "$ref": "#/definitions/xml" + }, + "externalDocs": { + "$ref": "#/definitions/externalDocs" + }, + "example": {} + }, + "additionalProperties": false + }, + "fileSchema": { + "type": "object", + "description": "A deterministic version of a JSON Schema object.", + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + }, + "required": [ + "type" + ], + "properties": { + "format": { + "type": "string" + }, + "title": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/title" + }, + "description": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/description" + }, + "default": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/default" + }, + "required": { + "$ref": "http://json-schema.org/draft-04/schema#/definitions/stringArray" + }, + "type": { + "type": "string", + "enum": [ + "file" + ] + }, + "readOnly": { + "type": "boolean", + "default": false + }, + "externalDocs": { + "$ref": "#/definitions/externalDocs" + }, + "example": {} + }, + "additionalProperties": false + }, + "primitivesItems": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "string", + "number", + "integer", + "boolean", + "array" + ] + }, + "format": { + "type": "string" + }, + "items": { + "$ref": "#/definitions/primitivesItems" + }, + "collectionFormat": { + "$ref": "#/definitions/collectionFormat" + }, + "default": { + "$ref": "#/definitions/default" + }, + "maximum": { + "$ref": "#/definitions/maximum" + }, + "exclusiveMaximum": { + "$ref": "#/definitions/exclusiveMaximum" + }, + "minimum": { + "$ref": "#/definitions/minimum" + }, + "exclusiveMinimum": { + "$ref": "#/definitions/exclusiveMinimum" + }, + "maxLength": { + "$ref": "#/definitions/maxLength" + }, + "minLength": { + "$ref": "#/definitions/minLength" + }, + "pattern": { + "$ref": "#/definitions/pattern" + }, + "maxItems": { + "$ref": "#/definitions/maxItems" + }, + "minItems": { + "$ref": "#/definitions/minItems" + }, + "uniqueItems": { + "$ref": "#/definitions/uniqueItems" + }, + "enum": { + "$ref": "#/definitions/enum" + }, + "multipleOf": { + "$ref": "#/definitions/multipleOf" + } + }, + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + } + }, + "security": { + "type": "array", + "items": { + "$ref": "#/definitions/securityRequirement" + }, + "uniqueItems": true + }, + "securityRequirement": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + } + }, + "xml": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "namespace": { + "type": "string" + }, + "prefix": { + "type": "string" + }, + "attribute": { + "type": "boolean", + "default": false + }, + "wrapped": { + "type": "boolean", + "default": false + } + }, + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + } + }, + "tag": { + "type": "object", + "additionalProperties": false, + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "externalDocs": { + "$ref": "#/definitions/externalDocs" + } + }, + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + } + }, + "securityDefinitions": { + "type": "object", + "additionalProperties": { + "oneOf": [ + { + "$ref": "#/definitions/basicAuthenticationSecurity" + }, + { + "$ref": "#/definitions/apiKeySecurity" + }, + { + "$ref": "#/definitions/oauth2ImplicitSecurity" + }, + { + "$ref": "#/definitions/oauth2PasswordSecurity" + }, + { + "$ref": "#/definitions/oauth2ApplicationSecurity" + }, + { + "$ref": "#/definitions/oauth2AccessCodeSecurity" + } + ] + } + }, + "basicAuthenticationSecurity": { + "type": "object", + "additionalProperties": false, + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "basic" + ] + }, + "description": { + "type": "string" + } + }, + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + } + }, + "apiKeySecurity": { + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "name", + "in" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "apiKey" + ] + }, + "name": { + "type": "string" + }, + "in": { + "type": "string", + "enum": [ + "header", + "query" + ] + }, + "description": { + "type": "string" + } + }, + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + } + }, + "oauth2ImplicitSecurity": { + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "flow", + "authorizationUrl" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "oauth2" + ] + }, + "flow": { + "type": "string", + "enum": [ + "implicit" + ] + }, + "scopes": { + "$ref": "#/definitions/oauth2Scopes" + }, + "authorizationUrl": { + "type": "string", + "format": "uri" + }, + "description": { + "type": "string" + } + }, + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + } + }, + "oauth2PasswordSecurity": { + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "flow", + "tokenUrl" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "oauth2" + ] + }, + "flow": { + "type": "string", + "enum": [ + "password" + ] + }, + "scopes": { + "$ref": "#/definitions/oauth2Scopes" + }, + "tokenUrl": { + "type": "string", + "format": "uri" + }, + "description": { + "type": "string" + } + }, + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + } + }, + "oauth2ApplicationSecurity": { + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "flow", + "tokenUrl" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "oauth2" + ] + }, + "flow": { + "type": "string", + "enum": [ + "application" + ] + }, + "scopes": { + "$ref": "#/definitions/oauth2Scopes" + }, + "tokenUrl": { + "type": "string", + "format": "uri" + }, + "description": { + "type": "string" + } + }, + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + } + }, + "oauth2AccessCodeSecurity": { + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "flow", + "authorizationUrl", + "tokenUrl" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "oauth2" + ] + }, + "flow": { + "type": "string", + "enum": [ + "accessCode" + ] + }, + "scopes": { + "$ref": "#/definitions/oauth2Scopes" + }, + "authorizationUrl": { + "type": "string", + "format": "uri" + }, + "tokenUrl": { + "type": "string", + "format": "uri" + }, + "description": { + "type": "string" + } + }, + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + } + }, + "oauth2Scopes": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "mediaTypeList": { + "type": "array", + "items": { + "$ref": "#/definitions/mimeType" + }, + "uniqueItems": true + }, + "parametersList": { + "type": "array", + "description": "The parameters needed to send a valid API call.", + "additionalItems": false, + "items": { + "oneOf": [ + { + "$ref": "#/definitions/parameter" + }, + { + "$ref": "#/definitions/jsonReference" + } + ] + }, + "uniqueItems": true + }, + "schemesList": { + "type": "array", + "description": "The transfer protocol of the API.", + "items": { + "type": "string", + "enum": [ + "http", + "https", + "ws", + "wss" + ] + }, + "uniqueItems": true + }, + "collectionFormat": { + "type": "string", + "enum": [ + "csv", + "ssv", + "tsv", + "pipes" + ], + "default": "csv" + }, + "collectionFormatWithMulti": { + "type": "string", + "enum": [ + "csv", + "ssv", + "tsv", + "pipes", + "multi" + ], + "default": "csv" + }, + "title": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/title" + }, + "description": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/description" + }, + "default": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/default" + }, + "multipleOf": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/multipleOf" + }, + "maximum": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/maximum" + }, + "exclusiveMaximum": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/exclusiveMaximum" + }, + "minimum": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/minimum" + }, + "exclusiveMinimum": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/exclusiveMinimum" + }, + "maxLength": { + "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveInteger" + }, + "minLength": { + "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveIntegerDefault0" + }, + "pattern": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/pattern" + }, + "maxItems": { + "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveInteger" + }, + "minItems": { + "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveIntegerDefault0" + }, + "uniqueItems": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/uniqueItems" + }, + "enum": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/enum" + }, + "jsonReference": { + "type": "object", + "required": [ + "$ref" + ], + "additionalProperties": false, + "properties": { + "$ref": { + "type": "string" + } + } + } + } + }, + "tests": [ + { + "description": "Example petsore", + "data": { + "swagger": "2.0", + "info": { + "description": "This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters.", + "version": "1.0.0", + "title": "Swagger Petstore", + "termsOfService": "http://swagger.io/terms/", + "contact": { + "email": "apiteam@swagger.io" + }, + "license": { + "name": "Apache 2.0", + "url": "http://www.apache.org/licenses/LICENSE-2.0.html" + } + }, + "host": "petstore.swagger.io", + "basePath": "/v2", + "tags": [ + { + "name": "pet", + "description": "Everything about your Pets", + "externalDocs": { + "description": "Find out more", + "url": "http://swagger.io" + } + }, + { + "name": "store", + "description": "Access to Petstore orders" + }, + { + "name": "user", + "description": "Operations about user", + "externalDocs": { + "description": "Find out more about our store", + "url": "http://swagger.io" + } + } + ], + "schemes": [ + "http" + ], + "paths": { + "/pet": { + "post": { + "tags": [ + "pet" + ], + "summary": "Add a new pet to the store", + "description": "", + "operationId": "addPet", + "consumes": [ + "application/json", + "application/xml" + ], + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "Pet object that needs to be added to the store", + "required": true, + "schema": { + "$ref": "#/definitions/Pet" + } + } + ], + "responses": { + "405": { + "description": "Invalid input" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + }, + "put": { + "tags": [ + "pet" + ], + "summary": "Update an existing pet", + "description": "", + "operationId": "updatePet", + "consumes": [ + "application/json", + "application/xml" + ], + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "Pet object that needs to be added to the store", + "required": true, + "schema": { + "$ref": "#/definitions/Pet" + } + } + ], + "responses": { + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Pet not found" + }, + "405": { + "description": "Validation exception" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/pet/findByStatus": { + "get": { + "tags": [ + "pet" + ], + "summary": "Finds Pets by status", + "description": "Multiple status values can be provided with comma separated strings", + "operationId": "findPetsByStatus", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "status", + "in": "query", + "description": "Status values that need to be considered for filter", + "required": true, + "type": "array", + "items": { + "type": "string", + "enum": [ + "available", + "pending", + "sold" + ], + "default": "available" + }, + "collectionFormat": "multi" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/Pet" + } + } + }, + "400": { + "description": "Invalid status value" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/pet/findByTags": { + "get": { + "tags": [ + "pet" + ], + "summary": "Finds Pets by tags", + "description": "Muliple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.", + "operationId": "findPetsByTags", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "tags", + "in": "query", + "description": "Tags to filter by", + "required": true, + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "multi" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/Pet" + } + } + }, + "400": { + "description": "Invalid tag value" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ], + "deprecated": true + } + }, + "/pet/{petId}": { + "get": { + "tags": [ + "pet" + ], + "summary": "Find pet by ID", + "description": "Returns a single pet", + "operationId": "getPetById", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "petId", + "in": "path", + "description": "ID of pet to return", + "required": true, + "type": "integer", + "format": "int64" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/Pet" + } + }, + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Pet not found" + } + }, + "security": [ + { + "api_key": [] + } + ] + }, + "post": { + "tags": [ + "pet" + ], + "summary": "Updates a pet in the store with form data", + "description": "", + "operationId": "updatePetWithForm", + "consumes": [ + "application/x-www-form-urlencoded" + ], + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "petId", + "in": "path", + "description": "ID of pet that needs to be updated", + "required": true, + "type": "integer", + "format": "int64" + }, + { + "name": "name", + "in": "formData", + "description": "Updated name of the pet", + "required": false, + "type": "string" + }, + { + "name": "status", + "in": "formData", + "description": "Updated status of the pet", + "required": false, + "type": "string" + } + ], + "responses": { + "405": { + "description": "Invalid input" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + }, + "delete": { + "tags": [ + "pet" + ], + "summary": "Deletes a pet", + "description": "", + "operationId": "deletePet", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "api_key", + "in": "header", + "required": false, + "type": "string" + }, + { + "name": "petId", + "in": "path", + "description": "Pet id to delete", + "required": true, + "type": "integer", + "format": "int64" + } + ], + "responses": { + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Pet not found" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/pet/{petId}/uploadImage": { + "post": { + "tags": [ + "pet" + ], + "summary": "uploads an image", + "description": "", + "operationId": "uploadFile", + "consumes": [ + "multipart/form-data" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "name": "petId", + "in": "path", + "description": "ID of pet to update", + "required": true, + "type": "integer", + "format": "int64" + }, + { + "name": "additionalMetadata", + "in": "formData", + "description": "Additional data to pass to server", + "required": false, + "type": "string" + }, + { + "name": "file", + "in": "formData", + "description": "file to upload", + "required": false, + "type": "file" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/ApiResponse" + } + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/store/inventory": { + "get": { + "tags": [ + "store" + ], + "summary": "Returns pet inventories by status", + "description": "Returns a map of status codes to quantities", + "operationId": "getInventory", + "produces": [ + "application/json" + ], + "parameters": [], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "type": "object", + "additionalProperties": { + "type": "integer", + "format": "int32" + } + } + } + }, + "security": [ + { + "api_key": [] + } + ] + } + }, + "/store/order": { + "post": { + "tags": [ + "store" + ], + "summary": "Place an order for a pet", + "description": "", + "operationId": "placeOrder", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "order placed for purchasing the pet", + "required": true, + "schema": { + "$ref": "#/definitions/Order" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/Order" + } + }, + "400": { + "description": "Invalid Order" + } + } + } + }, + "/store/order/{orderId}": { + "get": { + "tags": [ + "store" + ], + "summary": "Find purchase order by ID", + "description": "For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions", + "operationId": "getOrderById", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "orderId", + "in": "path", + "description": "ID of pet that needs to be fetched", + "required": true, + "type": "integer", + "maximum": 10.0, + "minimum": 1.0, + "format": "int64" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/Order" + } + }, + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Order not found" + } + } + }, + "delete": { + "tags": [ + "store" + ], + "summary": "Delete purchase order by ID", + "description": "For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors", + "operationId": "deleteOrder", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "orderId", + "in": "path", + "description": "ID of the order that needs to be deleted", + "required": true, + "type": "integer", + "minimum": 1.0, + "format": "int64" + } + ], + "responses": { + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Order not found" + } + } + } + }, + "/user": { + "post": { + "tags": [ + "user" + ], + "summary": "Create user", + "description": "This can only be done by the logged in user.", + "operationId": "createUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "Created user object", + "required": true, + "schema": { + "$ref": "#/definitions/User" + } + } + ], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/createWithArray": { + "post": { + "tags": [ + "user" + ], + "summary": "Creates list of users with given input array", + "description": "", + "operationId": "createUsersWithArrayInput", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "List of user object", + "required": true, + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/User" + } + } + } + ], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/createWithList": { + "post": { + "tags": [ + "user" + ], + "summary": "Creates list of users with given input array", + "description": "", + "operationId": "createUsersWithListInput", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "List of user object", + "required": true, + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/User" + } + } + } + ], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/login": { + "get": { + "tags": [ + "user" + ], + "summary": "Logs user into the system", + "description": "", + "operationId": "loginUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "username", + "in": "query", + "description": "The user name for login", + "required": true, + "type": "string" + }, + { + "name": "password", + "in": "query", + "description": "The password for login in clear text", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "type": "string" + }, + "headers": { + "X-Rate-Limit": { + "type": "integer", + "format": "int32", + "description": "calls per hour allowed by the user" + }, + "X-Expires-After": { + "type": "string", + "format": "date-time", + "description": "date in UTC when token expires" + } + } + }, + "400": { + "description": "Invalid username/password supplied" + } + } + } + }, + "/user/logout": { + "get": { + "tags": [ + "user" + ], + "summary": "Logs out current logged in user session", + "description": "", + "operationId": "logoutUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/{username}": { + "get": { + "tags": [ + "user" + ], + "summary": "Get user by user name", + "description": "", + "operationId": "getUserByName", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "username", + "in": "path", + "description": "The name that needs to be fetched. Use user1 for testing. ", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/User" + } + }, + "400": { + "description": "Invalid username supplied" + }, + "404": { + "description": "User not found" + } + } + }, + "put": { + "tags": [ + "user" + ], + "summary": "Updated user", + "description": "This can only be done by the logged in user.", + "operationId": "updateUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "username", + "in": "path", + "description": "name that need to be updated", + "required": true, + "type": "string" + }, + { + "in": "body", + "name": "body", + "description": "Updated user object", + "required": true, + "schema": { + "$ref": "#/definitions/User" + } + } + ], + "responses": { + "400": { + "description": "Invalid user supplied" + }, + "404": { + "description": "User not found" + } + } + }, + "delete": { + "tags": [ + "user" + ], + "summary": "Delete user", + "description": "This can only be done by the logged in user.", + "operationId": "deleteUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "username", + "in": "path", + "description": "The name that needs to be deleted", + "required": true, + "type": "string" + } + ], + "responses": { + "400": { + "description": "Invalid username supplied" + }, + "404": { + "description": "User not found" + } + } + } + } + }, + "securityDefinitions": { + "petstore_auth": { + "type": "oauth2", + "authorizationUrl": "http://petstore.swagger.io/oauth/dialog", + "flow": "implicit", + "scopes": { + "write:pets": "modify pets in your account", + "read:pets": "read your pets" + } + }, + "api_key": { + "type": "apiKey", + "name": "api_key", + "in": "header" + } + }, + "definitions": { + "Order": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "petId": { + "type": "integer", + "format": "int64" + }, + "quantity": { + "type": "integer", + "format": "int32" + }, + "shipDate": { + "type": "string", + "format": "date-time" + }, + "status": { + "type": "string", + "description": "Order Status", + "enum": [ + "placed", + "approved", + "delivered" + ] + }, + "complete": { + "type": "boolean", + "default": false + } + }, + "xml": { + "name": "Order" + } + }, + "Category": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + } + }, + "xml": { + "name": "Category" + } + }, + "User": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "username": { + "type": "string" + }, + "firstName": { + "type": "string" + }, + "lastName": { + "type": "string" + }, + "email": { + "type": "string" + }, + "password": { + "type": "string" + }, + "phone": { + "type": "string" + }, + "userStatus": { + "type": "integer", + "format": "int32", + "description": "User Status" + } + }, + "xml": { + "name": "User" + } + }, + "Tag": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + } + }, + "xml": { + "name": "Tag" + } + }, + "Pet": { + "type": "object", + "required": [ + "name", + "photoUrls" + ], + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "category": { + "$ref": "#/definitions/Category" + }, + "name": { + "type": "string", + "example": "doggie" + }, + "photoUrls": { + "type": "array", + "xml": { + "name": "photoUrl", + "wrapped": true + }, + "items": { + "type": "string" + } + }, + "tags": { + "type": "array", + "xml": { + "name": "tag", + "wrapped": true + }, + "items": { + "$ref": "#/definitions/Tag" + } + }, + "status": { + "type": "string", + "description": "pet status in the store", + "enum": [ + "available", + "pending", + "sold" + ] + } + }, + "xml": { + "name": "Pet" + } + }, + "ApiResponse": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "type": { + "type": "string" + }, + "message": { + "type": "string" + } + } + } + }, + "externalDocs": { + "description": "Find out more about Swagger", + "url": "http://swagger.io" + } + }, + "valid": true + } + ] + } +] diff --git a/lib/python3.12/site-packages/jsonschema/benchmarks/json_schema_test_suite.py b/lib/python3.12/site-packages/jsonschema/benchmarks/json_schema_test_suite.py new file mode 100644 index 0000000000000000000000000000000000000000..905fb6a3b88faf56e3288f7eb5053172f97abe8b --- /dev/null +++ b/lib/python3.12/site-packages/jsonschema/benchmarks/json_schema_test_suite.py @@ -0,0 +1,12 @@ +""" +A performance benchmark using the official test suite. + +This benchmarks jsonschema using every valid example in the +JSON-Schema-Test-Suite. It will take some time to complete. +""" +from pyperf import Runner + +from jsonschema.tests._suite import Suite + +if __name__ == "__main__": + Suite().benchmark(runner=Runner()) diff --git a/lib/python3.12/site-packages/jsonschema/benchmarks/nested_schemas.py b/lib/python3.12/site-packages/jsonschema/benchmarks/nested_schemas.py new file mode 100644 index 0000000000000000000000000000000000000000..b025c47cfd6736b0ea3d36856c1e2c8de35dde79 --- /dev/null +++ b/lib/python3.12/site-packages/jsonschema/benchmarks/nested_schemas.py @@ -0,0 +1,56 @@ +""" +Validating highly nested schemas shouldn't cause exponential time blowups. + +See https://github.com/python-jsonschema/jsonschema/issues/1097. +""" +from itertools import cycle + +from jsonschema.validators import validator_for + +metaschemaish = { + "$id": "https://example.com/draft/2020-12/schema/strict", + "$schema": "https://json-schema.org/draft/2020-12/schema", + + "$vocabulary": { + "https://json-schema.org/draft/2020-12/vocab/core": True, + "https://json-schema.org/draft/2020-12/vocab/applicator": True, + "https://json-schema.org/draft/2020-12/vocab/unevaluated": True, + "https://json-schema.org/draft/2020-12/vocab/validation": True, + "https://json-schema.org/draft/2020-12/vocab/meta-data": True, + "https://json-schema.org/draft/2020-12/vocab/format-annotation": True, + "https://json-schema.org/draft/2020-12/vocab/content": True, + }, + "$dynamicAnchor": "meta", + + "$ref": "https://json-schema.org/draft/2020-12/schema", + "unevaluatedProperties": False, +} + + +def nested_schema(levels): + """ + Produce a schema which validates deeply nested objects and arrays. + """ + + names = cycle(["foo", "bar", "baz", "quux", "spam", "eggs"]) + schema = {"type": "object", "properties": {"ham": {"type": "string"}}} + for _, name in zip(range(levels - 1), names): + schema = {"type": "object", "properties": {name: schema}} + return schema + + +validator = validator_for(metaschemaish)(metaschemaish) + +if __name__ == "__main__": + from pyperf import Runner + runner = Runner() + + not_nested = nested_schema(levels=1) + runner.bench_func("not nested", lambda: validator.is_valid(not_nested)) + + for levels in range(1, 11, 3): + schema = nested_schema(levels=levels) + runner.bench_func( + f"nested * {levels}", + lambda schema=schema: validator.is_valid(schema), + ) diff --git a/lib/python3.12/site-packages/jsonschema/benchmarks/subcomponents.py b/lib/python3.12/site-packages/jsonschema/benchmarks/subcomponents.py new file mode 100644 index 0000000000000000000000000000000000000000..6d78c7be6c88d177b89dfc80e52acae7820156a5 --- /dev/null +++ b/lib/python3.12/site-packages/jsonschema/benchmarks/subcomponents.py @@ -0,0 +1,42 @@ +""" +A benchmark which tries to compare the possible slow subparts of validation. +""" +from referencing import Registry +from referencing.jsonschema import DRAFT202012 +from rpds import HashTrieMap, HashTrieSet + +from jsonschema import Draft202012Validator + +schema = { + "type": "array", + "minLength": 1, + "maxLength": 1, + "items": {"type": "integer"}, +} + +hmap = HashTrieMap() +hset = HashTrieSet() + +registry = Registry() + +v = Draft202012Validator(schema) + + +def registry_data_structures(): + return hmap.insert("foo", "bar"), hset.insert("foo") + + +def registry_add(): + resource = DRAFT202012.create_resource(schema) + return registry.with_resource(uri="urn:example", resource=resource) + + +if __name__ == "__main__": + from pyperf import Runner + runner = Runner() + + runner.bench_func("HashMap/HashSet insertion", registry_data_structures) + runner.bench_func("Registry insertion", registry_add) + runner.bench_func("Success", lambda: v.is_valid([1])) + runner.bench_func("Failure", lambda: v.is_valid(["foo"])) + runner.bench_func("Metaschema validation", lambda: v.check_schema(schema)) diff --git a/lib/python3.12/site-packages/jsonschema/benchmarks/unused_registry.py b/lib/python3.12/site-packages/jsonschema/benchmarks/unused_registry.py new file mode 100644 index 0000000000000000000000000000000000000000..7b272c235625378b231d1c113bcb8ef45851cbc7 --- /dev/null +++ b/lib/python3.12/site-packages/jsonschema/benchmarks/unused_registry.py @@ -0,0 +1,35 @@ +""" +An unused schema registry should not cause slower validation. + +"Unused" here means one where no reference resolution is occurring anyhow. + +See https://github.com/python-jsonschema/jsonschema/issues/1088. +""" +from pyperf import Runner +from referencing import Registry +from referencing.jsonschema import DRAFT201909 + +from jsonschema import Draft201909Validator + +registry = Registry().with_resource( + "urn:example:foo", + DRAFT201909.create_resource({}), +) + +schema = {"$ref": "https://json-schema.org/draft/2019-09/schema"} +instance = {"maxLength": 4} + +no_registry = Draft201909Validator(schema) +with_useless_registry = Draft201909Validator(schema, registry=registry) + +if __name__ == "__main__": + runner = Runner() + + runner.bench_func( + "no registry", + lambda: no_registry.is_valid(instance), + ) + runner.bench_func( + "useless registry", + lambda: with_useless_registry.is_valid(instance), + ) diff --git a/lib/python3.12/site-packages/jsonschema/benchmarks/useless_applicator_schemas.py b/lib/python3.12/site-packages/jsonschema/benchmarks/useless_applicator_schemas.py new file mode 100644 index 0000000000000000000000000000000000000000..f3229c0b8b9341e80ed087297efe31b04aa77fd7 --- /dev/null +++ b/lib/python3.12/site-packages/jsonschema/benchmarks/useless_applicator_schemas.py @@ -0,0 +1,106 @@ + +""" +A benchmark for validation of applicators containing lots of useless schemas. + +Signals a small possible optimization to remove all such schemas ahead of time. +""" + +from pyperf import Runner + +from jsonschema import Draft202012Validator as Validator + +NUM_USELESS = 100000 + +subschema = {"const": 37} + +valid = 37 +invalid = 12 + +baseline = Validator(subschema) + + +# These should be indistinguishable from just `subschema` +by_name = { + "single subschema": { + "anyOf": Validator({"anyOf": [subschema]}), + "allOf": Validator({"allOf": [subschema]}), + "oneOf": Validator({"oneOf": [subschema]}), + }, + "redundant subschemas": { + "anyOf": Validator({"anyOf": [subschema] * NUM_USELESS}), + "allOf": Validator({"allOf": [subschema] * NUM_USELESS}), + }, + "useless successful subschemas (beginning)": { + "anyOf": Validator({"anyOf": [subschema, *[True] * NUM_USELESS]}), + "allOf": Validator({"allOf": [subschema, *[True] * NUM_USELESS]}), + }, + "useless successful subschemas (middle)": { + "anyOf": Validator( + { + "anyOf": [ + *[True] * (NUM_USELESS // 2), + subschema, + *[True] * (NUM_USELESS // 2), + ], + }, + ), + "allOf": Validator( + { + "allOf": [ + *[True] * (NUM_USELESS // 2), + subschema, + *[True] * (NUM_USELESS // 2), + ], + }, + ), + }, + "useless successful subschemas (end)": { + "anyOf": Validator({"anyOf": [*[True] * NUM_USELESS, subschema]}), + "allOf": Validator({"allOf": [*[True] * NUM_USELESS, subschema]}), + }, + "useless failing subschemas (beginning)": { + "anyOf": Validator({"anyOf": [subschema, *[False] * NUM_USELESS]}), + "oneOf": Validator({"oneOf": [subschema, *[False] * NUM_USELESS]}), + }, + "useless failing subschemas (middle)": { + "anyOf": Validator( + { + "anyOf": [ + *[False] * (NUM_USELESS // 2), + subschema, + *[False] * (NUM_USELESS // 2), + ], + }, + ), + "oneOf": Validator( + { + "oneOf": [ + *[False] * (NUM_USELESS // 2), + subschema, + *[False] * (NUM_USELESS // 2), + ], + }, + ), + }, + "useless failing subschemas (end)": { + "anyOf": Validator({"anyOf": [*[False] * NUM_USELESS, subschema]}), + "oneOf": Validator({"oneOf": [*[False] * NUM_USELESS, subschema]}), + }, +} + +if __name__ == "__main__": + runner = Runner() + + runner.bench_func("baseline valid", lambda: baseline.is_valid(valid)) + runner.bench_func("baseline invalid", lambda: baseline.is_valid(invalid)) + + for group, applicators in by_name.items(): + for applicator, validator in applicators.items(): + runner.bench_func( + f"{group}: {applicator} valid", + lambda validator=validator: validator.is_valid(valid), + ) + runner.bench_func( + f"{group}: {applicator} invalid", + lambda validator=validator: validator.is_valid(invalid), + ) diff --git a/lib/python3.12/site-packages/jsonschema/benchmarks/useless_keywords.py b/lib/python3.12/site-packages/jsonschema/benchmarks/useless_keywords.py new file mode 100644 index 0000000000000000000000000000000000000000..50f435989dd14d34d804508067e91e5fae4a275c --- /dev/null +++ b/lib/python3.12/site-packages/jsonschema/benchmarks/useless_keywords.py @@ -0,0 +1,32 @@ +""" +A benchmark for validation of schemas containing lots of useless keywords. + +Checks we filter them out once, ahead of time. +""" + +from pyperf import Runner + +from jsonschema import Draft202012Validator + +NUM_USELESS = 100000 +schema = dict( + [ + ("not", {"const": 42}), + *((str(i), i) for i in range(NUM_USELESS)), + ("type", "integer"), + *((str(i), i) for i in range(NUM_USELESS, NUM_USELESS)), + ("minimum", 37), + ], +) +validator = Draft202012Validator(schema) + +valid = 3737 +invalid = 12 + + +if __name__ == "__main__": + runner = Runner() + runner.bench_func("beginning of schema", lambda: validator.is_valid(42)) + runner.bench_func("middle of schema", lambda: validator.is_valid("foo")) + runner.bench_func("end of schema", lambda: validator.is_valid(12)) + runner.bench_func("valid", lambda: validator.is_valid(3737)) diff --git a/lib/python3.12/site-packages/jsonschema/benchmarks/validator_creation.py b/lib/python3.12/site-packages/jsonschema/benchmarks/validator_creation.py new file mode 100644 index 0000000000000000000000000000000000000000..4baeb3a31641a027496732a6f10e200346551209 --- /dev/null +++ b/lib/python3.12/site-packages/jsonschema/benchmarks/validator_creation.py @@ -0,0 +1,14 @@ +from pyperf import Runner + +from jsonschema import Draft202012Validator + +schema = { + "type": "array", + "minLength": 1, + "maxLength": 1, + "items": {"type": "integer"}, +} + + +if __name__ == "__main__": + Runner().bench_func("validator creation", Draft202012Validator, schema) diff --git a/lib/python3.12/site-packages/jsonschema/cli.py b/lib/python3.12/site-packages/jsonschema/cli.py new file mode 100644 index 0000000000000000000000000000000000000000..f3ca4d6ad04882f8107ce134c270b4fcbc94032b --- /dev/null +++ b/lib/python3.12/site-packages/jsonschema/cli.py @@ -0,0 +1,292 @@ +""" +The ``jsonschema`` command line. +""" + +from importlib import metadata +from json import JSONDecodeError +from pkgutil import resolve_name +from textwrap import dedent +import argparse +import json +import sys +import traceback +import warnings + +from attrs import define, field + +from jsonschema.exceptions import SchemaError +from jsonschema.validators import _RefResolver, validator_for + +warnings.warn( + ( + "The jsonschema CLI is deprecated and will be removed in a future " + "version. Please use check-jsonschema instead, which can be installed " + "from https://pypi.org/project/check-jsonschema/" + ), + DeprecationWarning, + stacklevel=2, +) + + +class _CannotLoadFile(Exception): + pass + + +@define +class _Outputter: + + _formatter = field() + _stdout = field() + _stderr = field() + + @classmethod + def from_arguments(cls, arguments, stdout, stderr): + if arguments["output"] == "plain": + formatter = _PlainFormatter(arguments["error_format"]) + elif arguments["output"] == "pretty": + formatter = _PrettyFormatter() + return cls(formatter=formatter, stdout=stdout, stderr=stderr) + + def load(self, path): + try: + file = open(path) # noqa: SIM115, PTH123 + except FileNotFoundError as error: + self.filenotfound_error(path=path, exc_info=sys.exc_info()) + raise _CannotLoadFile() from error + + with file: + try: + return json.load(file) + except JSONDecodeError as error: + self.parsing_error(path=path, exc_info=sys.exc_info()) + raise _CannotLoadFile() from error + + def filenotfound_error(self, **kwargs): + self._stderr.write(self._formatter.filenotfound_error(**kwargs)) + + def parsing_error(self, **kwargs): + self._stderr.write(self._formatter.parsing_error(**kwargs)) + + def validation_error(self, **kwargs): + self._stderr.write(self._formatter.validation_error(**kwargs)) + + def validation_success(self, **kwargs): + self._stdout.write(self._formatter.validation_success(**kwargs)) + + +@define +class _PrettyFormatter: + + _ERROR_MSG = dedent( + """\ + ===[{type}]===({path})=== + + {body} + ----------------------------- + """, + ) + _SUCCESS_MSG = "===[SUCCESS]===({path})===\n" + + def filenotfound_error(self, path, exc_info): + return self._ERROR_MSG.format( + path=path, + type="FileNotFoundError", + body=f"{path!r} does not exist.", + ) + + def parsing_error(self, path, exc_info): + exc_type, exc_value, exc_traceback = exc_info + exc_lines = "".join( + traceback.format_exception(exc_type, exc_value, exc_traceback), + ) + return self._ERROR_MSG.format( + path=path, + type=exc_type.__name__, + body=exc_lines, + ) + + def validation_error(self, instance_path, error): + return self._ERROR_MSG.format( + path=instance_path, + type=error.__class__.__name__, + body=error, + ) + + def validation_success(self, instance_path): + return self._SUCCESS_MSG.format(path=instance_path) + + +@define +class _PlainFormatter: + + _error_format = field() + + def filenotfound_error(self, path, exc_info): + return f"{path!r} does not exist.\n" + + def parsing_error(self, path, exc_info): + return "Failed to parse {}: {}\n".format( + "" if path == "" else repr(path), + exc_info[1], + ) + + def validation_error(self, instance_path, error): + return self._error_format.format(file_name=instance_path, error=error) + + def validation_success(self, instance_path): + return "" + + +def _resolve_name_with_default(name): + if "." not in name: + name = "jsonschema." + name + return resolve_name(name) + + +parser = argparse.ArgumentParser( + description="JSON Schema Validation CLI", +) +parser.add_argument( + "-i", "--instance", + action="append", + dest="instances", + help=""" + a path to a JSON instance (i.e. filename.json) to validate (may + be specified multiple times). If no instances are provided via this + option, one will be expected on standard input. + """, +) +parser.add_argument( + "-F", "--error-format", + help=""" + the format to use for each validation error message, specified + in a form suitable for str.format. This string will be passed + one formatted object named 'error' for each ValidationError. + Only provide this option when using --output=plain, which is the + default. If this argument is unprovided and --output=plain is + used, a simple default representation will be used. + """, +) +parser.add_argument( + "-o", "--output", + choices=["plain", "pretty"], + default="plain", + help=""" + an output format to use. 'plain' (default) will produce minimal + text with one line for each error, while 'pretty' will produce + more detailed human-readable output on multiple lines. + """, +) +parser.add_argument( + "-V", "--validator", + type=_resolve_name_with_default, + help=""" + the fully qualified object name of a validator to use, or, for + validators that are registered with jsonschema, simply the name + of the class. + """, +) +parser.add_argument( + "--base-uri", + help=""" + a base URI to assign to the provided schema, even if it does not + declare one (via e.g. $id). This option can be used if you wish to + resolve relative references to a particular URI (or local path) + """, +) +parser.add_argument( + "--version", + action="version", + version=metadata.version("jsonschema"), +) +parser.add_argument( + "schema", + help="the path to a JSON Schema to validate with (i.e. schema.json)", +) + + +def parse_args(args): # noqa: D103 + arguments = vars(parser.parse_args(args=args or ["--help"])) + if arguments["output"] != "plain" and arguments["error_format"]: + raise parser.error( + "--error-format can only be used with --output plain", + ) + if arguments["output"] == "plain" and arguments["error_format"] is None: + arguments["error_format"] = "{error.instance}: {error.message}\n" + return arguments + + +def _validate_instance(instance_path, instance, validator, outputter): + invalid = False + for error in validator.iter_errors(instance): + invalid = True + outputter.validation_error(instance_path=instance_path, error=error) + + if not invalid: + outputter.validation_success(instance_path=instance_path) + return invalid + + +def main(args=sys.argv[1:]): # noqa: D103 + sys.exit(run(arguments=parse_args(args=args))) + + +def run(arguments, stdout=sys.stdout, stderr=sys.stderr, stdin=sys.stdin): # noqa: D103 + outputter = _Outputter.from_arguments( + arguments=arguments, + stdout=stdout, + stderr=stderr, + ) + + try: + schema = outputter.load(arguments["schema"]) + except _CannotLoadFile: + return 1 + + Validator = arguments["validator"] + if Validator is None: + Validator = validator_for(schema) + + try: + Validator.check_schema(schema) + except SchemaError as error: + outputter.validation_error( + instance_path=arguments["schema"], + error=error, + ) + return 1 + + if arguments["instances"]: + load, instances = outputter.load, arguments["instances"] + else: + def load(_): + try: + return json.load(stdin) + except JSONDecodeError as error: + outputter.parsing_error( + path="", exc_info=sys.exc_info(), + ) + raise _CannotLoadFile() from error + instances = [""] + + resolver = _RefResolver( + base_uri=arguments["base_uri"], + referrer=schema, + ) if arguments["base_uri"] is not None else None + + validator = Validator(schema, resolver=resolver) + exit_code = 0 + for each in instances: + try: + instance = load(each) + except _CannotLoadFile: + exit_code = 1 + else: + exit_code |= _validate_instance( + instance_path=each, + instance=instance, + validator=validator, + outputter=outputter, + ) + + return exit_code diff --git a/lib/python3.12/site-packages/jsonschema/exceptions.py b/lib/python3.12/site-packages/jsonschema/exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..d955e356ef584df655accc390b4cdba22f72a545 --- /dev/null +++ b/lib/python3.12/site-packages/jsonschema/exceptions.py @@ -0,0 +1,490 @@ +""" +Validation errors, and some surrounding helpers. +""" +from __future__ import annotations + +from collections import defaultdict, deque +from pprint import pformat +from textwrap import dedent, indent +from typing import TYPE_CHECKING, Any, ClassVar +import heapq +import re +import warnings + +from attrs import define +from referencing.exceptions import Unresolvable as _Unresolvable + +from jsonschema import _utils + +if TYPE_CHECKING: + from collections.abc import Iterable, Mapping, MutableMapping, Sequence + + from jsonschema import _types + +WEAK_MATCHES: frozenset[str] = frozenset(["anyOf", "oneOf"]) +STRONG_MATCHES: frozenset[str] = frozenset() + +_JSON_PATH_COMPATIBLE_PROPERTY_PATTERN = re.compile("^[a-zA-Z][a-zA-Z0-9_]*$") + +_unset = _utils.Unset() + + +def _pretty(thing: Any, prefix: str): + """ + Format something for an error message as prettily as we currently can. + """ + return indent(pformat(thing, width=72, sort_dicts=False), prefix).lstrip() + + +def __getattr__(name): + if name == "RefResolutionError": + warnings.warn( + _RefResolutionError._DEPRECATION_MESSAGE, + DeprecationWarning, + stacklevel=2, + ) + return _RefResolutionError + raise AttributeError(f"module {__name__} has no attribute {name}") + + +class _Error(Exception): + + _word_for_schema_in_error_message: ClassVar[str] + _word_for_instance_in_error_message: ClassVar[str] + + def __init__( + self, + message: str, + validator: str = _unset, # type: ignore[assignment] + path: Iterable[str | int] = (), + cause: Exception | None = None, + context=(), + validator_value: Any = _unset, + instance: Any = _unset, + schema: Mapping[str, Any] | bool = _unset, # type: ignore[assignment] + schema_path: Iterable[str | int] = (), + parent: _Error | None = None, + type_checker: _types.TypeChecker = _unset, # type: ignore[assignment] + ) -> None: + super().__init__( + message, + validator, + path, + cause, + context, + validator_value, + instance, + schema, + schema_path, + parent, + ) + self.message = message + self.path = self.relative_path = deque(path) + self.schema_path = self.relative_schema_path = deque(schema_path) + self.context = list(context) + self.cause = self.__cause__ = cause + self.validator = validator + self.validator_value = validator_value + self.instance = instance + self.schema = schema + self.parent = parent + self._type_checker = type_checker + + for error in context: + error.parent = self + + def __repr__(self) -> str: + return f"<{self.__class__.__name__}: {self.message!r}>" + + def __str__(self) -> str: + essential_for_verbose = ( + self.validator, self.validator_value, self.instance, self.schema, + ) + if any(m is _unset for m in essential_for_verbose): + return self.message + + schema_path = _utils.format_as_index( + container=self._word_for_schema_in_error_message, + indices=list(self.relative_schema_path)[:-1], + ) + instance_path = _utils.format_as_index( + container=self._word_for_instance_in_error_message, + indices=self.relative_path, + ) + prefix = 16 * " " + + return dedent( + f"""\ + {self.message} + + Failed validating {self.validator!r} in {schema_path}: + {_pretty(self.schema, prefix=prefix)} + + On {instance_path}: + {_pretty(self.instance, prefix=prefix)} + """.rstrip(), + ) + + @classmethod + def create_from(cls, other: _Error): + return cls(**other._contents()) + + @property + def absolute_path(self) -> Sequence[str | int]: + parent = self.parent + if parent is None: + return self.relative_path + + path = deque(self.relative_path) + path.extendleft(reversed(parent.absolute_path)) + return path + + @property + def absolute_schema_path(self) -> Sequence[str | int]: + parent = self.parent + if parent is None: + return self.relative_schema_path + + path = deque(self.relative_schema_path) + path.extendleft(reversed(parent.absolute_schema_path)) + return path + + @property + def json_path(self) -> str: + path = "$" + for elem in self.absolute_path: + if isinstance(elem, int): + path += "[" + str(elem) + "]" + elif _JSON_PATH_COMPATIBLE_PROPERTY_PATTERN.match(elem): + path += "." + elem + else: + escaped_elem = elem.replace("\\", "\\\\").replace("'", r"\'") + path += "['" + escaped_elem + "']" + return path + + def _set( + self, + type_checker: _types.TypeChecker | None = None, + **kwargs: Any, + ) -> None: + if type_checker is not None and self._type_checker is _unset: + self._type_checker = type_checker + + for k, v in kwargs.items(): + if getattr(self, k) is _unset: + setattr(self, k, v) + + def _contents(self): + attrs = ( + "message", "cause", "context", "validator", "validator_value", + "path", "schema_path", "instance", "schema", "parent", + ) + return {attr: getattr(self, attr) for attr in attrs} + + def _matches_type(self) -> bool: + try: + # We ignore this as we want to simply crash if this happens + expected = self.schema["type"] # type: ignore[index] + except (KeyError, TypeError): + return False + + if isinstance(expected, str): + return self._type_checker.is_type(self.instance, expected) + + return any( + self._type_checker.is_type(self.instance, expected_type) + for expected_type in expected + ) + + +class ValidationError(_Error): + """ + An instance was invalid under a provided schema. + """ + + _word_for_schema_in_error_message = "schema" + _word_for_instance_in_error_message = "instance" + + +class SchemaError(_Error): + """ + A schema was invalid under its corresponding metaschema. + """ + + _word_for_schema_in_error_message = "metaschema" + _word_for_instance_in_error_message = "schema" + + +@define(slots=False) +class _RefResolutionError(Exception): # noqa: PLW1641 + """ + A ref could not be resolved. + """ + + _DEPRECATION_MESSAGE = ( + "jsonschema.exceptions.RefResolutionError is deprecated as of version " + "4.18.0. If you wish to catch potential reference resolution errors, " + "directly catch referencing.exceptions.Unresolvable." + ) + + _cause: Exception + + def __eq__(self, other): + if self.__class__ is not other.__class__: + return NotImplemented # pragma: no cover -- uncovered but deprecated # noqa: E501 + return self._cause == other._cause + + def __str__(self) -> str: + return str(self._cause) + + +class _WrappedReferencingError(_RefResolutionError, _Unresolvable): # pragma: no cover -- partially uncovered but to be removed # noqa: E501 + def __init__(self, cause: _Unresolvable): + object.__setattr__(self, "_wrapped", cause) + + def __eq__(self, other): + if other.__class__ is self.__class__: + return self._wrapped == other._wrapped + elif other.__class__ is self._wrapped.__class__: + return self._wrapped == other + return NotImplemented + + def __getattr__(self, attr): + return getattr(self._wrapped, attr) + + def __hash__(self): + return hash(self._wrapped) + + def __repr__(self): + return f"" + + def __str__(self): + return f"{self._wrapped.__class__.__name__}: {self._wrapped}" + + +class UndefinedTypeCheck(Exception): + """ + A type checker was asked to check a type it did not have registered. + """ + + def __init__(self, type: str) -> None: + self.type = type + + def __str__(self) -> str: + return f"Type {self.type!r} is unknown to this type checker" + + +class UnknownType(Exception): + """ + A validator was asked to validate an instance against an unknown type. + """ + + def __init__(self, type, instance, schema): + self.type = type + self.instance = instance + self.schema = schema + + def __str__(self): + prefix = 16 * " " + + return dedent( + f"""\ + Unknown type {self.type!r} for validator with schema: + {_pretty(self.schema, prefix=prefix)} + + While checking instance: + {_pretty(self.instance, prefix=prefix)} + """.rstrip(), + ) + + +class FormatError(Exception): + """ + Validating a format failed. + """ + + def __init__(self, message, cause=None): + super().__init__(message, cause) + self.message = message + self.cause = self.__cause__ = cause + + def __str__(self): + return self.message + + +class ErrorTree: + """ + ErrorTrees make it easier to check which validations failed. + """ + + _instance = _unset + + def __init__(self, errors: Iterable[ValidationError] = ()): + self.errors: MutableMapping[str, ValidationError] = {} + self._contents: Mapping[str, ErrorTree] = defaultdict(self.__class__) + + for error in errors: + container = self + for element in error.path: + container = container[element] + container.errors[error.validator] = error + + container._instance = error.instance + + def __contains__(self, index: str | int): + """ + Check whether ``instance[index]`` has any errors. + """ + return index in self._contents + + def __getitem__(self, index): + """ + Retrieve the child tree one level down at the given ``index``. + + If the index is not in the instance that this tree corresponds + to and is not known by this tree, whatever error would be raised + by ``instance.__getitem__`` will be propagated (usually this is + some subclass of `LookupError`. + """ + if self._instance is not _unset and index not in self: + self._instance[index] + return self._contents[index] + + def __setitem__(self, index: str | int, value: ErrorTree): + """ + Add an error to the tree at the given ``index``. + + .. deprecated:: v4.20.0 + + Setting items on an `ErrorTree` is deprecated without replacement. + To populate a tree, provide all of its sub-errors when you + construct the tree. + """ + warnings.warn( + "ErrorTree.__setitem__ is deprecated without replacement.", + DeprecationWarning, + stacklevel=2, + ) + self._contents[index] = value # type: ignore[index] + + def __iter__(self): + """ + Iterate (non-recursively) over the indices in the instance with errors. + """ + return iter(self._contents) + + def __len__(self): + """ + Return the `total_errors`. + """ + return self.total_errors + + def __repr__(self): + total = len(self) + errors = "error" if total == 1 else "errors" + return f"<{self.__class__.__name__} ({total} total {errors})>" + + @property + def total_errors(self): + """ + The total number of errors in the entire tree, including children. + """ + child_errors = sum(len(tree) for _, tree in self._contents.items()) + return len(self.errors) + child_errors + + +def by_relevance(weak=WEAK_MATCHES, strong=STRONG_MATCHES): + """ + Create a key function that can be used to sort errors by relevance. + + Arguments: + weak (set): + a collection of validation keywords to consider to be + "weak". If there are two errors at the same level of the + instance and one is in the set of weak validation keywords, + the other error will take priority. By default, :kw:`anyOf` + and :kw:`oneOf` are considered weak keywords and will be + superseded by other same-level validation errors. + + strong (set): + a collection of validation keywords to consider to be + "strong" + + """ + + def relevance(error): + validator = error.validator + return ( # prefer errors which are ... + -len(error.path), # 'deeper' and thereby more specific + error.path, # earlier (for sibling errors) + validator not in weak, # for a non-low-priority keyword + validator in strong, # for a high priority keyword + not error._matches_type(), # at least match the instance's type + ) # otherwise we'll treat them the same + + return relevance + + +relevance = by_relevance() +""" +A key function (e.g. to use with `sorted`) which sorts errors by relevance. + +Example: + +.. code:: python + + sorted(validator.iter_errors(12), key=jsonschema.exceptions.relevance) +""" + + +def best_match(errors, key=relevance): + """ + Try to find an error that appears to be the best match among given errors. + + In general, errors that are higher up in the instance (i.e. for which + `ValidationError.path` is shorter) are considered better matches, + since they indicate "more" is wrong with the instance. + + If the resulting match is either :kw:`oneOf` or :kw:`anyOf`, the + *opposite* assumption is made -- i.e. the deepest error is picked, + since these keywords only need to match once, and any other errors + may not be relevant. + + Arguments: + errors (collections.abc.Iterable): + + the errors to select from. Do not provide a mixture of + errors from different validation attempts (i.e. from + different instances or schemas), since it won't produce + sensical output. + + key (collections.abc.Callable): + + the key to use when sorting errors. See `relevance` and + transitively `by_relevance` for more details (the default is + to sort with the defaults of that function). Changing the + default is only useful if you want to change the function + that rates errors but still want the error context descent + done by this function. + + Returns: + the best matching error, or ``None`` if the iterable was empty + + .. note:: + + This function is a heuristic. Its return value may change for a given + set of inputs from version to version if better heuristics are added. + + """ + best = max(errors, key=key, default=None) + if best is None: + return + + while best.context: + # Calculate the minimum via nsmallest, because we don't recurse if + # all nested errors have the same relevance (i.e. if min == max == all) + smallest = heapq.nsmallest(2, best.context, key=key) + if len(smallest) == 2 and key(smallest[0]) == key(smallest[1]): # noqa: PLR2004 + return best + best = smallest[0] + return best diff --git a/lib/python3.12/site-packages/jsonschema/protocols.py b/lib/python3.12/site-packages/jsonschema/protocols.py new file mode 100644 index 0000000000000000000000000000000000000000..b6288dcc2dfec270ec216588c4d8b56fe3fb213e --- /dev/null +++ b/lib/python3.12/site-packages/jsonschema/protocols.py @@ -0,0 +1,230 @@ +""" +typing.Protocol classes for jsonschema interfaces. +""" + +# for reference material on Protocols, see +# https://www.python.org/dev/peps/pep-0544/ + +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, ClassVar, Protocol, runtime_checkable + +# in order for Sphinx to resolve references accurately from type annotations, +# it needs to see names like `jsonschema.TypeChecker` +# therefore, only import at type-checking time (to avoid circular references), +# but use `jsonschema` for any types which will otherwise not be resolvable +if TYPE_CHECKING: + from collections.abc import Iterable, Mapping + + import referencing.jsonschema + + from jsonschema import _typing + from jsonschema.exceptions import ValidationError + import jsonschema + import jsonschema.validators + +# For code authors working on the validator protocol, these are the three +# use-cases which should be kept in mind: +# +# 1. As a protocol class, it can be used in type annotations to describe the +# available methods and attributes of a validator +# 2. It is the source of autodoc for the validator documentation +# 3. It is runtime_checkable, meaning that it can be used in isinstance() +# checks. +# +# Since protocols are not base classes, isinstance() checking is limited in +# its capabilities. See docs on runtime_checkable for detail + + +@runtime_checkable +class Validator(Protocol): + """ + The protocol to which all validator classes adhere. + + Arguments: + + schema: + + The schema that the validator object will validate with. + It is assumed to be valid, and providing + an invalid schema can lead to undefined behavior. See + `Validator.check_schema` to validate a schema first. + + registry: + + a schema registry that will be used for looking up JSON references + + resolver: + + a resolver that will be used to resolve :kw:`$ref` + properties (JSON references). If unprovided, one will be created. + + .. deprecated:: v4.18.0 + + `RefResolver <_RefResolver>` has been deprecated in favor of + `referencing`, and with it, this argument. + + format_checker: + + if provided, a checker which will be used to assert about + :kw:`format` properties present in the schema. If unprovided, + *no* format validation is done, and the presence of format + within schemas is strictly informational. Certain formats + require additional packages to be installed in order to assert + against instances. Ensure you've installed `jsonschema` with + its `extra (optional) dependencies ` when + invoking ``pip``. + + .. deprecated:: v4.12.0 + + Subclassing validator classes now explicitly warns this is not part of + their public API. + + """ + + #: An object representing the validator's meta schema (the schema that + #: describes valid schemas in the given version). + META_SCHEMA: ClassVar[Mapping] + + #: A mapping of validation keywords (`str`\s) to functions that + #: validate the keyword with that name. For more information see + #: `creating-validators`. + VALIDATORS: ClassVar[Mapping] + + #: A `jsonschema.TypeChecker` that will be used when validating + #: :kw:`type` keywords in JSON schemas. + TYPE_CHECKER: ClassVar[jsonschema.TypeChecker] + + #: A `jsonschema.FormatChecker` that will be used when validating + #: :kw:`format` keywords in JSON schemas. + FORMAT_CHECKER: ClassVar[jsonschema.FormatChecker] + + #: A function which given a schema returns its ID. + ID_OF: _typing.id_of + + #: The schema that will be used to validate instances + schema: Mapping | bool + + def __init__( + self, + schema: Mapping | bool, + resolver: Any = None, # deprecated + format_checker: jsonschema.FormatChecker | None = None, + *, + registry: referencing.jsonschema.SchemaRegistry = ..., + ) -> None: ... + + @classmethod + def check_schema(cls, schema: Mapping | bool) -> None: + """ + Validate the given schema against the validator's `META_SCHEMA`. + + Raises: + + `jsonschema.exceptions.SchemaError`: + + if the schema is invalid + + """ + + def is_type(self, instance: Any, type: str) -> bool: + """ + Check if the instance is of the given (JSON Schema) type. + + Arguments: + + instance: + + the value to check + + type: + + the name of a known (JSON Schema) type + + Returns: + + whether the instance is of the given type + + Raises: + + `jsonschema.exceptions.UnknownType`: + + if ``type`` is not a known type + + """ + + def is_valid(self, instance: Any) -> bool: + """ + Check if the instance is valid under the current `schema`. + + Returns: + + whether the instance is valid or not + + >>> schema = {"maxItems" : 2} + >>> Draft202012Validator(schema).is_valid([2, 3, 4]) + False + + """ + + def iter_errors(self, instance: Any) -> Iterable[ValidationError]: + r""" + Lazily yield each of the validation errors in the given instance. + + >>> schema = { + ... "type" : "array", + ... "items" : {"enum" : [1, 2, 3]}, + ... "maxItems" : 2, + ... } + >>> v = Draft202012Validator(schema) + >>> for error in sorted(v.iter_errors([2, 3, 4]), key=str): + ... print(error.message) + 4 is not one of [1, 2, 3] + [2, 3, 4] is too long + + .. deprecated:: v4.0.0 + + Calling this function with a second schema argument is deprecated. + Use `Validator.evolve` instead. + """ + + def validate(self, instance: Any) -> None: + """ + Check if the instance is valid under the current `schema`. + + Raises: + + `jsonschema.exceptions.ValidationError`: + + if the instance is invalid + + >>> schema = {"maxItems" : 2} + >>> Draft202012Validator(schema).validate([2, 3, 4]) + Traceback (most recent call last): + ... + ValidationError: [2, 3, 4] is too long + + """ + + def evolve(self, **kwargs) -> Validator: + """ + Create a new validator like this one, but with given changes. + + Preserves all other attributes, so can be used to e.g. create a + validator with a different schema but with the same :kw:`$ref` + resolution behavior. + + >>> validator = Draft202012Validator({}) + >>> validator.evolve(schema={"type": "number"}) + Draft202012Validator(schema={'type': 'number'}, format_checker=None) + + The returned object satisfies the validator protocol, but may not + be of the same concrete class! In particular this occurs + when a :kw:`$ref` occurs to a schema with a different + :kw:`$schema` than this one (i.e. for a different draft). + + >>> validator.evolve( + ... schema={"$schema": Draft7Validator.META_SCHEMA["$id"]} + ... ) + Draft7Validator(schema=..., format_checker=None) + """ diff --git a/lib/python3.12/site-packages/jsonschema/tests/__init__.py b/lib/python3.12/site-packages/jsonschema/tests/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/lib/python3.12/site-packages/jsonschema/tests/__pycache__/__init__.cpython-312.pyc b/lib/python3.12/site-packages/jsonschema/tests/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a483734846ea50a7f74c18ab2c5416fda424a21d Binary files /dev/null and b/lib/python3.12/site-packages/jsonschema/tests/__pycache__/__init__.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/jsonschema/tests/__pycache__/_suite.cpython-312.pyc b/lib/python3.12/site-packages/jsonschema/tests/__pycache__/_suite.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..558cc184eeb66c8403d815fe427bfefee8be7ef3 Binary files /dev/null and b/lib/python3.12/site-packages/jsonschema/tests/__pycache__/_suite.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/jsonschema/tests/__pycache__/fuzz_validate.cpython-312.pyc b/lib/python3.12/site-packages/jsonschema/tests/__pycache__/fuzz_validate.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..747ad756d41436c6cfea3c8066ebed52da9af848 Binary files /dev/null and b/lib/python3.12/site-packages/jsonschema/tests/__pycache__/fuzz_validate.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/jsonschema/tests/__pycache__/test_cli.cpython-312.pyc b/lib/python3.12/site-packages/jsonschema/tests/__pycache__/test_cli.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2c44fdf7e154f77c9632ac030d6ae6c5ee55ba78 Binary files /dev/null and b/lib/python3.12/site-packages/jsonschema/tests/__pycache__/test_cli.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/jsonschema/tests/__pycache__/test_deprecations.cpython-312.pyc b/lib/python3.12/site-packages/jsonschema/tests/__pycache__/test_deprecations.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dd0ea356df475453399b1b62dcc1f3aa03c3ddeb Binary files /dev/null and b/lib/python3.12/site-packages/jsonschema/tests/__pycache__/test_deprecations.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/jsonschema/tests/__pycache__/test_exceptions.cpython-312.pyc b/lib/python3.12/site-packages/jsonschema/tests/__pycache__/test_exceptions.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ac98d029e1f09862db91f8ba7b3710be37088244 Binary files /dev/null and b/lib/python3.12/site-packages/jsonschema/tests/__pycache__/test_exceptions.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/jsonschema/tests/__pycache__/test_format.cpython-312.pyc b/lib/python3.12/site-packages/jsonschema/tests/__pycache__/test_format.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8584924162638ce611e5974817cb2025954064e7 Binary files /dev/null and b/lib/python3.12/site-packages/jsonschema/tests/__pycache__/test_format.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/jsonschema/tests/__pycache__/test_jsonschema_test_suite.cpython-312.pyc b/lib/python3.12/site-packages/jsonschema/tests/__pycache__/test_jsonschema_test_suite.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..76e0cf3981f781f89db332b441d22dd86c0ca92f Binary files /dev/null and b/lib/python3.12/site-packages/jsonschema/tests/__pycache__/test_jsonschema_test_suite.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/jsonschema/tests/__pycache__/test_types.cpython-312.pyc b/lib/python3.12/site-packages/jsonschema/tests/__pycache__/test_types.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f59061874e930e6f54fc71e2995334619a2bef8b Binary files /dev/null and b/lib/python3.12/site-packages/jsonschema/tests/__pycache__/test_types.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/jsonschema/tests/__pycache__/test_utils.cpython-312.pyc b/lib/python3.12/site-packages/jsonschema/tests/__pycache__/test_utils.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ef19c9f7dbb7af0a37e57b97d40713e7d1e72e6b Binary files /dev/null and b/lib/python3.12/site-packages/jsonschema/tests/__pycache__/test_utils.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/jsonschema/tests/_suite.py b/lib/python3.12/site-packages/jsonschema/tests/_suite.py new file mode 100644 index 0000000000000000000000000000000000000000..d61d38277d31d3eba23f6681d92066b8dba3ffc6 --- /dev/null +++ b/lib/python3.12/site-packages/jsonschema/tests/_suite.py @@ -0,0 +1,285 @@ +""" +Python representations of the JSON Schema Test Suite tests. +""" +from __future__ import annotations + +from contextlib import suppress +from functools import partial +from pathlib import Path +from typing import TYPE_CHECKING, Any +import json +import os +import re +import sys +import unittest + +from attrs import field, frozen +from referencing import Registry +import referencing.jsonschema + +if TYPE_CHECKING: + from collections.abc import Iterable, Mapping, Sequence + + from referencing.jsonschema import Schema + import pyperf + +from jsonschema.validators import _VALIDATORS +import jsonschema + +MAGIC_REMOTE_URL = "http://localhost:1234" + +_DELIMITERS = re.compile(r"[\W\- ]+") + + +def _find_suite(): + root = os.environ.get("JSON_SCHEMA_TEST_SUITE") + if root is not None: + return Path(root) + + root = Path(jsonschema.__file__).parent.parent / "json" + if not root.is_dir(): # pragma: no cover + raise ValueError( + ( + "Can't find the JSON-Schema-Test-Suite directory. " + "Set the 'JSON_SCHEMA_TEST_SUITE' environment " + "variable or run the tests from alongside a checkout " + "of the suite." + ), + ) + return root + + +@frozen +class Suite: + + _root: Path = field(factory=_find_suite) + + + def benchmark(self, runner: pyperf.Runner): # pragma: no cover + for name, Validator in _VALIDATORS.items(): + self.version(name=name).benchmark( + runner=runner, + Validator=Validator, + ) + + def version(self, name) -> Version: + Validator = _VALIDATORS[name] + uri: str = Validator.ID_OF(Validator.META_SCHEMA) # type: ignore[assignment] + specification = referencing.jsonschema.specification_with(uri) + + registry = Registry().with_contents( + remotes_in(root=self._root / "remotes", name=name, uri=uri), + default_specification=specification, + ) + return Version( + name=name, + path=self._root / "tests" / name, + remotes=registry, + ) + + +@frozen +class Version: + + _path: Path + _remotes: referencing.jsonschema.SchemaRegistry + + name: str + + def benchmark(self, **kwargs): # pragma: no cover + for case in self.cases(): + case.benchmark(**kwargs) + + def cases(self) -> Iterable[_Case]: + return self._cases_in(paths=self._path.glob("*.json")) + + def format_cases(self) -> Iterable[_Case]: + return self._cases_in(paths=self._path.glob("optional/format/*.json")) + + def optional_cases_of(self, name: str) -> Iterable[_Case]: + return self._cases_in(paths=[self._path / "optional" / f"{name}.json"]) + + def to_unittest_testcase(self, *groups, **kwargs): + name = kwargs.pop("name", "Test" + self.name.title().replace("-", "")) + methods = { + method.__name__: method + for method in ( + test.to_unittest_method(**kwargs) + for group in groups + for case in group + for test in case.tests + ) + } + cls = type(name, (unittest.TestCase,), methods) + + # We're doing crazy things, so if they go wrong, like a function + # behaving differently on some other interpreter, just make them + # not happen. + with suppress(Exception): + cls.__module__ = _someone_save_us_the_module_of_the_caller() + + return cls + + def _cases_in(self, paths: Iterable[Path]) -> Iterable[_Case]: + for path in paths: + for case in json.loads(path.read_text(encoding="utf-8")): + yield _Case.from_dict( + case, + version=self, + subject=path.stem, + remotes=self._remotes, + ) + + +@frozen +class _Case: + + version: Version + + subject: str + description: str + schema: Mapping[str, Any] | bool + tests: list[_Test] + comment: str | None = None + specification: Sequence[dict[str, str]] = () + + @classmethod + def from_dict(cls, data, remotes, **kwargs): + data.update(kwargs) + tests = [ + _Test( + version=data["version"], + subject=data["subject"], + case_description=data["description"], + schema=data["schema"], + remotes=remotes, + **test, + ) for test in data.pop("tests") + ] + return cls(tests=tests, **data) + + def benchmark(self, runner: pyperf.Runner, **kwargs): # pragma: no cover + for test in self.tests: + runner.bench_func( + test.fully_qualified_name, + partial(test.validate_ignoring_errors, **kwargs), + ) + + +def remotes_in( + root: Path, + name: str, + uri: str, +) -> Iterable[tuple[str, Schema]]: + # This messy logic is because the test suite is terrible at indicating + # what remotes are needed for what drafts, and mixes in schemas which + # have no $schema and which are invalid under earlier versions, in with + # other schemas which are needed for tests. + + for each in root.rglob("*.json"): + schema = json.loads(each.read_text()) + + relative = str(each.relative_to(root)).replace("\\", "/") + + if ( + ( # invalid boolean schema + name in {"draft3", "draft4"} + and each.stem == "tree" + ) or + ( # draft/*.json + "$schema" not in schema + and relative.startswith("draft") + and not relative.startswith(name) + ) + ): + continue + yield f"{MAGIC_REMOTE_URL}/{relative}", schema + + +@frozen(repr=False) +class _Test: + + version: Version + + subject: str + case_description: str + description: str + + data: Any + schema: Mapping[str, Any] | bool + + valid: bool + + _remotes: referencing.jsonschema.SchemaRegistry + + comment: str | None = None + + def __repr__(self): # pragma: no cover + return f"" + + @property + def fully_qualified_name(self): # pragma: no cover + return " > ".join( # noqa: FLY002 + [ + self.version.name, + self.subject, + self.case_description, + self.description, + ], + ) + + def to_unittest_method(self, skip=lambda test: None, **kwargs): + if self.valid: + def fn(this): + self.validate(**kwargs) + else: + def fn(this): + with this.assertRaises(jsonschema.ValidationError): + self.validate(**kwargs) + + fn.__name__ = "_".join( + [ + "test", + _DELIMITERS.sub("_", self.subject), + _DELIMITERS.sub("_", self.case_description), + _DELIMITERS.sub("_", self.description), + ], + ) + reason = skip(self) + if reason is None or os.environ.get("JSON_SCHEMA_DEBUG", "0") != "0": + return fn + elif os.environ.get("JSON_SCHEMA_EXPECTED_FAILURES", "0") != "0": # pragma: no cover # noqa: E501 + return unittest.expectedFailure(fn) + else: + return unittest.skip(reason)(fn) + + def validate(self, Validator, **kwargs): + Validator.check_schema(self.schema) + validator = Validator( + schema=self.schema, + registry=self._remotes, + **kwargs, + ) + if os.environ.get("JSON_SCHEMA_DEBUG", "0") != "0": # pragma: no cover + breakpoint() # noqa: T100 + validator.validate(instance=self.data) + + def validate_ignoring_errors(self, Validator): # pragma: no cover + with suppress(jsonschema.ValidationError): + self.validate(Validator=Validator) + + +def _someone_save_us_the_module_of_the_caller(): + """ + The FQON of the module 2nd stack frames up from here. + + This is intended to allow us to dynamically return test case classes that + are indistinguishable from being defined in the module that wants them. + + Otherwise, trial will mis-print the FQON, and copy pasting it won't re-run + the class that really is running. + + Save us all, this is all so so so so so terrible. + """ + + return sys._getframe(2).f_globals["__name__"] diff --git a/lib/python3.12/site-packages/jsonschema/tests/fuzz_validate.py b/lib/python3.12/site-packages/jsonschema/tests/fuzz_validate.py new file mode 100644 index 0000000000000000000000000000000000000000..c12e88bcfe9bfdc0e0ffaab502789a6b585d4be2 --- /dev/null +++ b/lib/python3.12/site-packages/jsonschema/tests/fuzz_validate.py @@ -0,0 +1,50 @@ +""" +Fuzzing setup for OSS-Fuzz. + +See https://github.com/google/oss-fuzz/tree/master/projects/jsonschema for the +other half of the setup here. +""" +import sys + +from hypothesis import given, strategies + +import jsonschema + +PRIM = strategies.one_of( + strategies.booleans(), + strategies.integers(), + strategies.floats(allow_nan=False, allow_infinity=False), + strategies.text(), +) +DICT = strategies.recursive( + base=strategies.one_of( + strategies.booleans(), + strategies.dictionaries(strategies.text(), PRIM), + ), + extend=lambda inner: strategies.dictionaries(strategies.text(), inner), +) + + +@given(obj1=DICT, obj2=DICT) +def test_schemas(obj1, obj2): + try: + jsonschema.validate(instance=obj1, schema=obj2) + except jsonschema.exceptions.ValidationError: + pass + except jsonschema.exceptions.SchemaError: + pass + + +def main(): + atheris.instrument_all() + atheris.Setup( + sys.argv, + test_schemas.hypothesis.fuzz_one_input, + enable_python_coverage=True, + ) + atheris.Fuzz() + + +if __name__ == "__main__": + import atheris + main() diff --git a/lib/python3.12/site-packages/jsonschema/tests/test_cli.py b/lib/python3.12/site-packages/jsonschema/tests/test_cli.py new file mode 100644 index 0000000000000000000000000000000000000000..bed9f3e4c4089a4cc653ffdb02d3648042df4088 --- /dev/null +++ b/lib/python3.12/site-packages/jsonschema/tests/test_cli.py @@ -0,0 +1,904 @@ +from contextlib import redirect_stderr, redirect_stdout +from importlib import metadata +from io import StringIO +from json import JSONDecodeError +from pathlib import Path +from textwrap import dedent +from unittest import TestCase +import json +import os +import subprocess +import sys +import tempfile +import warnings + +from jsonschema import Draft4Validator, Draft202012Validator +from jsonschema.exceptions import ( + SchemaError, + ValidationError, + _RefResolutionError, +) +from jsonschema.validators import _LATEST_VERSION, validate + +with warnings.catch_warnings(): + warnings.simplefilter("ignore") + from jsonschema import cli + + +def fake_validator(*errors): + errors = list(reversed(errors)) + + class FakeValidator: + def __init__(self, *args, **kwargs): + pass + + def iter_errors(self, instance): + if errors: + return errors.pop() + return [] # pragma: no cover + + @classmethod + def check_schema(self, schema): + pass + + return FakeValidator + + +def fake_open(all_contents): + def open(path): + contents = all_contents.get(path) + if contents is None: + raise FileNotFoundError(path) + return StringIO(contents) + return open + + +def _message_for(non_json): + try: + json.loads(non_json) + except JSONDecodeError as error: + return str(error) + else: # pragma: no cover + raise RuntimeError("Tried and failed to capture a JSON dump error.") + + +class TestCLI(TestCase): + def run_cli( + self, argv, files=None, stdin=StringIO(), exit_code=0, **override, + ): + arguments = cli.parse_args(argv) + arguments.update(override) + + self.assertFalse(hasattr(cli, "open")) + cli.open = fake_open(files or {}) + try: + stdout, stderr = StringIO(), StringIO() + actual_exit_code = cli.run( + arguments, + stdin=stdin, + stdout=stdout, + stderr=stderr, + ) + finally: + del cli.open + + self.assertEqual( + actual_exit_code, exit_code, msg=dedent( + f""" + Expected an exit code of {exit_code} != {actual_exit_code}. + + stdout: {stdout.getvalue()} + + stderr: {stderr.getvalue()} + """, + ), + ) + return stdout.getvalue(), stderr.getvalue() + + def assertOutputs(self, stdout="", stderr="", **kwargs): + self.assertEqual( + self.run_cli(**kwargs), + (dedent(stdout), dedent(stderr)), + ) + + def test_invalid_instance(self): + error = ValidationError("I am an error!", instance=12) + self.assertOutputs( + files=dict( + some_schema='{"does not": "matter since it is stubbed"}', + some_instance=json.dumps(error.instance), + ), + validator=fake_validator([error]), + + argv=["-i", "some_instance", "some_schema"], + + exit_code=1, + stderr="12: I am an error!\n", + ) + + def test_invalid_instance_pretty_output(self): + error = ValidationError("I am an error!", instance=12) + self.assertOutputs( + files=dict( + some_schema='{"does not": "matter since it is stubbed"}', + some_instance=json.dumps(error.instance), + ), + validator=fake_validator([error]), + + argv=["-i", "some_instance", "--output", "pretty", "some_schema"], + + exit_code=1, + stderr="""\ + ===[ValidationError]===(some_instance)=== + + I am an error! + ----------------------------- + """, + ) + + def test_invalid_instance_explicit_plain_output(self): + error = ValidationError("I am an error!", instance=12) + self.assertOutputs( + files=dict( + some_schema='{"does not": "matter since it is stubbed"}', + some_instance=json.dumps(error.instance), + ), + validator=fake_validator([error]), + + argv=["--output", "plain", "-i", "some_instance", "some_schema"], + + exit_code=1, + stderr="12: I am an error!\n", + ) + + def test_invalid_instance_multiple_errors(self): + instance = 12 + first = ValidationError("First error", instance=instance) + second = ValidationError("Second error", instance=instance) + + self.assertOutputs( + files=dict( + some_schema='{"does not": "matter since it is stubbed"}', + some_instance=json.dumps(instance), + ), + validator=fake_validator([first, second]), + + argv=["-i", "some_instance", "some_schema"], + + exit_code=1, + stderr="""\ + 12: First error + 12: Second error + """, + ) + + def test_invalid_instance_multiple_errors_pretty_output(self): + instance = 12 + first = ValidationError("First error", instance=instance) + second = ValidationError("Second error", instance=instance) + + self.assertOutputs( + files=dict( + some_schema='{"does not": "matter since it is stubbed"}', + some_instance=json.dumps(instance), + ), + validator=fake_validator([first, second]), + + argv=["-i", "some_instance", "--output", "pretty", "some_schema"], + + exit_code=1, + stderr="""\ + ===[ValidationError]===(some_instance)=== + + First error + ----------------------------- + ===[ValidationError]===(some_instance)=== + + Second error + ----------------------------- + """, + ) + + def test_multiple_invalid_instances(self): + first_instance = 12 + first_errors = [ + ValidationError("An error", instance=first_instance), + ValidationError("Another error", instance=first_instance), + ] + second_instance = "foo" + second_errors = [ValidationError("BOOM", instance=second_instance)] + + self.assertOutputs( + files=dict( + some_schema='{"does not": "matter since it is stubbed"}', + some_first_instance=json.dumps(first_instance), + some_second_instance=json.dumps(second_instance), + ), + validator=fake_validator(first_errors, second_errors), + + argv=[ + "-i", "some_first_instance", + "-i", "some_second_instance", + "some_schema", + ], + + exit_code=1, + stderr="""\ + 12: An error + 12: Another error + foo: BOOM + """, + ) + + def test_multiple_invalid_instances_pretty_output(self): + first_instance = 12 + first_errors = [ + ValidationError("An error", instance=first_instance), + ValidationError("Another error", instance=first_instance), + ] + second_instance = "foo" + second_errors = [ValidationError("BOOM", instance=second_instance)] + + self.assertOutputs( + files=dict( + some_schema='{"does not": "matter since it is stubbed"}', + some_first_instance=json.dumps(first_instance), + some_second_instance=json.dumps(second_instance), + ), + validator=fake_validator(first_errors, second_errors), + + argv=[ + "--output", "pretty", + "-i", "some_first_instance", + "-i", "some_second_instance", + "some_schema", + ], + + exit_code=1, + stderr="""\ + ===[ValidationError]===(some_first_instance)=== + + An error + ----------------------------- + ===[ValidationError]===(some_first_instance)=== + + Another error + ----------------------------- + ===[ValidationError]===(some_second_instance)=== + + BOOM + ----------------------------- + """, + ) + + def test_custom_error_format(self): + first_instance = 12 + first_errors = [ + ValidationError("An error", instance=first_instance), + ValidationError("Another error", instance=first_instance), + ] + second_instance = "foo" + second_errors = [ValidationError("BOOM", instance=second_instance)] + + self.assertOutputs( + files=dict( + some_schema='{"does not": "matter since it is stubbed"}', + some_first_instance=json.dumps(first_instance), + some_second_instance=json.dumps(second_instance), + ), + validator=fake_validator(first_errors, second_errors), + + argv=[ + "--error-format", ":{error.message}._-_.{error.instance}:", + "-i", "some_first_instance", + "-i", "some_second_instance", + "some_schema", + ], + + exit_code=1, + stderr=":An error._-_.12::Another error._-_.12::BOOM._-_.foo:", + ) + + def test_invalid_schema(self): + self.assertOutputs( + files=dict(some_schema='{"type": 12}'), + argv=["some_schema"], + + exit_code=1, + stderr="""\ + 12: 12 is not valid under any of the given schemas + """, + ) + + def test_invalid_schema_pretty_output(self): + schema = {"type": 12} + + with self.assertRaises(SchemaError) as e: + validate(schema=schema, instance="") + error = str(e.exception) + + self.assertOutputs( + files=dict(some_schema=json.dumps(schema)), + argv=["--output", "pretty", "some_schema"], + + exit_code=1, + stderr=( + "===[SchemaError]===(some_schema)===\n\n" + + str(error) + + "\n-----------------------------\n" + ), + ) + + def test_invalid_schema_multiple_errors(self): + self.assertOutputs( + files=dict(some_schema='{"type": 12, "items": 57}'), + argv=["some_schema"], + + exit_code=1, + stderr="""\ + 57: 57 is not of type 'object', 'boolean' + """, + ) + + def test_invalid_schema_multiple_errors_pretty_output(self): + schema = {"type": 12, "items": 57} + + with self.assertRaises(SchemaError) as e: + validate(schema=schema, instance="") + error = str(e.exception) + + self.assertOutputs( + files=dict(some_schema=json.dumps(schema)), + argv=["--output", "pretty", "some_schema"], + + exit_code=1, + stderr=( + "===[SchemaError]===(some_schema)===\n\n" + + str(error) + + "\n-----------------------------\n" + ), + ) + + def test_invalid_schema_with_invalid_instance(self): + """ + "Validating" an instance that's invalid under an invalid schema + just shows the schema error. + """ + self.assertOutputs( + files=dict( + some_schema='{"type": 12, "minimum": 30}', + some_instance="13", + ), + argv=["-i", "some_instance", "some_schema"], + + exit_code=1, + stderr="""\ + 12: 12 is not valid under any of the given schemas + """, + ) + + def test_invalid_schema_with_invalid_instance_pretty_output(self): + instance, schema = 13, {"type": 12, "minimum": 30} + + with self.assertRaises(SchemaError) as e: + validate(schema=schema, instance=instance) + error = str(e.exception) + + self.assertOutputs( + files=dict( + some_schema=json.dumps(schema), + some_instance=json.dumps(instance), + ), + argv=["--output", "pretty", "-i", "some_instance", "some_schema"], + + exit_code=1, + stderr=( + "===[SchemaError]===(some_schema)===\n\n" + + str(error) + + "\n-----------------------------\n" + ), + ) + + def test_invalid_instance_continues_with_the_rest(self): + self.assertOutputs( + files=dict( + some_schema='{"minimum": 30}', + first_instance="not valid JSON!", + second_instance="12", + ), + argv=[ + "-i", "first_instance", + "-i", "second_instance", + "some_schema", + ], + + exit_code=1, + stderr="""\ + Failed to parse 'first_instance': {} + 12: 12 is less than the minimum of 30 + """.format(_message_for("not valid JSON!")), + ) + + def test_custom_error_format_applies_to_schema_errors(self): + instance, schema = 13, {"type": 12, "minimum": 30} + + with self.assertRaises(SchemaError): + validate(schema=schema, instance=instance) + + self.assertOutputs( + files=dict(some_schema=json.dumps(schema)), + + argv=[ + "--error-format", ":{error.message}._-_.{error.instance}:", + "some_schema", + ], + + exit_code=1, + stderr=":12 is not valid under any of the given schemas._-_.12:", + ) + + def test_instance_is_invalid_JSON(self): + instance = "not valid JSON!" + + self.assertOutputs( + files=dict(some_schema="{}", some_instance=instance), + argv=["-i", "some_instance", "some_schema"], + + exit_code=1, + stderr=f"""\ + Failed to parse 'some_instance': {_message_for(instance)} + """, + ) + + def test_instance_is_invalid_JSON_pretty_output(self): + stdout, stderr = self.run_cli( + files=dict( + some_schema="{}", + some_instance="not valid JSON!", + ), + + argv=["--output", "pretty", "-i", "some_instance", "some_schema"], + + exit_code=1, + ) + self.assertFalse(stdout) + self.assertIn( + "(some_instance)===\n\nTraceback (most recent call last):\n", + stderr, + ) + self.assertNotIn("some_schema", stderr) + + def test_instance_is_invalid_JSON_on_stdin(self): + instance = "not valid JSON!" + + self.assertOutputs( + files=dict(some_schema="{}"), + stdin=StringIO(instance), + + argv=["some_schema"], + + exit_code=1, + stderr=f"""\ + Failed to parse : {_message_for(instance)} + """, + ) + + def test_instance_is_invalid_JSON_on_stdin_pretty_output(self): + stdout, stderr = self.run_cli( + files=dict(some_schema="{}"), + stdin=StringIO("not valid JSON!"), + + argv=["--output", "pretty", "some_schema"], + + exit_code=1, + ) + self.assertFalse(stdout) + self.assertIn( + "()===\n\nTraceback (most recent call last):\n", + stderr, + ) + self.assertNotIn("some_schema", stderr) + + def test_schema_is_invalid_JSON(self): + schema = "not valid JSON!" + + self.assertOutputs( + files=dict(some_schema=schema), + + argv=["some_schema"], + + exit_code=1, + stderr=f"""\ + Failed to parse 'some_schema': {_message_for(schema)} + """, + ) + + def test_schema_is_invalid_JSON_pretty_output(self): + stdout, stderr = self.run_cli( + files=dict(some_schema="not valid JSON!"), + + argv=["--output", "pretty", "some_schema"], + + exit_code=1, + ) + self.assertFalse(stdout) + self.assertIn( + "(some_schema)===\n\nTraceback (most recent call last):\n", + stderr, + ) + + def test_schema_and_instance_are_both_invalid_JSON(self): + """ + Only the schema error is reported, as we abort immediately. + """ + schema, instance = "not valid JSON!", "also not valid JSON!" + self.assertOutputs( + files=dict(some_schema=schema, some_instance=instance), + + argv=["some_schema"], + + exit_code=1, + stderr=f"""\ + Failed to parse 'some_schema': {_message_for(schema)} + """, + ) + + def test_schema_and_instance_are_both_invalid_JSON_pretty_output(self): + """ + Only the schema error is reported, as we abort immediately. + """ + stdout, stderr = self.run_cli( + files=dict( + some_schema="not valid JSON!", + some_instance="also not valid JSON!", + ), + + argv=["--output", "pretty", "-i", "some_instance", "some_schema"], + + exit_code=1, + ) + self.assertFalse(stdout) + self.assertIn( + "(some_schema)===\n\nTraceback (most recent call last):\n", + stderr, + ) + self.assertNotIn("some_instance", stderr) + + def test_instance_does_not_exist(self): + self.assertOutputs( + files=dict(some_schema="{}"), + argv=["-i", "nonexisting_instance", "some_schema"], + + exit_code=1, + stderr="""\ + 'nonexisting_instance' does not exist. + """, + ) + + def test_instance_does_not_exist_pretty_output(self): + self.assertOutputs( + files=dict(some_schema="{}"), + argv=[ + "--output", "pretty", + "-i", "nonexisting_instance", + "some_schema", + ], + + exit_code=1, + stderr="""\ + ===[FileNotFoundError]===(nonexisting_instance)=== + + 'nonexisting_instance' does not exist. + ----------------------------- + """, + ) + + def test_schema_does_not_exist(self): + self.assertOutputs( + argv=["nonexisting_schema"], + + exit_code=1, + stderr="'nonexisting_schema' does not exist.\n", + ) + + def test_schema_does_not_exist_pretty_output(self): + self.assertOutputs( + argv=["--output", "pretty", "nonexisting_schema"], + + exit_code=1, + stderr="""\ + ===[FileNotFoundError]===(nonexisting_schema)=== + + 'nonexisting_schema' does not exist. + ----------------------------- + """, + ) + + def test_neither_instance_nor_schema_exist(self): + self.assertOutputs( + argv=["-i", "nonexisting_instance", "nonexisting_schema"], + + exit_code=1, + stderr="'nonexisting_schema' does not exist.\n", + ) + + def test_neither_instance_nor_schema_exist_pretty_output(self): + self.assertOutputs( + argv=[ + "--output", "pretty", + "-i", "nonexisting_instance", + "nonexisting_schema", + ], + + exit_code=1, + stderr="""\ + ===[FileNotFoundError]===(nonexisting_schema)=== + + 'nonexisting_schema' does not exist. + ----------------------------- + """, + ) + + def test_successful_validation(self): + self.assertOutputs( + files=dict(some_schema="{}", some_instance="{}"), + argv=["-i", "some_instance", "some_schema"], + stdout="", + stderr="", + ) + + def test_successful_validation_pretty_output(self): + self.assertOutputs( + files=dict(some_schema="{}", some_instance="{}"), + argv=["--output", "pretty", "-i", "some_instance", "some_schema"], + stdout="===[SUCCESS]===(some_instance)===\n", + stderr="", + ) + + def test_successful_validation_of_stdin(self): + self.assertOutputs( + files=dict(some_schema="{}"), + stdin=StringIO("{}"), + argv=["some_schema"], + stdout="", + stderr="", + ) + + def test_successful_validation_of_stdin_pretty_output(self): + self.assertOutputs( + files=dict(some_schema="{}"), + stdin=StringIO("{}"), + argv=["--output", "pretty", "some_schema"], + stdout="===[SUCCESS]===()===\n", + stderr="", + ) + + def test_successful_validation_of_just_the_schema(self): + self.assertOutputs( + files=dict(some_schema="{}", some_instance="{}"), + argv=["-i", "some_instance", "some_schema"], + stdout="", + stderr="", + ) + + def test_successful_validation_of_just_the_schema_pretty_output(self): + self.assertOutputs( + files=dict(some_schema="{}", some_instance="{}"), + argv=["--output", "pretty", "-i", "some_instance", "some_schema"], + stdout="===[SUCCESS]===(some_instance)===\n", + stderr="", + ) + + def test_successful_validation_via_explicit_base_uri(self): + ref_schema_file = tempfile.NamedTemporaryFile(delete=False) # noqa: SIM115 + ref_schema_file.close() + self.addCleanup(os.remove, ref_schema_file.name) + + ref_path = Path(ref_schema_file.name) + ref_path.write_text('{"definitions": {"num": {"type": "integer"}}}') + + schema = f'{{"$ref": "{ref_path.name}#/definitions/num"}}' + + self.assertOutputs( + files=dict(some_schema=schema, some_instance="1"), + argv=[ + "-i", "some_instance", + "--base-uri", ref_path.parent.as_uri() + "/", + "some_schema", + ], + stdout="", + stderr="", + ) + + def test_unsuccessful_validation_via_explicit_base_uri(self): + ref_schema_file = tempfile.NamedTemporaryFile(delete=False) # noqa: SIM115 + ref_schema_file.close() + self.addCleanup(os.remove, ref_schema_file.name) + + ref_path = Path(ref_schema_file.name) + ref_path.write_text('{"definitions": {"num": {"type": "integer"}}}') + + schema = f'{{"$ref": "{ref_path.name}#/definitions/num"}}' + + self.assertOutputs( + files=dict(some_schema=schema, some_instance='"1"'), + argv=[ + "-i", "some_instance", + "--base-uri", ref_path.parent.as_uri() + "/", + "some_schema", + ], + exit_code=1, + stdout="", + stderr="1: '1' is not of type 'integer'\n", + ) + + def test_nonexistent_file_with_explicit_base_uri(self): + schema = '{"$ref": "someNonexistentFile.json#definitions/num"}' + instance = "1" + + with self.assertRaises(_RefResolutionError) as e: + self.assertOutputs( + files=dict( + some_schema=schema, + some_instance=instance, + ), + argv=[ + "-i", "some_instance", + "--base-uri", Path.cwd().as_uri(), + "some_schema", + ], + ) + error = str(e.exception) + self.assertIn(f"{os.sep}someNonexistentFile.json'", error) + + def test_invalid_explicit_base_uri(self): + schema = '{"$ref": "foo.json#definitions/num"}' + instance = "1" + + with self.assertRaises(_RefResolutionError) as e: + self.assertOutputs( + files=dict( + some_schema=schema, + some_instance=instance, + ), + argv=[ + "-i", "some_instance", + "--base-uri", "not@UR1", + "some_schema", + ], + ) + error = str(e.exception) + self.assertEqual( + error, "unknown url type: 'foo.json'", + ) + + def test_it_validates_using_the_latest_validator_when_unspecified(self): + # There isn't a better way now I can think of to ensure that the + # latest version was used, given that the call to validator_for + # is hidden inside the CLI, so guard that that's the case, and + # this test will have to be updated when versions change until + # we can think of a better way to ensure this behavior. + self.assertIs(Draft202012Validator, _LATEST_VERSION) + + self.assertOutputs( + files=dict(some_schema='{"const": "check"}', some_instance='"a"'), + argv=["-i", "some_instance", "some_schema"], + exit_code=1, + stdout="", + stderr="a: 'check' was expected\n", + ) + + def test_it_validates_using_draft7_when_specified(self): + """ + Specifically, `const` validation applies for Draft 7. + """ + schema = """ + { + "$schema": "http://json-schema.org/draft-07/schema#", + "const": "check" + } + """ + instance = '"foo"' + self.assertOutputs( + files=dict(some_schema=schema, some_instance=instance), + argv=["-i", "some_instance", "some_schema"], + exit_code=1, + stdout="", + stderr="foo: 'check' was expected\n", + ) + + def test_it_validates_using_draft4_when_specified(self): + """ + Specifically, `const` validation *does not* apply for Draft 4. + """ + schema = """ + { + "$schema": "http://json-schema.org/draft-04/schema#", + "const": "check" + } + """ + instance = '"foo"' + self.assertOutputs( + files=dict(some_schema=schema, some_instance=instance), + argv=["-i", "some_instance", "some_schema"], + stdout="", + stderr="", + ) + + +class TestParser(TestCase): + + FakeValidator = fake_validator() + + def test_find_validator_by_fully_qualified_object_name(self): + arguments = cli.parse_args( + [ + "--validator", + "jsonschema.tests.test_cli.TestParser.FakeValidator", + "--instance", "mem://some/instance", + "mem://some/schema", + ], + ) + self.assertIs(arguments["validator"], self.FakeValidator) + + def test_find_validator_in_jsonschema(self): + arguments = cli.parse_args( + [ + "--validator", "Draft4Validator", + "--instance", "mem://some/instance", + "mem://some/schema", + ], + ) + self.assertIs(arguments["validator"], Draft4Validator) + + def cli_output_for(self, *argv): + stdout, stderr = StringIO(), StringIO() + with redirect_stdout(stdout), redirect_stderr(stderr): # noqa: SIM117 + with self.assertRaises(SystemExit): + cli.parse_args(argv) + return stdout.getvalue(), stderr.getvalue() + + def test_unknown_output(self): + stdout, stderr = self.cli_output_for( + "--output", "foo", + "mem://some/schema", + ) + self.assertIn("invalid choice: 'foo'", stderr) + self.assertFalse(stdout) + + def test_useless_error_format(self): + stdout, stderr = self.cli_output_for( + "--output", "pretty", + "--error-format", "foo", + "mem://some/schema", + ) + self.assertIn( + "--error-format can only be used with --output plain", + stderr, + ) + self.assertFalse(stdout) + + +class TestCLIIntegration(TestCase): + def test_license(self): + our_metadata = metadata.metadata("jsonschema") + self.assertEqual(our_metadata.get("License-Expression"), "MIT") + + def test_version(self): + version = subprocess.check_output( + [sys.executable, "-W", "ignore", "-m", "jsonschema", "--version"], + stderr=subprocess.STDOUT, + ) + version = version.decode("utf-8").strip() + self.assertEqual(version, metadata.version("jsonschema")) + + def test_no_arguments_shows_usage_notes(self): + output = subprocess.check_output( + [sys.executable, "-m", "jsonschema"], + stderr=subprocess.STDOUT, + ) + output_for_help = subprocess.check_output( + [sys.executable, "-m", "jsonschema", "--help"], + stderr=subprocess.STDOUT, + ) + self.assertEqual(output, output_for_help) diff --git a/lib/python3.12/site-packages/jsonschema/tests/test_deprecations.py b/lib/python3.12/site-packages/jsonschema/tests/test_deprecations.py new file mode 100644 index 0000000000000000000000000000000000000000..a54b02f380ae098ba7b3f2c518a44935859f1ce6 --- /dev/null +++ b/lib/python3.12/site-packages/jsonschema/tests/test_deprecations.py @@ -0,0 +1,432 @@ +from contextlib import contextmanager +from io import BytesIO +from unittest import TestCase, mock +import importlib.metadata +import json +import subprocess +import sys +import urllib.request + +import referencing.exceptions + +from jsonschema import FormatChecker, exceptions, protocols, validators + + +class TestDeprecations(TestCase): + def test_version(self): + """ + As of v4.0.0, __version__ is deprecated in favor of importlib.metadata. + """ + + message = "Accessing jsonschema.__version__ is deprecated" + with self.assertWarnsRegex(DeprecationWarning, message) as w: + from jsonschema import __version__ + + self.assertEqual(__version__, importlib.metadata.version("jsonschema")) + self.assertEqual(w.filename, __file__) + + def test_validators_ErrorTree(self): + """ + As of v4.0.0, importing ErrorTree from jsonschema.validators is + deprecated in favor of doing so from jsonschema.exceptions. + """ + + message = "Importing ErrorTree from jsonschema.validators is " + with self.assertWarnsRegex(DeprecationWarning, message) as w: + from jsonschema.validators import ErrorTree + + self.assertEqual(ErrorTree, exceptions.ErrorTree) + self.assertEqual(w.filename, __file__) + + def test_import_ErrorTree(self): + """ + As of v4.18.0, importing ErrorTree from the package root is + deprecated in favor of doing so from jsonschema.exceptions. + """ + + message = "Importing ErrorTree directly from the jsonschema package " + with self.assertWarnsRegex(DeprecationWarning, message) as w: + from jsonschema import ErrorTree + + self.assertEqual(ErrorTree, exceptions.ErrorTree) + self.assertEqual(w.filename, __file__) + + def test_ErrorTree_setitem(self): + """ + As of v4.20.0, setting items on an ErrorTree is deprecated. + """ + + e = exceptions.ValidationError("some error", path=["foo"]) + tree = exceptions.ErrorTree() + subtree = exceptions.ErrorTree(errors=[e]) + + message = "ErrorTree.__setitem__ is " + with self.assertWarnsRegex(DeprecationWarning, message) as w: + tree["foo"] = subtree + + self.assertEqual(tree["foo"], subtree) + self.assertEqual(w.filename, __file__) + + def test_import_FormatError(self): + """ + As of v4.18.0, importing FormatError from the package root is + deprecated in favor of doing so from jsonschema.exceptions. + """ + + message = "Importing FormatError directly from the jsonschema package " + with self.assertWarnsRegex(DeprecationWarning, message) as w: + from jsonschema import FormatError + + self.assertEqual(FormatError, exceptions.FormatError) + self.assertEqual(w.filename, __file__) + + def test_import_Validator(self): + """ + As of v4.19.0, importing Validator from the package root is + deprecated in favor of doing so from jsonschema.protocols. + """ + + message = "Importing Validator directly from the jsonschema package " + with self.assertWarnsRegex(DeprecationWarning, message) as w: + from jsonschema import Validator + + self.assertEqual(Validator, protocols.Validator) + self.assertEqual(w.filename, __file__) + + def test_validators_validators(self): + """ + As of v4.0.0, accessing jsonschema.validators.validators is + deprecated. + """ + + message = "Accessing jsonschema.validators.validators is deprecated" + with self.assertWarnsRegex(DeprecationWarning, message) as w: + value = validators.validators + + self.assertEqual(value, validators._VALIDATORS) + self.assertEqual(w.filename, __file__) + + def test_validators_meta_schemas(self): + """ + As of v4.0.0, accessing jsonschema.validators.meta_schemas is + deprecated. + """ + + message = "Accessing jsonschema.validators.meta_schemas is deprecated" + with self.assertWarnsRegex(DeprecationWarning, message) as w: + value = validators.meta_schemas + + self.assertEqual(value, validators._META_SCHEMAS) + self.assertEqual(w.filename, __file__) + + def test_RefResolver_in_scope(self): + """ + As of v4.0.0, RefResolver.in_scope is deprecated. + """ + + resolver = validators._RefResolver.from_schema({}) + message = "jsonschema.RefResolver.in_scope is deprecated " + with self.assertWarnsRegex(DeprecationWarning, message) as w: # noqa: SIM117 + with resolver.in_scope("foo"): + pass + + self.assertEqual(w.filename, __file__) + + def test_Validator_is_valid_two_arguments(self): + """ + As of v4.0.0, calling is_valid with two arguments (to provide a + different schema) is deprecated. + """ + + validator = validators.Draft7Validator({}) + message = "Passing a schema to Validator.is_valid is deprecated " + with self.assertWarnsRegex(DeprecationWarning, message) as w: + result = validator.is_valid("foo", {"type": "number"}) + + self.assertFalse(result) + self.assertEqual(w.filename, __file__) + + def test_Validator_iter_errors_two_arguments(self): + """ + As of v4.0.0, calling iter_errors with two arguments (to provide a + different schema) is deprecated. + """ + + validator = validators.Draft7Validator({}) + message = "Passing a schema to Validator.iter_errors is deprecated " + with self.assertWarnsRegex(DeprecationWarning, message) as w: + error, = validator.iter_errors("foo", {"type": "number"}) + + self.assertEqual(error.validator, "type") + self.assertEqual(w.filename, __file__) + + def test_Validator_resolver(self): + """ + As of v4.18.0, accessing Validator.resolver is deprecated. + """ + + validator = validators.Draft7Validator({}) + message = "Accessing Draft7Validator.resolver is " + with self.assertWarnsRegex(DeprecationWarning, message) as w: + self.assertIsInstance(validator.resolver, validators._RefResolver) + + self.assertEqual(w.filename, __file__) + + def test_RefResolver(self): + """ + As of v4.18.0, RefResolver is fully deprecated. + """ + + message = "jsonschema.RefResolver is deprecated" + with self.assertWarnsRegex(DeprecationWarning, message) as w: + from jsonschema import RefResolver + self.assertEqual(w.filename, __file__) + + with self.assertWarnsRegex(DeprecationWarning, message) as w: + from jsonschema.validators import RefResolver # noqa: F401 + self.assertEqual(w.filename, __file__) + + def test_RefResolutionError(self): + """ + As of v4.18.0, RefResolutionError is deprecated in favor of directly + catching errors from the referencing library. + """ + + message = "jsonschema.exceptions.RefResolutionError is deprecated" + with self.assertWarnsRegex(DeprecationWarning, message) as w: + from jsonschema import RefResolutionError + + self.assertEqual(RefResolutionError, exceptions._RefResolutionError) + self.assertEqual(w.filename, __file__) + + with self.assertWarnsRegex(DeprecationWarning, message) as w: + from jsonschema.exceptions import RefResolutionError + + self.assertEqual(RefResolutionError, exceptions._RefResolutionError) + self.assertEqual(w.filename, __file__) + + def test_catching_Unresolvable_directly(self): + """ + This behavior is the intended behavior (i.e. it's not deprecated), but + given we do "tricksy" things in the iterim to wrap exceptions in a + multiple inheritance subclass, we need to be extra sure it works and + stays working. + """ + validator = validators.Draft202012Validator({"$ref": "urn:nothing"}) + + with self.assertRaises(referencing.exceptions.Unresolvable) as e: + validator.validate(12) + + expected = referencing.exceptions.Unresolvable(ref="urn:nothing") + self.assertEqual( + (e.exception, str(e.exception)), + (expected, "Unresolvable: urn:nothing"), + ) + + def test_catching_Unresolvable_via_RefResolutionError(self): + """ + Until RefResolutionError is removed, it is still possible to catch + exceptions from reference resolution using it, even though they may + have been raised by referencing. + """ + with self.assertWarns(DeprecationWarning): + from jsonschema import RefResolutionError + + validator = validators.Draft202012Validator({"$ref": "urn:nothing"}) + + with self.assertRaises(referencing.exceptions.Unresolvable) as u: + validator.validate(12) + + with self.assertRaises(RefResolutionError) as e: + validator.validate(12) + + self.assertEqual( + (e.exception, str(e.exception)), + (u.exception, "Unresolvable: urn:nothing"), + ) + + def test_WrappedReferencingError_hashability(self): + """ + Ensure the wrapped referencing errors are hashable when possible. + """ + with self.assertWarns(DeprecationWarning): + from jsonschema import RefResolutionError + + validator = validators.Draft202012Validator({"$ref": "urn:nothing"}) + + with self.assertRaises(referencing.exceptions.Unresolvable) as u: + validator.validate(12) + + with self.assertRaises(RefResolutionError) as e: + validator.validate(12) + + self.assertIn(e.exception, {u.exception}) + self.assertIn(u.exception, {e.exception}) + + def test_Validator_subclassing(self): + """ + As of v4.12.0, subclassing a validator class produces an explicit + deprecation warning. + + This was never intended to be public API (and some comments over the + years in issues said so, but obviously that's not a great way to make + sure it's followed). + + A future version will explicitly raise an error. + """ + + message = "Subclassing validator classes is " + with self.assertWarnsRegex(DeprecationWarning, message) as w: + class Subclass(validators.Draft202012Validator): + pass + + self.assertEqual(w.filename, __file__) + + with self.assertWarnsRegex(DeprecationWarning, message) as w: + class AnotherSubclass(validators.create(meta_schema={})): + pass + + def test_FormatChecker_cls_checks(self): + """ + As of v4.14.0, FormatChecker.cls_checks is deprecated without + replacement. + """ + + self.addCleanup(FormatChecker.checkers.pop, "boom", None) + + message = "FormatChecker.cls_checks " + with self.assertWarnsRegex(DeprecationWarning, message) as w: + FormatChecker.cls_checks("boom") + + self.assertEqual(w.filename, __file__) + + def test_draftN_format_checker(self): + """ + As of v4.16.0, accessing jsonschema.draftn_format_checker is deprecated + in favor of Validator.FORMAT_CHECKER. + """ + + message = "Accessing jsonschema.draft202012_format_checker is " + with self.assertWarnsRegex(DeprecationWarning, message) as w: + from jsonschema import draft202012_format_checker + + self.assertIs( + draft202012_format_checker, + validators.Draft202012Validator.FORMAT_CHECKER, + ) + self.assertEqual(w.filename, __file__) + + message = "Accessing jsonschema.draft201909_format_checker is " + with self.assertWarnsRegex(DeprecationWarning, message) as w: + from jsonschema import draft201909_format_checker + + self.assertIs( + draft201909_format_checker, + validators.Draft201909Validator.FORMAT_CHECKER, + ) + self.assertEqual(w.filename, __file__) + + message = "Accessing jsonschema.draft7_format_checker is " + with self.assertWarnsRegex(DeprecationWarning, message) as w: + from jsonschema import draft7_format_checker + + self.assertIs( + draft7_format_checker, + validators.Draft7Validator.FORMAT_CHECKER, + ) + self.assertEqual(w.filename, __file__) + + message = "Accessing jsonschema.draft6_format_checker is " + with self.assertWarnsRegex(DeprecationWarning, message) as w: + from jsonschema import draft6_format_checker + + self.assertIs( + draft6_format_checker, + validators.Draft6Validator.FORMAT_CHECKER, + ) + self.assertEqual(w.filename, __file__) + + message = "Accessing jsonschema.draft4_format_checker is " + with self.assertWarnsRegex(DeprecationWarning, message) as w: + from jsonschema import draft4_format_checker + + self.assertIs( + draft4_format_checker, + validators.Draft4Validator.FORMAT_CHECKER, + ) + self.assertEqual(w.filename, __file__) + + message = "Accessing jsonschema.draft3_format_checker is " + with self.assertWarnsRegex(DeprecationWarning, message) as w: + from jsonschema import draft3_format_checker + + self.assertIs( + draft3_format_checker, + validators.Draft3Validator.FORMAT_CHECKER, + ) + self.assertEqual(w.filename, __file__) + + with self.assertRaises(ImportError): + from jsonschema import draft1234_format_checker # noqa: F401 + + def test_import_cli(self): + """ + As of v4.17.0, importing jsonschema.cli is deprecated. + """ + + message = "The jsonschema CLI is deprecated and will be removed " + with self.assertWarnsRegex(DeprecationWarning, message) as w: + import jsonschema.cli + importlib.reload(jsonschema.cli) + + self.assertEqual(w.filename, importlib.__file__) + + def test_cli(self): + """ + As of v4.17.0, the jsonschema CLI is deprecated. + """ + + process = subprocess.run( + [sys.executable, "-m", "jsonschema"], + capture_output=True, + check=True, + ) + self.assertIn(b"The jsonschema CLI is deprecated ", process.stderr) + + def test_automatic_remote_retrieval(self): + """ + Automatic retrieval of remote references is deprecated as of v4.18.0. + """ + ref = "http://bar#/$defs/baz" + schema = {"$defs": {"baz": {"type": "integer"}}} + + if "requests" in sys.modules: # pragma: no cover + self.addCleanup( + sys.modules.__setitem__, "requests", sys.modules["requests"], + ) + sys.modules["requests"] = None + + @contextmanager + def fake_urlopen(request): + self.assertIsInstance(request, urllib.request.Request) + self.assertEqual(request.full_url, "http://bar") + + # Ha ha urllib.request.Request "normalizes" header names and + # Request.get_header does not also normalize them... + (header, value), = request.header_items() + self.assertEqual(header.lower(), "user-agent") + self.assertEqual( + value, "python-jsonschema (deprecated $ref resolution)", + ) + yield BytesIO(json.dumps(schema).encode("utf8")) + + validator = validators.Draft202012Validator({"$ref": ref}) + + message = "Automatically retrieving remote references " + patch = mock.patch.object(urllib.request, "urlopen", new=fake_urlopen) + + with patch, self.assertWarnsRegex(DeprecationWarning, message): + self.assertEqual( + (validator.is_valid({}), validator.is_valid(37)), + (False, True), + ) diff --git a/lib/python3.12/site-packages/jsonschema/tests/test_exceptions.py b/lib/python3.12/site-packages/jsonschema/tests/test_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..358b924252c3d67373b7924d11fdfaf2878ab5b5 --- /dev/null +++ b/lib/python3.12/site-packages/jsonschema/tests/test_exceptions.py @@ -0,0 +1,759 @@ +from unittest import TestCase +import textwrap + +import jsonpath_ng + +from jsonschema import exceptions +from jsonschema.validators import _LATEST_VERSION + + +class TestBestMatch(TestCase): + def best_match_of(self, instance, schema): + errors = list(_LATEST_VERSION(schema).iter_errors(instance)) + msg = f"No errors found for {instance} under {schema!r}!" + self.assertTrue(errors, msg=msg) + + best = exceptions.best_match(iter(errors)) + reversed_best = exceptions.best_match(reversed(errors)) + + self.assertEqual( + best._contents(), + reversed_best._contents(), + f"No consistent best match!\nGot: {best}\n\nThen: {reversed_best}", + ) + return best + + def test_shallower_errors_are_better_matches(self): + schema = { + "properties": { + "foo": { + "minProperties": 2, + "properties": {"bar": {"type": "object"}}, + }, + }, + } + best = self.best_match_of(instance={"foo": {"bar": []}}, schema=schema) + self.assertEqual(best.validator, "minProperties") + + def test_oneOf_and_anyOf_are_weak_matches(self): + """ + A property you *must* match is probably better than one you have to + match a part of. + """ + + schema = { + "minProperties": 2, + "anyOf": [{"type": "string"}, {"type": "number"}], + "oneOf": [{"type": "string"}, {"type": "number"}], + } + best = self.best_match_of(instance={}, schema=schema) + self.assertEqual(best.validator, "minProperties") + + def test_if_the_most_relevant_error_is_anyOf_it_is_traversed(self): + """ + If the most relevant error is an anyOf, then we traverse its context + and select the otherwise *least* relevant error, since in this case + that means the most specific, deep, error inside the instance. + + I.e. since only one of the schemas must match, we look for the most + relevant one. + """ + + schema = { + "properties": { + "foo": { + "anyOf": [ + {"type": "string"}, + {"properties": {"bar": {"type": "array"}}}, + ], + }, + }, + } + best = self.best_match_of(instance={"foo": {"bar": 12}}, schema=schema) + self.assertEqual(best.validator_value, "array") + + def test_no_anyOf_traversal_for_equally_relevant_errors(self): + """ + We don't traverse into an anyOf (as above) if all of its context errors + seem to be equally "wrong" against the instance. + """ + + schema = { + "anyOf": [ + {"type": "string"}, + {"type": "integer"}, + {"type": "object"}, + ], + } + best = self.best_match_of(instance=[], schema=schema) + self.assertEqual(best.validator, "anyOf") + + def test_anyOf_traversal_for_single_equally_relevant_error(self): + """ + We *do* traverse anyOf with a single nested error, even though it is + vacuously equally relevant to itself. + """ + + schema = { + "anyOf": [ + {"type": "string"}, + ], + } + best = self.best_match_of(instance=[], schema=schema) + self.assertEqual(best.validator, "type") + + def test_anyOf_traversal_for_single_sibling_errors(self): + """ + We *do* traverse anyOf with a single subschema that fails multiple + times (e.g. on multiple items). + """ + + schema = { + "anyOf": [ + {"items": {"const": 37}}, + ], + } + best = self.best_match_of(instance=[12, 12], schema=schema) + self.assertEqual(best.validator, "const") + + def test_anyOf_traversal_for_non_type_matching_sibling_errors(self): + """ + We *do* traverse anyOf with multiple subschemas when one does not type + match. + """ + + schema = { + "anyOf": [ + {"type": "object"}, + {"items": {"const": 37}}, + ], + } + best = self.best_match_of(instance=[12, 12], schema=schema) + self.assertEqual(best.validator, "const") + + def test_if_the_most_relevant_error_is_oneOf_it_is_traversed(self): + """ + If the most relevant error is an oneOf, then we traverse its context + and select the otherwise *least* relevant error, since in this case + that means the most specific, deep, error inside the instance. + + I.e. since only one of the schemas must match, we look for the most + relevant one. + """ + + schema = { + "properties": { + "foo": { + "oneOf": [ + {"type": "string"}, + {"properties": {"bar": {"type": "array"}}}, + ], + }, + }, + } + best = self.best_match_of(instance={"foo": {"bar": 12}}, schema=schema) + self.assertEqual(best.validator_value, "array") + + def test_no_oneOf_traversal_for_equally_relevant_errors(self): + """ + We don't traverse into an oneOf (as above) if all of its context errors + seem to be equally "wrong" against the instance. + """ + + schema = { + "oneOf": [ + {"type": "string"}, + {"type": "integer"}, + {"type": "object"}, + ], + } + best = self.best_match_of(instance=[], schema=schema) + self.assertEqual(best.validator, "oneOf") + + def test_oneOf_traversal_for_single_equally_relevant_error(self): + """ + We *do* traverse oneOf with a single nested error, even though it is + vacuously equally relevant to itself. + """ + + schema = { + "oneOf": [ + {"type": "string"}, + ], + } + best = self.best_match_of(instance=[], schema=schema) + self.assertEqual(best.validator, "type") + + def test_oneOf_traversal_for_single_sibling_errors(self): + """ + We *do* traverse oneOf with a single subschema that fails multiple + times (e.g. on multiple items). + """ + + schema = { + "oneOf": [ + {"items": {"const": 37}}, + ], + } + best = self.best_match_of(instance=[12, 12], schema=schema) + self.assertEqual(best.validator, "const") + + def test_oneOf_traversal_for_non_type_matching_sibling_errors(self): + """ + We *do* traverse oneOf with multiple subschemas when one does not type + match. + """ + + schema = { + "oneOf": [ + {"type": "object"}, + {"items": {"const": 37}}, + ], + } + best = self.best_match_of(instance=[12, 12], schema=schema) + self.assertEqual(best.validator, "const") + + def test_if_the_most_relevant_error_is_allOf_it_is_traversed(self): + """ + Now, if the error is allOf, we traverse but select the *most* relevant + error from the context, because all schemas here must match anyways. + """ + + schema = { + "properties": { + "foo": { + "allOf": [ + {"type": "string"}, + {"properties": {"bar": {"type": "array"}}}, + ], + }, + }, + } + best = self.best_match_of(instance={"foo": {"bar": 12}}, schema=schema) + self.assertEqual(best.validator_value, "string") + + def test_nested_context_for_oneOf(self): + """ + We traverse into nested contexts (a oneOf containing an error in a + nested oneOf here). + """ + + schema = { + "properties": { + "foo": { + "oneOf": [ + {"type": "string"}, + { + "oneOf": [ + {"type": "string"}, + { + "properties": { + "bar": {"type": "array"}, + }, + }, + ], + }, + ], + }, + }, + } + best = self.best_match_of(instance={"foo": {"bar": 12}}, schema=schema) + self.assertEqual(best.validator_value, "array") + + def test_it_prioritizes_matching_types(self): + schema = { + "properties": { + "foo": { + "anyOf": [ + {"type": "array", "minItems": 2}, + {"type": "string", "minLength": 10}, + ], + }, + }, + } + best = self.best_match_of(instance={"foo": "bar"}, schema=schema) + self.assertEqual(best.validator, "minLength") + + reordered = { + "properties": { + "foo": { + "anyOf": [ + {"type": "string", "minLength": 10}, + {"type": "array", "minItems": 2}, + ], + }, + }, + } + best = self.best_match_of(instance={"foo": "bar"}, schema=reordered) + self.assertEqual(best.validator, "minLength") + + def test_it_prioritizes_matching_union_types(self): + schema = { + "properties": { + "foo": { + "anyOf": [ + {"type": ["array", "object"], "minItems": 2}, + {"type": ["integer", "string"], "minLength": 10}, + ], + }, + }, + } + best = self.best_match_of(instance={"foo": "bar"}, schema=schema) + self.assertEqual(best.validator, "minLength") + + reordered = { + "properties": { + "foo": { + "anyOf": [ + {"type": "string", "minLength": 10}, + {"type": "array", "minItems": 2}, + ], + }, + }, + } + best = self.best_match_of(instance={"foo": "bar"}, schema=reordered) + self.assertEqual(best.validator, "minLength") + + def test_boolean_schemas(self): + schema = {"properties": {"foo": False}} + best = self.best_match_of(instance={"foo": "bar"}, schema=schema) + self.assertIsNone(best.validator) + + def test_one_error(self): + validator = _LATEST_VERSION({"minProperties": 2}) + validator.iter_errors({}) + self.assertEqual( + exceptions.best_match(validator.iter_errors({})).validator, + "minProperties", + ) + + def test_no_errors(self): + validator = _LATEST_VERSION({}) + self.assertIsNone(exceptions.best_match(validator.iter_errors({}))) + + +class TestByRelevance(TestCase): + def test_short_paths_are_better_matches(self): + shallow = exceptions.ValidationError("Oh no!", path=["baz"]) + deep = exceptions.ValidationError("Oh yes!", path=["foo", "bar"]) + match = max([shallow, deep], key=exceptions.relevance) + self.assertIs(match, shallow) + + match = max([deep, shallow], key=exceptions.relevance) + self.assertIs(match, shallow) + + def test_global_errors_are_even_better_matches(self): + shallow = exceptions.ValidationError("Oh no!", path=[]) + deep = exceptions.ValidationError("Oh yes!", path=["foo"]) + + errors = sorted([shallow, deep], key=exceptions.relevance) + self.assertEqual( + [list(error.path) for error in errors], + [["foo"], []], + ) + + errors = sorted([deep, shallow], key=exceptions.relevance) + self.assertEqual( + [list(error.path) for error in errors], + [["foo"], []], + ) + + def test_weak_keywords_are_lower_priority(self): + weak = exceptions.ValidationError("Oh no!", path=[], validator="a") + normal = exceptions.ValidationError("Oh yes!", path=[], validator="b") + + best_match = exceptions.by_relevance(weak="a") + + match = max([weak, normal], key=best_match) + self.assertIs(match, normal) + + match = max([normal, weak], key=best_match) + self.assertIs(match, normal) + + def test_strong_keywords_are_higher_priority(self): + weak = exceptions.ValidationError("Oh no!", path=[], validator="a") + normal = exceptions.ValidationError("Oh yes!", path=[], validator="b") + strong = exceptions.ValidationError("Oh fine!", path=[], validator="c") + + best_match = exceptions.by_relevance(weak="a", strong="c") + + match = max([weak, normal, strong], key=best_match) + self.assertIs(match, strong) + + match = max([strong, normal, weak], key=best_match) + self.assertIs(match, strong) + + +class TestErrorTree(TestCase): + def test_it_knows_how_many_total_errors_it_contains(self): + # FIXME: #442 + errors = [ + exceptions.ValidationError("Something", validator=i) + for i in range(8) + ] + tree = exceptions.ErrorTree(errors) + self.assertEqual(tree.total_errors, 8) + + def test_it_contains_an_item_if_the_item_had_an_error(self): + errors = [exceptions.ValidationError("a message", path=["bar"])] + tree = exceptions.ErrorTree(errors) + self.assertIn("bar", tree) + + def test_it_does_not_contain_an_item_if_the_item_had_no_error(self): + errors = [exceptions.ValidationError("a message", path=["bar"])] + tree = exceptions.ErrorTree(errors) + self.assertNotIn("foo", tree) + + def test_keywords_that_failed_appear_in_errors_dict(self): + error = exceptions.ValidationError("a message", validator="foo") + tree = exceptions.ErrorTree([error]) + self.assertEqual(tree.errors, {"foo": error}) + + def test_it_creates_a_child_tree_for_each_nested_path(self): + errors = [ + exceptions.ValidationError("a bar message", path=["bar"]), + exceptions.ValidationError("a bar -> 0 message", path=["bar", 0]), + ] + tree = exceptions.ErrorTree(errors) + self.assertIn(0, tree["bar"]) + self.assertNotIn(1, tree["bar"]) + + def test_children_have_their_errors_dicts_built(self): + e1, e2 = ( + exceptions.ValidationError("1", validator="foo", path=["bar", 0]), + exceptions.ValidationError("2", validator="quux", path=["bar", 0]), + ) + tree = exceptions.ErrorTree([e1, e2]) + self.assertEqual(tree["bar"][0].errors, {"foo": e1, "quux": e2}) + + def test_multiple_errors_with_instance(self): + e1, e2 = ( + exceptions.ValidationError( + "1", + validator="foo", + path=["bar", "bar2"], + instance="i1"), + exceptions.ValidationError( + "2", + validator="quux", + path=["foobar", 2], + instance="i2"), + ) + exceptions.ErrorTree([e1, e2]) + + def test_it_does_not_contain_subtrees_that_are_not_in_the_instance(self): + error = exceptions.ValidationError("123", validator="foo", instance=[]) + tree = exceptions.ErrorTree([error]) + + with self.assertRaises(IndexError): + tree[0] + + def test_if_its_in_the_tree_anyhow_it_does_not_raise_an_error(self): + """ + If a keyword refers to a path that isn't in the instance, the + tree still properly returns a subtree for that path. + """ + + error = exceptions.ValidationError( + "a message", validator="foo", instance={}, path=["foo"], + ) + tree = exceptions.ErrorTree([error]) + self.assertIsInstance(tree["foo"], exceptions.ErrorTree) + + def test_iter(self): + e1, e2 = ( + exceptions.ValidationError( + "1", + validator="foo", + path=["bar", "bar2"], + instance="i1"), + exceptions.ValidationError( + "2", + validator="quux", + path=["foobar", 2], + instance="i2"), + ) + tree = exceptions.ErrorTree([e1, e2]) + self.assertEqual(set(tree), {"bar", "foobar"}) + + def test_repr_single(self): + error = exceptions.ValidationError( + "1", + validator="foo", + path=["bar", "bar2"], + instance="i1", + ) + tree = exceptions.ErrorTree([error]) + self.assertEqual(repr(tree), "") + + def test_repr_multiple(self): + e1, e2 = ( + exceptions.ValidationError( + "1", + validator="foo", + path=["bar", "bar2"], + instance="i1"), + exceptions.ValidationError( + "2", + validator="quux", + path=["foobar", 2], + instance="i2"), + ) + tree = exceptions.ErrorTree([e1, e2]) + self.assertEqual(repr(tree), "") + + def test_repr_empty(self): + tree = exceptions.ErrorTree([]) + self.assertEqual(repr(tree), "") + + +class TestErrorInitReprStr(TestCase): + def make_error(self, **kwargs): + defaults = dict( + message="hello", + validator="type", + validator_value="string", + instance=5, + schema={"type": "string"}, + ) + defaults.update(kwargs) + return exceptions.ValidationError(**defaults) + + def assertShows(self, expected, **kwargs): + expected = textwrap.dedent(expected).rstrip("\n") + + error = self.make_error(**kwargs) + message_line, _, rest = str(error).partition("\n") + self.assertEqual(message_line, error.message) + self.assertEqual(rest, expected) + + def test_it_calls_super_and_sets_args(self): + error = self.make_error() + self.assertGreater(len(error.args), 1) + + def test_repr(self): + self.assertEqual( + repr(exceptions.ValidationError(message="Hello!")), + "", + ) + + def test_unset_error(self): + error = exceptions.ValidationError("message") + self.assertEqual(str(error), "message") + + kwargs = { + "validator": "type", + "validator_value": "string", + "instance": 5, + "schema": {"type": "string"}, + } + # Just the message should show if any of the attributes are unset + for attr in kwargs: + k = dict(kwargs) + del k[attr] + error = exceptions.ValidationError("message", **k) + self.assertEqual(str(error), "message") + + def test_empty_paths(self): + self.assertShows( + """ + Failed validating 'type' in schema: + {'type': 'string'} + + On instance: + 5 + """, + path=[], + schema_path=[], + ) + + def test_one_item_paths(self): + self.assertShows( + """ + Failed validating 'type' in schema: + {'type': 'string'} + + On instance[0]: + 5 + """, + path=[0], + schema_path=["items"], + ) + + def test_multiple_item_paths(self): + self.assertShows( + """ + Failed validating 'type' in schema['items'][0]: + {'type': 'string'} + + On instance[0]['a']: + 5 + """, + path=[0, "a"], + schema_path=["items", 0, 1], + ) + + def test_uses_pprint(self): + self.assertShows( + """ + Failed validating 'maxLength' in schema: + {0: 0, + 1: 1, + 2: 2, + 3: 3, + 4: 4, + 5: 5, + 6: 6, + 7: 7, + 8: 8, + 9: 9, + 10: 10, + 11: 11, + 12: 12, + 13: 13, + 14: 14, + 15: 15, + 16: 16, + 17: 17, + 18: 18, + 19: 19} + + On instance: + [0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24] + """, + instance=list(range(25)), + schema=dict(zip(range(20), range(20))), + validator="maxLength", + ) + + def test_does_not_reorder_dicts(self): + self.assertShows( + """ + Failed validating 'type' in schema: + {'do': 3, 'not': 7, 'sort': 37, 'me': 73} + + On instance: + {'here': 73, 'too': 37, 'no': 7, 'sorting': 3} + """, + schema={ + "do": 3, + "not": 7, + "sort": 37, + "me": 73, + }, + instance={ + "here": 73, + "too": 37, + "no": 7, + "sorting": 3, + }, + ) + + def test_str_works_with_instances_having_overriden_eq_operator(self): + """ + Check for #164 which rendered exceptions unusable when a + `ValidationError` involved instances with an `__eq__` method + that returned truthy values. + """ + + class DontEQMeBro: + def __eq__(this, other): # pragma: no cover + self.fail("Don't!") + + def __ne__(this, other): # pragma: no cover + self.fail("Don't!") + + instance = DontEQMeBro() + error = exceptions.ValidationError( + "a message", + validator="foo", + instance=instance, + validator_value="some", + schema="schema", + ) + self.assertIn(repr(instance), str(error)) + + +class TestHashable(TestCase): + def test_hashable(self): + {exceptions.ValidationError("")} + {exceptions.SchemaError("")} + + +class TestJsonPathRendering(TestCase): + def validate_json_path_rendering(self, property_name, expected_path): + error = exceptions.ValidationError( + path=[property_name], + message="1", + validator="foo", + instance="i1", + ) + + rendered_json_path = error.json_path + self.assertEqual(rendered_json_path, expected_path) + + re_parsed_name = jsonpath_ng.parse(rendered_json_path).right.fields[0] + self.assertEqual(re_parsed_name, property_name) + + def test_basic(self): + self.validate_json_path_rendering("x", "$.x") + + def test_empty(self): + self.validate_json_path_rendering("", "$['']") + + def test_number(self): + self.validate_json_path_rendering("1", "$['1']") + + def test_period(self): + self.validate_json_path_rendering(".", "$['.']") + + def test_single_quote(self): + self.validate_json_path_rendering("'", r"$['\'']") + + def test_space(self): + self.validate_json_path_rendering(" ", "$[' ']") + + def test_backslash(self): + self.validate_json_path_rendering("\\", r"$['\\']") + + def test_backslash_single_quote(self): + self.validate_json_path_rendering(r"\'", r"$['\\\'']") + + def test_underscore(self): + self.validate_json_path_rendering("_", r"$['_']") + + def test_double_quote(self): + self.validate_json_path_rendering('"', """$['"']""") + + def test_hyphen(self): + self.validate_json_path_rendering("-", "$['-']") + + def test_json_path_injection(self): + self.validate_json_path_rendering("a[0]", "$['a[0]']") + + def test_open_bracket(self): + self.validate_json_path_rendering("[", "$['[']") diff --git a/lib/python3.12/site-packages/jsonschema/tests/test_format.py b/lib/python3.12/site-packages/jsonschema/tests/test_format.py new file mode 100644 index 0000000000000000000000000000000000000000..d829f9848f51f882d5a3f9413c80e0dbdcdaf292 --- /dev/null +++ b/lib/python3.12/site-packages/jsonschema/tests/test_format.py @@ -0,0 +1,91 @@ +""" +Tests for the parts of jsonschema related to the :kw:`format` keyword. +""" + +from unittest import TestCase + +from jsonschema import FormatChecker, ValidationError +from jsonschema.exceptions import FormatError +from jsonschema.validators import Draft4Validator + +BOOM = ValueError("Boom!") +BANG = ZeroDivisionError("Bang!") + + +def boom(thing): + if thing == "bang": + raise BANG + raise BOOM + + +class TestFormatChecker(TestCase): + def test_it_can_validate_no_formats(self): + checker = FormatChecker(formats=()) + self.assertFalse(checker.checkers) + + def test_it_raises_a_key_error_for_unknown_formats(self): + with self.assertRaises(KeyError): + FormatChecker(formats=["o noes"]) + + def test_it_can_register_cls_checkers(self): + original = dict(FormatChecker.checkers) + self.addCleanup(FormatChecker.checkers.pop, "boom") + with self.assertWarns(DeprecationWarning): + FormatChecker.cls_checks("boom")(boom) + self.assertEqual( + FormatChecker.checkers, + dict(original, boom=(boom, ())), + ) + + def test_it_can_register_checkers(self): + checker = FormatChecker() + checker.checks("boom")(boom) + self.assertEqual( + checker.checkers, + dict(FormatChecker.checkers, boom=(boom, ())), + ) + + def test_it_catches_registered_errors(self): + checker = FormatChecker() + checker.checks("boom", raises=type(BOOM))(boom) + + with self.assertRaises(FormatError) as cm: + checker.check(instance=12, format="boom") + + self.assertIs(cm.exception.cause, BOOM) + self.assertIs(cm.exception.__cause__, BOOM) + self.assertEqual(str(cm.exception), "12 is not a 'boom'") + + # Unregistered errors should not be caught + with self.assertRaises(type(BANG)): + checker.check(instance="bang", format="boom") + + def test_format_error_causes_become_validation_error_causes(self): + checker = FormatChecker() + checker.checks("boom", raises=ValueError)(boom) + validator = Draft4Validator({"format": "boom"}, format_checker=checker) + + with self.assertRaises(ValidationError) as cm: + validator.validate("BOOM") + + self.assertIs(cm.exception.cause, BOOM) + self.assertIs(cm.exception.__cause__, BOOM) + + def test_format_checkers_come_with_defaults(self): + # This is bad :/ but relied upon. + # The docs for quite awhile recommended people do things like + # validate(..., format_checker=FormatChecker()) + # We should change that, but we can't without deprecation... + checker = FormatChecker() + with self.assertRaises(FormatError): + checker.check(instance="not-an-ipv4", format="ipv4") + + def test_repr(self): + checker = FormatChecker(formats=()) + checker.checks("foo")(lambda thing: True) # pragma: no cover + checker.checks("bar")(lambda thing: True) # pragma: no cover + checker.checks("baz")(lambda thing: True) # pragma: no cover + self.assertEqual( + repr(checker), + "", + ) diff --git a/lib/python3.12/site-packages/jsonschema/tests/test_jsonschema_test_suite.py b/lib/python3.12/site-packages/jsonschema/tests/test_jsonschema_test_suite.py new file mode 100644 index 0000000000000000000000000000000000000000..41c982553688ab179dcb95eedc3aae78648e2a4d --- /dev/null +++ b/lib/python3.12/site-packages/jsonschema/tests/test_jsonschema_test_suite.py @@ -0,0 +1,262 @@ +""" +Test runner for the JSON Schema official test suite + +Tests comprehensive correctness of each draft's validator. + +See https://github.com/json-schema-org/JSON-Schema-Test-Suite for details. +""" + + +from jsonschema.tests._suite import Suite +import jsonschema + +SUITE = Suite() +DRAFT3 = SUITE.version(name="draft3") +DRAFT4 = SUITE.version(name="draft4") +DRAFT6 = SUITE.version(name="draft6") +DRAFT7 = SUITE.version(name="draft7") +DRAFT201909 = SUITE.version(name="draft2019-09") +DRAFT202012 = SUITE.version(name="draft2020-12") + + +def skip(message, **kwargs): + def skipper(test): + if all(value == getattr(test, attr) for attr, value in kwargs.items()): + return message + return skipper + + +def ecmascript_regex(test): + if test.subject == "ecmascript-regex": + return "ECMA regex support will be added in #1142." + + +def missing_format(Validator): + def missing_format(test): # pragma: no cover + schema = test.schema + if ( + schema is True + or schema is False + or "format" not in schema + or schema["format"] in Validator.FORMAT_CHECKER.checkers + or test.valid + ): + return + + return f"Format checker {schema['format']!r} not found." + return missing_format + + +def complex_email_validation(test): + if test.subject != "email": + return + + message = "Complex email validation is (intentionally) unsupported." + return skip( + message=message, + description="an invalid domain", + )(test) or skip( + message=message, + description="an invalid IPv4-address-literal", + )(test) or skip( + message=message, + description="dot after local part is not valid", + )(test) or skip( + message=message, + description="dot before local part is not valid", + )(test) or skip( + message=message, + description="two subsequent dots inside local part are not valid", + )(test) + + +def leap_second(test): + message = "Leap seconds are unsupported." + return skip( + message=message, + subject="time", + description="a valid time string with leap second", + )(test) or skip( + message=message, + subject="time", + description="a valid time string with leap second, Zulu", + )(test) or skip( + message=message, + subject="time", + description="a valid time string with leap second with offset", + )(test) or skip( + message=message, + subject="time", + description="valid leap second, positive time-offset", + )(test) or skip( + message=message, + subject="time", + description="valid leap second, negative time-offset", + )(test) or skip( + message=message, + subject="time", + description="valid leap second, large positive time-offset", + )(test) or skip( + message=message, + subject="time", + description="valid leap second, large negative time-offset", + )(test) or skip( + message=message, + subject="time", + description="valid leap second, zero time-offset", + )(test) or skip( + message=message, + subject="date-time", + description="a valid date-time with a leap second, UTC", + )(test) or skip( + message=message, + subject="date-time", + description="a valid date-time with a leap second, with minus offset", + )(test) + + +TestDraft3 = DRAFT3.to_unittest_testcase( + DRAFT3.cases(), + DRAFT3.format_cases(), + DRAFT3.optional_cases_of(name="bignum"), + DRAFT3.optional_cases_of(name="non-bmp-regex"), + DRAFT3.optional_cases_of(name="zeroTerminatedFloats"), + Validator=jsonschema.Draft3Validator, + format_checker=jsonschema.Draft3Validator.FORMAT_CHECKER, + skip=lambda test: ( + ecmascript_regex(test) + or missing_format(jsonschema.Draft3Validator)(test) + or complex_email_validation(test) + ), +) + + +TestDraft4 = DRAFT4.to_unittest_testcase( + DRAFT4.cases(), + DRAFT4.format_cases(), + DRAFT4.optional_cases_of(name="bignum"), + DRAFT4.optional_cases_of(name="float-overflow"), + DRAFT4.optional_cases_of(name="id"), + DRAFT4.optional_cases_of(name="non-bmp-regex"), + DRAFT4.optional_cases_of(name="zeroTerminatedFloats"), + Validator=jsonschema.Draft4Validator, + format_checker=jsonschema.Draft4Validator.FORMAT_CHECKER, + skip=lambda test: ( + ecmascript_regex(test) + or leap_second(test) + or missing_format(jsonschema.Draft4Validator)(test) + or complex_email_validation(test) + ), +) + + +TestDraft6 = DRAFT6.to_unittest_testcase( + DRAFT6.cases(), + DRAFT6.format_cases(), + DRAFT6.optional_cases_of(name="bignum"), + DRAFT6.optional_cases_of(name="float-overflow"), + DRAFT6.optional_cases_of(name="id"), + DRAFT6.optional_cases_of(name="non-bmp-regex"), + Validator=jsonschema.Draft6Validator, + format_checker=jsonschema.Draft6Validator.FORMAT_CHECKER, + skip=lambda test: ( + ecmascript_regex(test) + or leap_second(test) + or missing_format(jsonschema.Draft6Validator)(test) + or complex_email_validation(test) + ), +) + + +TestDraft7 = DRAFT7.to_unittest_testcase( + DRAFT7.cases(), + DRAFT7.format_cases(), + DRAFT7.optional_cases_of(name="bignum"), + DRAFT7.optional_cases_of(name="cross-draft"), + DRAFT7.optional_cases_of(name="float-overflow"), + DRAFT6.optional_cases_of(name="id"), + DRAFT7.optional_cases_of(name="non-bmp-regex"), + DRAFT7.optional_cases_of(name="unknownKeyword"), + Validator=jsonschema.Draft7Validator, + format_checker=jsonschema.Draft7Validator.FORMAT_CHECKER, + skip=lambda test: ( + ecmascript_regex(test) + or leap_second(test) + or missing_format(jsonschema.Draft7Validator)(test) + or complex_email_validation(test) + ), +) + + +TestDraft201909 = DRAFT201909.to_unittest_testcase( + DRAFT201909.cases(), + DRAFT201909.optional_cases_of(name="anchor"), + DRAFT201909.optional_cases_of(name="bignum"), + DRAFT201909.optional_cases_of(name="cross-draft"), + DRAFT201909.optional_cases_of(name="float-overflow"), + DRAFT201909.optional_cases_of(name="id"), + DRAFT201909.optional_cases_of(name="no-schema"), + DRAFT201909.optional_cases_of(name="non-bmp-regex"), + DRAFT201909.optional_cases_of(name="refOfUnknownKeyword"), + DRAFT201909.optional_cases_of(name="unknownKeyword"), + Validator=jsonschema.Draft201909Validator, + skip=skip( + message="Vocabulary support is still in-progress.", + subject="vocabulary", + description=( + "no validation: invalid number, but it still validates" + ), + ), +) + + +TestDraft201909Format = DRAFT201909.to_unittest_testcase( + DRAFT201909.format_cases(), + name="TestDraft201909Format", + Validator=jsonschema.Draft201909Validator, + format_checker=jsonschema.Draft201909Validator.FORMAT_CHECKER, + skip=lambda test: ( + complex_email_validation(test) + or ecmascript_regex(test) + or leap_second(test) + or missing_format(jsonschema.Draft201909Validator)(test) + or complex_email_validation(test) + ), +) + + +TestDraft202012 = DRAFT202012.to_unittest_testcase( + DRAFT202012.cases(), + DRAFT201909.optional_cases_of(name="anchor"), + DRAFT202012.optional_cases_of(name="bignum"), + DRAFT202012.optional_cases_of(name="cross-draft"), + DRAFT202012.optional_cases_of(name="float-overflow"), + DRAFT202012.optional_cases_of(name="id"), + DRAFT202012.optional_cases_of(name="no-schema"), + DRAFT202012.optional_cases_of(name="non-bmp-regex"), + DRAFT202012.optional_cases_of(name="refOfUnknownKeyword"), + DRAFT202012.optional_cases_of(name="unknownKeyword"), + Validator=jsonschema.Draft202012Validator, + skip=skip( + message="Vocabulary support is still in-progress.", + subject="vocabulary", + description=( + "no validation: invalid number, but it still validates" + ), + ), +) + + +TestDraft202012Format = DRAFT202012.to_unittest_testcase( + DRAFT202012.format_cases(), + name="TestDraft202012Format", + Validator=jsonschema.Draft202012Validator, + format_checker=jsonschema.Draft202012Validator.FORMAT_CHECKER, + skip=lambda test: ( + complex_email_validation(test) + or ecmascript_regex(test) + or leap_second(test) + or missing_format(jsonschema.Draft202012Validator)(test) + or complex_email_validation(test) + ), +) diff --git a/lib/python3.12/site-packages/jsonschema/tests/test_types.py b/lib/python3.12/site-packages/jsonschema/tests/test_types.py new file mode 100644 index 0000000000000000000000000000000000000000..bd97b180029ae05d7f3b22f1048f9adcb769f36f --- /dev/null +++ b/lib/python3.12/site-packages/jsonschema/tests/test_types.py @@ -0,0 +1,221 @@ +""" +Tests for the `TypeChecker`-based type interface. + +The actual correctness of the type checking is handled in +`test_jsonschema_test_suite`; these tests check that TypeChecker +functions correctly at a more granular level. +""" +from collections import namedtuple +from unittest import TestCase + +from jsonschema import ValidationError, _keywords +from jsonschema._types import TypeChecker +from jsonschema.exceptions import UndefinedTypeCheck, UnknownType +from jsonschema.validators import Draft202012Validator, extend + + +def equals_2(checker, instance): + return instance == 2 + + +def is_namedtuple(instance): + return isinstance(instance, tuple) and getattr(instance, "_fields", None) + + +def is_object_or_named_tuple(checker, instance): + if Draft202012Validator.TYPE_CHECKER.is_type(instance, "object"): + return True + return is_namedtuple(instance) + + +class TestTypeChecker(TestCase): + def test_is_type(self): + checker = TypeChecker({"two": equals_2}) + self.assertEqual( + ( + checker.is_type(instance=2, type="two"), + checker.is_type(instance="bar", type="two"), + ), + (True, False), + ) + + def test_is_unknown_type(self): + with self.assertRaises(UndefinedTypeCheck) as e: + TypeChecker().is_type(4, "foobar") + self.assertIn( + "'foobar' is unknown to this type checker", + str(e.exception), + ) + self.assertTrue( + e.exception.__suppress_context__, + msg="Expected the internal KeyError to be hidden.", + ) + + def test_checks_can_be_added_at_init(self): + checker = TypeChecker({"two": equals_2}) + self.assertEqual(checker, TypeChecker().redefine("two", equals_2)) + + def test_redefine_existing_type(self): + self.assertEqual( + TypeChecker().redefine("two", object()).redefine("two", equals_2), + TypeChecker().redefine("two", equals_2), + ) + + def test_remove(self): + self.assertEqual( + TypeChecker({"two": equals_2}).remove("two"), + TypeChecker(), + ) + + def test_remove_unknown_type(self): + with self.assertRaises(UndefinedTypeCheck) as context: + TypeChecker().remove("foobar") + self.assertIn("foobar", str(context.exception)) + + def test_redefine_many(self): + self.assertEqual( + TypeChecker().redefine_many({"foo": int, "bar": str}), + TypeChecker().redefine("foo", int).redefine("bar", str), + ) + + def test_remove_multiple(self): + self.assertEqual( + TypeChecker({"foo": int, "bar": str}).remove("foo", "bar"), + TypeChecker(), + ) + + def test_type_check_can_raise_key_error(self): + """ + Make sure no one writes: + + try: + self._type_checkers[type](...) + except KeyError: + + ignoring the fact that the function itself can raise that. + """ + + error = KeyError("Stuff") + + def raises_keyerror(checker, instance): + raise error + + with self.assertRaises(KeyError) as context: + TypeChecker({"foo": raises_keyerror}).is_type(4, "foo") + + self.assertIs(context.exception, error) + + def test_repr(self): + checker = TypeChecker({"foo": is_namedtuple, "bar": is_namedtuple}) + self.assertEqual(repr(checker), "") + + +class TestCustomTypes(TestCase): + def test_simple_type_can_be_extended(self): + def int_or_str_int(checker, instance): + if not isinstance(instance, (int, str)): + return False + try: + int(instance) + except ValueError: + return False + return True + + CustomValidator = extend( + Draft202012Validator, + type_checker=Draft202012Validator.TYPE_CHECKER.redefine( + "integer", int_or_str_int, + ), + ) + validator = CustomValidator({"type": "integer"}) + + validator.validate(4) + validator.validate("4") + + with self.assertRaises(ValidationError): + validator.validate(4.4) + + with self.assertRaises(ValidationError): + validator.validate("foo") + + def test_object_can_be_extended(self): + schema = {"type": "object"} + + Point = namedtuple("Point", ["x", "y"]) + + type_checker = Draft202012Validator.TYPE_CHECKER.redefine( + "object", is_object_or_named_tuple, + ) + + CustomValidator = extend( + Draft202012Validator, + type_checker=type_checker, + ) + validator = CustomValidator(schema) + + validator.validate(Point(x=4, y=5)) + + def test_object_extensions_require_custom_validators(self): + schema = {"type": "object", "required": ["x"]} + + type_checker = Draft202012Validator.TYPE_CHECKER.redefine( + "object", is_object_or_named_tuple, + ) + + CustomValidator = extend( + Draft202012Validator, + type_checker=type_checker, + ) + validator = CustomValidator(schema) + + Point = namedtuple("Point", ["x", "y"]) + # Cannot handle required + with self.assertRaises(ValidationError): + validator.validate(Point(x=4, y=5)) + + def test_object_extensions_can_handle_custom_validators(self): + schema = { + "type": "object", + "required": ["x"], + "properties": {"x": {"type": "integer"}}, + } + + type_checker = Draft202012Validator.TYPE_CHECKER.redefine( + "object", is_object_or_named_tuple, + ) + + def coerce_named_tuple(fn): + def coerced(validator, value, instance, schema): + if is_namedtuple(instance): + instance = instance._asdict() + return fn(validator, value, instance, schema) + return coerced + + required = coerce_named_tuple(_keywords.required) + properties = coerce_named_tuple(_keywords.properties) + + CustomValidator = extend( + Draft202012Validator, + type_checker=type_checker, + validators={"required": required, "properties": properties}, + ) + + validator = CustomValidator(schema) + + Point = namedtuple("Point", ["x", "y"]) + # Can now process required and properties + validator.validate(Point(x=4, y=5)) + + with self.assertRaises(ValidationError): + validator.validate(Point(x="not an integer", y=5)) + + # As well as still handle objects. + validator.validate({"x": 4, "y": 5}) + + with self.assertRaises(ValidationError): + validator.validate({"x": "not an integer", "y": 5}) + + def test_unknown_type(self): + with self.assertRaises(UnknownType) as e: + Draft202012Validator({}).is_type(12, "some unknown type") + self.assertIn("'some unknown type'", str(e.exception)) diff --git a/lib/python3.12/site-packages/jsonschema/tests/test_utils.py b/lib/python3.12/site-packages/jsonschema/tests/test_utils.py new file mode 100644 index 0000000000000000000000000000000000000000..d9764b0f9e92edb38c19e1fc43b248a20186ef6b --- /dev/null +++ b/lib/python3.12/site-packages/jsonschema/tests/test_utils.py @@ -0,0 +1,138 @@ +from math import nan +from unittest import TestCase + +from jsonschema._utils import equal + + +class TestEqual(TestCase): + def test_none(self): + self.assertTrue(equal(None, None)) + + def test_nan(self): + self.assertTrue(equal(nan, nan)) + + +class TestDictEqual(TestCase): + def test_equal_dictionaries(self): + dict_1 = {"a": "b", "c": "d"} + dict_2 = {"c": "d", "a": "b"} + self.assertTrue(equal(dict_1, dict_2)) + + def test_equal_dictionaries_with_nan(self): + dict_1 = {"a": nan, "c": "d"} + dict_2 = {"c": "d", "a": nan} + self.assertTrue(equal(dict_1, dict_2)) + + def test_missing_key(self): + dict_1 = {"a": "b", "c": "d"} + dict_2 = {"c": "d", "x": "b"} + self.assertFalse(equal(dict_1, dict_2)) + + def test_additional_key(self): + dict_1 = {"a": "b", "c": "d"} + dict_2 = {"c": "d", "a": "b", "x": "x"} + self.assertFalse(equal(dict_1, dict_2)) + + def test_missing_value(self): + dict_1 = {"a": "b", "c": "d"} + dict_2 = {"c": "d", "a": "x"} + self.assertFalse(equal(dict_1, dict_2)) + + def test_empty_dictionaries(self): + dict_1 = {} + dict_2 = {} + self.assertTrue(equal(dict_1, dict_2)) + + def test_one_none(self): + dict_1 = None + dict_2 = {"a": "b", "c": "d"} + self.assertFalse(equal(dict_1, dict_2)) + + def test_same_item(self): + dict_1 = {"a": "b", "c": "d"} + self.assertTrue(equal(dict_1, dict_1)) + + def test_nested_equal(self): + dict_1 = {"a": {"a": "b", "c": "d"}, "c": "d"} + dict_2 = {"c": "d", "a": {"a": "b", "c": "d"}} + self.assertTrue(equal(dict_1, dict_2)) + + def test_nested_dict_unequal(self): + dict_1 = {"a": {"a": "b", "c": "d"}, "c": "d"} + dict_2 = {"c": "d", "a": {"a": "b", "c": "x"}} + self.assertFalse(equal(dict_1, dict_2)) + + def test_mixed_nested_equal(self): + dict_1 = {"a": ["a", "b", "c", "d"], "c": "d"} + dict_2 = {"c": "d", "a": ["a", "b", "c", "d"]} + self.assertTrue(equal(dict_1, dict_2)) + + def test_nested_list_unequal(self): + dict_1 = {"a": ["a", "b", "c", "d"], "c": "d"} + dict_2 = {"c": "d", "a": ["b", "c", "d", "a"]} + self.assertFalse(equal(dict_1, dict_2)) + + +class TestListEqual(TestCase): + def test_equal_lists(self): + list_1 = ["a", "b", "c"] + list_2 = ["a", "b", "c"] + self.assertTrue(equal(list_1, list_2)) + + def test_equal_lists_with_nan(self): + list_1 = ["a", nan, "c"] + list_2 = ["a", nan, "c"] + self.assertTrue(equal(list_1, list_2)) + + def test_unsorted_lists(self): + list_1 = ["a", "b", "c"] + list_2 = ["b", "b", "a"] + self.assertFalse(equal(list_1, list_2)) + + def test_first_list_larger(self): + list_1 = ["a", "b", "c"] + list_2 = ["a", "b"] + self.assertFalse(equal(list_1, list_2)) + + def test_second_list_larger(self): + list_1 = ["a", "b"] + list_2 = ["a", "b", "c"] + self.assertFalse(equal(list_1, list_2)) + + def test_list_with_none_unequal(self): + list_1 = ["a", "b", None] + list_2 = ["a", "b", "c"] + self.assertFalse(equal(list_1, list_2)) + + list_1 = ["a", "b", None] + list_2 = [None, "b", "c"] + self.assertFalse(equal(list_1, list_2)) + + def test_list_with_none_equal(self): + list_1 = ["a", None, "c"] + list_2 = ["a", None, "c"] + self.assertTrue(equal(list_1, list_2)) + + def test_empty_list(self): + list_1 = [] + list_2 = [] + self.assertTrue(equal(list_1, list_2)) + + def test_one_none(self): + list_1 = None + list_2 = [] + self.assertFalse(equal(list_1, list_2)) + + def test_same_list(self): + list_1 = ["a", "b", "c"] + self.assertTrue(equal(list_1, list_1)) + + def test_equal_nested_lists(self): + list_1 = ["a", ["b", "c"], "d"] + list_2 = ["a", ["b", "c"], "d"] + self.assertTrue(equal(list_1, list_2)) + + def test_unequal_nested_lists(self): + list_1 = ["a", ["b", "c"], "d"] + list_2 = ["a", [], "c"] + self.assertFalse(equal(list_1, list_2)) diff --git a/lib/python3.12/site-packages/jsonschema/tests/test_validators.py b/lib/python3.12/site-packages/jsonschema/tests/test_validators.py new file mode 100644 index 0000000000000000000000000000000000000000..d419ddf8e354333f5794cb761348517595167f28 --- /dev/null +++ b/lib/python3.12/site-packages/jsonschema/tests/test_validators.py @@ -0,0 +1,2573 @@ +from __future__ import annotations + +from collections import deque, namedtuple +from contextlib import contextmanager +from decimal import Decimal +from io import BytesIO +from typing import Any +from unittest import TestCase, mock +from urllib.request import pathname2url +import json +import os +import sys +import tempfile +import warnings + +from attrs import define, field +from referencing.jsonschema import DRAFT202012 +import referencing.exceptions + +from jsonschema import ( + FormatChecker, + TypeChecker, + exceptions, + protocols, + validators, +) + + +def fail(validator, errors, instance, schema): + for each in errors: + each.setdefault("message", "You told me to fail!") + yield exceptions.ValidationError(**each) + + +class TestCreateAndExtend(TestCase): + def setUp(self): + self.addCleanup( + self.assertEqual, + validators._META_SCHEMAS, + dict(validators._META_SCHEMAS), + ) + self.addCleanup( + self.assertEqual, + validators._VALIDATORS, + dict(validators._VALIDATORS), + ) + + self.meta_schema = {"$id": "some://meta/schema"} + self.validators = {"fail": fail} + self.type_checker = TypeChecker() + self.Validator = validators.create( + meta_schema=self.meta_schema, + validators=self.validators, + type_checker=self.type_checker, + ) + + def test_attrs(self): + self.assertEqual( + ( + self.Validator.VALIDATORS, + self.Validator.META_SCHEMA, + self.Validator.TYPE_CHECKER, + ), ( + self.validators, + self.meta_schema, + self.type_checker, + ), + ) + + def test_init(self): + schema = {"fail": []} + self.assertEqual(self.Validator(schema).schema, schema) + + def test_iter_errors_successful(self): + schema = {"fail": []} + validator = self.Validator(schema) + + errors = list(validator.iter_errors("hello")) + self.assertEqual(errors, []) + + def test_iter_errors_one_error(self): + schema = {"fail": [{"message": "Whoops!"}]} + validator = self.Validator(schema) + + expected_error = exceptions.ValidationError( + "Whoops!", + instance="goodbye", + schema=schema, + validator="fail", + validator_value=[{"message": "Whoops!"}], + schema_path=deque(["fail"]), + ) + + errors = list(validator.iter_errors("goodbye")) + self.assertEqual(len(errors), 1) + self.assertEqual(errors[0]._contents(), expected_error._contents()) + + def test_iter_errors_multiple_errors(self): + schema = { + "fail": [ + {"message": "First"}, + {"message": "Second!", "validator": "asdf"}, + {"message": "Third"}, + ], + } + validator = self.Validator(schema) + + errors = list(validator.iter_errors("goodbye")) + self.assertEqual(len(errors), 3) + + def test_if_a_version_is_provided_it_is_registered(self): + Validator = validators.create( + meta_schema={"$id": "something"}, + version="my version", + ) + self.addCleanup(validators._META_SCHEMAS.pop, "something") + self.addCleanup(validators._VALIDATORS.pop, "my version") + self.assertEqual(Validator.__name__, "MyVersionValidator") + self.assertEqual(Validator.__qualname__, "MyVersionValidator") + + def test_repr(self): + Validator = validators.create( + meta_schema={"$id": "something"}, + version="my version", + ) + self.addCleanup(validators._META_SCHEMAS.pop, "something") + self.addCleanup(validators._VALIDATORS.pop, "my version") + self.assertEqual( + repr(Validator({})), + "MyVersionValidator(schema={}, format_checker=None)", + ) + + def test_long_repr(self): + Validator = validators.create( + meta_schema={"$id": "something"}, + version="my version", + ) + self.addCleanup(validators._META_SCHEMAS.pop, "something") + self.addCleanup(validators._VALIDATORS.pop, "my version") + self.assertEqual( + repr(Validator({"a": list(range(1000))})), ( + "MyVersionValidator(schema={'a': [0, 1, 2, 3, 4, 5, ...]}, " + "format_checker=None)" + ), + ) + + def test_repr_no_version(self): + Validator = validators.create(meta_schema={}) + self.assertEqual( + repr(Validator({})), + "Validator(schema={}, format_checker=None)", + ) + + def test_dashes_are_stripped_from_validator_names(self): + Validator = validators.create( + meta_schema={"$id": "something"}, + version="foo-bar", + ) + self.addCleanup(validators._META_SCHEMAS.pop, "something") + self.addCleanup(validators._VALIDATORS.pop, "foo-bar") + self.assertEqual(Validator.__qualname__, "FooBarValidator") + + def test_if_a_version_is_not_provided_it_is_not_registered(self): + original = dict(validators._META_SCHEMAS) + validators.create(meta_schema={"id": "id"}) + self.assertEqual(validators._META_SCHEMAS, original) + + def test_validates_registers_meta_schema_id(self): + meta_schema_key = "meta schema id" + my_meta_schema = {"id": meta_schema_key} + + validators.create( + meta_schema=my_meta_schema, + version="my version", + id_of=lambda s: s.get("id", ""), + ) + self.addCleanup(validators._META_SCHEMAS.pop, meta_schema_key) + self.addCleanup(validators._VALIDATORS.pop, "my version") + + self.assertIn(meta_schema_key, validators._META_SCHEMAS) + + def test_validates_registers_meta_schema_draft6_id(self): + meta_schema_key = "meta schema $id" + my_meta_schema = {"$id": meta_schema_key} + + validators.create( + meta_schema=my_meta_schema, + version="my version", + ) + self.addCleanup(validators._META_SCHEMAS.pop, meta_schema_key) + self.addCleanup(validators._VALIDATORS.pop, "my version") + + self.assertIn(meta_schema_key, validators._META_SCHEMAS) + + def test_create_default_types(self): + Validator = validators.create(meta_schema={}, validators=()) + self.assertTrue( + all( + Validator({}).is_type(instance=instance, type=type) + for type, instance in [ + ("array", []), + ("boolean", True), + ("integer", 12), + ("null", None), + ("number", 12.0), + ("object", {}), + ("string", "foo"), + ] + ), + ) + + def test_check_schema_with_different_metaschema(self): + """ + One can create a validator class whose metaschema uses a different + dialect than itself. + """ + + NoEmptySchemasValidator = validators.create( + meta_schema={ + "$schema": validators.Draft202012Validator.META_SCHEMA["$id"], + "not": {"const": {}}, + }, + ) + NoEmptySchemasValidator.check_schema({"foo": "bar"}) + + with self.assertRaises(exceptions.SchemaError): + NoEmptySchemasValidator.check_schema({}) + + NoEmptySchemasValidator({"foo": "bar"}).validate("foo") + + def test_check_schema_with_different_metaschema_defaults_to_self(self): + """ + A validator whose metaschema doesn't declare $schema defaults to its + own validation behavior, not the latest "normal" specification. + """ + + NoEmptySchemasValidator = validators.create( + meta_schema={"fail": [{"message": "Meta schema whoops!"}]}, + validators={"fail": fail}, + ) + with self.assertRaises(exceptions.SchemaError): + NoEmptySchemasValidator.check_schema({}) + + def test_extend(self): + original = dict(self.Validator.VALIDATORS) + new = object() + + Extended = validators.extend( + self.Validator, + validators={"new": new}, + ) + self.assertEqual( + ( + Extended.VALIDATORS, + Extended.META_SCHEMA, + Extended.TYPE_CHECKER, + self.Validator.VALIDATORS, + ), ( + dict(original, new=new), + self.Validator.META_SCHEMA, + self.Validator.TYPE_CHECKER, + original, + ), + ) + + def test_extend_idof(self): + """ + Extending a validator preserves its notion of schema IDs. + """ + def id_of(schema): + return schema.get("__test__", self.Validator.ID_OF(schema)) + correct_id = "the://correct/id/" + meta_schema = { + "$id": "the://wrong/id/", + "__test__": correct_id, + } + Original = validators.create( + meta_schema=meta_schema, + validators=self.validators, + type_checker=self.type_checker, + id_of=id_of, + ) + self.assertEqual(Original.ID_OF(Original.META_SCHEMA), correct_id) + + Derived = validators.extend(Original) + self.assertEqual(Derived.ID_OF(Derived.META_SCHEMA), correct_id) + + def test_extend_applicable_validators(self): + """ + Extending a validator preserves its notion of applicable validators. + """ + + schema = { + "$defs": {"test": {"type": "number"}}, + "$ref": "#/$defs/test", + "maximum": 1, + } + + draft4 = validators.Draft4Validator(schema) + self.assertTrue(draft4.is_valid(37)) # as $ref ignores siblings + + Derived = validators.extend(validators.Draft4Validator) + self.assertTrue(Derived(schema).is_valid(37)) + + +class TestValidationErrorMessages(TestCase): + def message_for(self, instance, schema, *args, **kwargs): + cls = kwargs.pop("cls", validators._LATEST_VERSION) + cls.check_schema(schema) + validator = cls(schema, *args, **kwargs) + errors = list(validator.iter_errors(instance)) + self.assertTrue(errors, msg=f"No errors were raised for {instance!r}") + self.assertEqual( + len(errors), + 1, + msg=f"Expected exactly one error, found {errors!r}", + ) + return errors[0].message + + def test_single_type_failure(self): + message = self.message_for(instance=1, schema={"type": "string"}) + self.assertEqual(message, "1 is not of type 'string'") + + def test_single_type_list_failure(self): + message = self.message_for(instance=1, schema={"type": ["string"]}) + self.assertEqual(message, "1 is not of type 'string'") + + def test_multiple_type_failure(self): + types = "string", "object" + message = self.message_for(instance=1, schema={"type": list(types)}) + self.assertEqual(message, "1 is not of type 'string', 'object'") + + def test_object_with_named_type_failure(self): + schema = {"type": [{"name": "Foo", "minimum": 3}]} + message = self.message_for( + instance=1, + schema=schema, + cls=validators.Draft3Validator, + ) + self.assertEqual(message, "1 is not of type 'Foo'") + + def test_minimum(self): + message = self.message_for(instance=1, schema={"minimum": 2}) + self.assertEqual(message, "1 is less than the minimum of 2") + + def test_maximum(self): + message = self.message_for(instance=1, schema={"maximum": 0}) + self.assertEqual(message, "1 is greater than the maximum of 0") + + def test_dependencies_single_element(self): + depend, on = "bar", "foo" + schema = {"dependencies": {depend: on}} + message = self.message_for( + instance={"bar": 2}, + schema=schema, + cls=validators.Draft3Validator, + ) + self.assertEqual(message, "'foo' is a dependency of 'bar'") + + def test_object_without_title_type_failure_draft3(self): + type = {"type": [{"minimum": 3}]} + message = self.message_for( + instance=1, + schema={"type": [type]}, + cls=validators.Draft3Validator, + ) + self.assertEqual( + message, + "1 is not of type {'type': [{'minimum': 3}]}", + ) + + def test_dependencies_list_draft3(self): + depend, on = "bar", "foo" + schema = {"dependencies": {depend: [on]}} + message = self.message_for( + instance={"bar": 2}, + schema=schema, + cls=validators.Draft3Validator, + ) + self.assertEqual(message, "'foo' is a dependency of 'bar'") + + def test_dependencies_list_draft7(self): + depend, on = "bar", "foo" + schema = {"dependencies": {depend: [on]}} + message = self.message_for( + instance={"bar": 2}, + schema=schema, + cls=validators.Draft7Validator, + ) + self.assertEqual(message, "'foo' is a dependency of 'bar'") + + def test_additionalItems_single_failure(self): + message = self.message_for( + instance=[2], + schema={"items": [], "additionalItems": False}, + cls=validators.Draft3Validator, + ) + self.assertIn("(2 was unexpected)", message) + + def test_additionalItems_multiple_failures(self): + message = self.message_for( + instance=[1, 2, 3], + schema={"items": [], "additionalItems": False}, + cls=validators.Draft3Validator, + ) + self.assertIn("(1, 2, 3 were unexpected)", message) + + def test_additionalProperties_single_failure(self): + additional = "foo" + schema = {"additionalProperties": False} + message = self.message_for(instance={additional: 2}, schema=schema) + self.assertIn("('foo' was unexpected)", message) + + def test_additionalProperties_multiple_failures(self): + schema = {"additionalProperties": False} + message = self.message_for( + instance=dict.fromkeys(["foo", "bar"]), + schema=schema, + ) + + self.assertIn(repr("foo"), message) + self.assertIn(repr("bar"), message) + self.assertIn("were unexpected)", message) + + def test_const(self): + schema = {"const": 12} + message = self.message_for( + instance={"foo": "bar"}, + schema=schema, + ) + self.assertIn("12 was expected", message) + + def test_contains_draft_6(self): + schema = {"contains": {"const": 12}} + message = self.message_for( + instance=[2, {}, []], + schema=schema, + cls=validators.Draft6Validator, + ) + self.assertEqual( + message, + "None of [2, {}, []] are valid under the given schema", + ) + + def test_invalid_format_default_message(self): + checker = FormatChecker(formats=()) + checker.checks("thing")(lambda value: False) + + schema = {"format": "thing"} + message = self.message_for( + instance="bla", + schema=schema, + format_checker=checker, + ) + + self.assertIn(repr("bla"), message) + self.assertIn(repr("thing"), message) + self.assertIn("is not a", message) + + def test_additionalProperties_false_patternProperties(self): + schema = {"type": "object", + "additionalProperties": False, + "patternProperties": { + "^abc$": {"type": "string"}, + "^def$": {"type": "string"}, + }} + message = self.message_for( + instance={"zebra": 123}, + schema=schema, + cls=validators.Draft4Validator, + ) + self.assertEqual( + message, + "{} does not match any of the regexes: {}, {}".format( + repr("zebra"), repr("^abc$"), repr("^def$"), + ), + ) + message = self.message_for( + instance={"zebra": 123, "fish": 456}, + schema=schema, + cls=validators.Draft4Validator, + ) + self.assertEqual( + message, + "{}, {} do not match any of the regexes: {}, {}".format( + repr("fish"), repr("zebra"), repr("^abc$"), repr("^def$"), + ), + ) + + def test_False_schema(self): + message = self.message_for( + instance="something", + schema=False, + ) + self.assertEqual(message, "False schema does not allow 'something'") + + def test_multipleOf(self): + message = self.message_for( + instance=3, + schema={"multipleOf": 2}, + ) + self.assertEqual(message, "3 is not a multiple of 2") + + def test_minItems(self): + message = self.message_for(instance=[], schema={"minItems": 2}) + self.assertEqual(message, "[] is too short") + + def test_maxItems(self): + message = self.message_for(instance=[1, 2, 3], schema={"maxItems": 2}) + self.assertEqual(message, "[1, 2, 3] is too long") + + def test_minItems_1(self): + message = self.message_for(instance=[], schema={"minItems": 1}) + self.assertEqual(message, "[] should be non-empty") + + def test_maxItems_0(self): + message = self.message_for(instance=[1, 2, 3], schema={"maxItems": 0}) + self.assertEqual(message, "[1, 2, 3] is expected to be empty") + + def test_minLength(self): + message = self.message_for( + instance="", + schema={"minLength": 2}, + ) + self.assertEqual(message, "'' is too short") + + def test_maxLength(self): + message = self.message_for( + instance="abc", + schema={"maxLength": 2}, + ) + self.assertEqual(message, "'abc' is too long") + + def test_minLength_1(self): + message = self.message_for(instance="", schema={"minLength": 1}) + self.assertEqual(message, "'' should be non-empty") + + def test_maxLength_0(self): + message = self.message_for(instance="abc", schema={"maxLength": 0}) + self.assertEqual(message, "'abc' is expected to be empty") + + def test_minProperties(self): + message = self.message_for(instance={}, schema={"minProperties": 2}) + self.assertEqual(message, "{} does not have enough properties") + + def test_maxProperties(self): + message = self.message_for( + instance={"a": {}, "b": {}, "c": {}}, + schema={"maxProperties": 2}, + ) + self.assertEqual( + message, + "{'a': {}, 'b': {}, 'c': {}} has too many properties", + ) + + def test_minProperties_1(self): + message = self.message_for(instance={}, schema={"minProperties": 1}) + self.assertEqual(message, "{} should be non-empty") + + def test_maxProperties_0(self): + message = self.message_for( + instance={1: 2}, + schema={"maxProperties": 0}, + ) + self.assertEqual(message, "{1: 2} is expected to be empty") + + def test_prefixItems_with_items(self): + message = self.message_for( + instance=[1, 2, "foo"], + schema={"items": False, "prefixItems": [{}, {}]}, + ) + self.assertEqual( + message, + "Expected at most 2 items but found 1 extra: 'foo'", + ) + + def test_prefixItems_with_multiple_extra_items(self): + message = self.message_for( + instance=[1, 2, "foo", 5], + schema={"items": False, "prefixItems": [{}, {}]}, + ) + self.assertEqual( + message, + "Expected at most 2 items but found 2 extra: ['foo', 5]", + ) + + def test_pattern(self): + message = self.message_for( + instance="bbb", + schema={"pattern": "^a*$"}, + ) + self.assertEqual(message, "'bbb' does not match '^a*$'") + + def test_does_not_contain(self): + message = self.message_for( + instance=[], + schema={"contains": {"type": "string"}}, + ) + self.assertEqual( + message, + "[] does not contain items matching the given schema", + ) + + def test_contains_too_few(self): + message = self.message_for( + instance=["foo", 1], + schema={"contains": {"type": "string"}, "minContains": 2}, + ) + self.assertEqual( + message, + "Too few items match the given schema " + "(expected at least 2 but only 1 matched)", + ) + + def test_contains_too_few_both_constrained(self): + message = self.message_for( + instance=["foo", 1], + schema={ + "contains": {"type": "string"}, + "minContains": 2, + "maxContains": 4, + }, + ) + self.assertEqual( + message, + "Too few items match the given schema (expected at least 2 but " + "only 1 matched)", + ) + + def test_contains_too_many(self): + message = self.message_for( + instance=["foo", "bar", "baz"], + schema={"contains": {"type": "string"}, "maxContains": 2}, + ) + self.assertEqual( + message, + "Too many items match the given schema (expected at most 2)", + ) + + def test_contains_too_many_both_constrained(self): + message = self.message_for( + instance=["foo"] * 5, + schema={ + "contains": {"type": "string"}, + "minContains": 2, + "maxContains": 4, + }, + ) + self.assertEqual( + message, + "Too many items match the given schema (expected at most 4)", + ) + + def test_exclusiveMinimum(self): + message = self.message_for( + instance=3, + schema={"exclusiveMinimum": 5}, + ) + self.assertEqual( + message, + "3 is less than or equal to the minimum of 5", + ) + + def test_exclusiveMaximum(self): + message = self.message_for(instance=3, schema={"exclusiveMaximum": 2}) + self.assertEqual( + message, + "3 is greater than or equal to the maximum of 2", + ) + + def test_required(self): + message = self.message_for(instance={}, schema={"required": ["foo"]}) + self.assertEqual(message, "'foo' is a required property") + + def test_dependentRequired(self): + message = self.message_for( + instance={"foo": {}}, + schema={"dependentRequired": {"foo": ["bar"]}}, + ) + self.assertEqual(message, "'bar' is a dependency of 'foo'") + + def test_oneOf_matches_none(self): + message = self.message_for(instance={}, schema={"oneOf": [False]}) + self.assertEqual( + message, + "{} is not valid under any of the given schemas", + ) + + def test_oneOf_matches_too_many(self): + message = self.message_for(instance={}, schema={"oneOf": [True, True]}) + self.assertEqual(message, "{} is valid under each of True, True") + + def test_unevaluated_items(self): + schema = {"type": "array", "unevaluatedItems": False} + message = self.message_for(instance=["foo", "bar"], schema=schema) + self.assertIn( + message, + "Unevaluated items are not allowed ('foo', 'bar' were unexpected)", + ) + + def test_unevaluated_items_on_invalid_type(self): + schema = {"type": "array", "unevaluatedItems": False} + message = self.message_for(instance="foo", schema=schema) + self.assertEqual(message, "'foo' is not of type 'array'") + + def test_unevaluated_properties_invalid_against_subschema(self): + schema = { + "properties": {"foo": {"type": "string"}}, + "unevaluatedProperties": {"const": 12}, + } + message = self.message_for( + instance={ + "foo": "foo", + "bar": "bar", + "baz": 12, + }, + schema=schema, + ) + self.assertEqual( + message, + "Unevaluated properties are not valid under the given schema " + "('bar' was unevaluated and invalid)", + ) + + def test_unevaluated_properties_disallowed(self): + schema = {"type": "object", "unevaluatedProperties": False} + message = self.message_for( + instance={ + "foo": "foo", + "bar": "bar", + }, + schema=schema, + ) + self.assertEqual( + message, + "Unevaluated properties are not allowed " + "('bar', 'foo' were unexpected)", + ) + + def test_unevaluated_properties_on_invalid_type(self): + schema = {"type": "object", "unevaluatedProperties": False} + message = self.message_for(instance="foo", schema=schema) + self.assertEqual(message, "'foo' is not of type 'object'") + + def test_single_item(self): + schema = {"prefixItems": [{}], "items": False} + message = self.message_for( + instance=["foo", "bar", "baz"], + schema=schema, + ) + self.assertEqual( + message, + "Expected at most 1 item but found 2 extra: ['bar', 'baz']", + ) + + def test_heterogeneous_additionalItems_with_Items(self): + schema = {"items": [{}], "additionalItems": False} + message = self.message_for( + instance=["foo", "bar", 37], + schema=schema, + cls=validators.Draft7Validator, + ) + self.assertEqual( + message, + "Additional items are not allowed ('bar', 37 were unexpected)", + ) + + def test_heterogeneous_items_prefixItems(self): + schema = {"prefixItems": [{}], "items": False} + message = self.message_for( + instance=["foo", "bar", 37], + schema=schema, + ) + self.assertEqual( + message, + "Expected at most 1 item but found 2 extra: ['bar', 37]", + ) + + def test_heterogeneous_unevaluatedItems_prefixItems(self): + schema = {"prefixItems": [{}], "unevaluatedItems": False} + message = self.message_for( + instance=["foo", "bar", 37], + schema=schema, + ) + self.assertEqual( + message, + "Unevaluated items are not allowed ('bar', 37 were unexpected)", + ) + + def test_heterogeneous_properties_additionalProperties(self): + """ + Not valid deserialized JSON, but this should not blow up. + """ + schema = {"properties": {"foo": {}}, "additionalProperties": False} + message = self.message_for( + instance={"foo": {}, "a": "baz", 37: 12}, + schema=schema, + ) + self.assertEqual( + message, + "Additional properties are not allowed (37, 'a' were unexpected)", + ) + + def test_heterogeneous_properties_unevaluatedProperties(self): + """ + Not valid deserialized JSON, but this should not blow up. + """ + schema = {"properties": {"foo": {}}, "unevaluatedProperties": False} + message = self.message_for( + instance={"foo": {}, "a": "baz", 37: 12}, + schema=schema, + ) + self.assertEqual( + message, + "Unevaluated properties are not allowed (37, 'a' were unexpected)", + ) + + +class TestValidationErrorDetails(TestCase): + # TODO: These really need unit tests for each individual keyword, rather + # than just these higher level tests. + def test_anyOf(self): + instance = 5 + schema = { + "anyOf": [ + {"minimum": 20}, + {"type": "string"}, + ], + } + + validator = validators.Draft4Validator(schema) + errors = list(validator.iter_errors(instance)) + self.assertEqual(len(errors), 1) + e = errors[0] + + self.assertEqual(e.validator, "anyOf") + self.assertEqual(e.validator_value, schema["anyOf"]) + self.assertEqual(e.instance, instance) + self.assertEqual(e.schema, schema) + self.assertIsNone(e.parent) + + self.assertEqual(e.path, deque([])) + self.assertEqual(e.relative_path, deque([])) + self.assertEqual(e.absolute_path, deque([])) + self.assertEqual(e.json_path, "$") + + self.assertEqual(e.schema_path, deque(["anyOf"])) + self.assertEqual(e.relative_schema_path, deque(["anyOf"])) + self.assertEqual(e.absolute_schema_path, deque(["anyOf"])) + + self.assertEqual(len(e.context), 2) + + e1, e2 = sorted_errors(e.context) + + self.assertEqual(e1.validator, "minimum") + self.assertEqual(e1.validator_value, schema["anyOf"][0]["minimum"]) + self.assertEqual(e1.instance, instance) + self.assertEqual(e1.schema, schema["anyOf"][0]) + self.assertIs(e1.parent, e) + + self.assertEqual(e1.path, deque([])) + self.assertEqual(e1.absolute_path, deque([])) + self.assertEqual(e1.relative_path, deque([])) + self.assertEqual(e1.json_path, "$") + + self.assertEqual(e1.schema_path, deque([0, "minimum"])) + self.assertEqual(e1.relative_schema_path, deque([0, "minimum"])) + self.assertEqual( + e1.absolute_schema_path, deque(["anyOf", 0, "minimum"]), + ) + + self.assertFalse(e1.context) + + self.assertEqual(e2.validator, "type") + self.assertEqual(e2.validator_value, schema["anyOf"][1]["type"]) + self.assertEqual(e2.instance, instance) + self.assertEqual(e2.schema, schema["anyOf"][1]) + self.assertIs(e2.parent, e) + + self.assertEqual(e2.path, deque([])) + self.assertEqual(e2.relative_path, deque([])) + self.assertEqual(e2.absolute_path, deque([])) + self.assertEqual(e2.json_path, "$") + + self.assertEqual(e2.schema_path, deque([1, "type"])) + self.assertEqual(e2.relative_schema_path, deque([1, "type"])) + self.assertEqual(e2.absolute_schema_path, deque(["anyOf", 1, "type"])) + + self.assertEqual(len(e2.context), 0) + + def test_type(self): + instance = {"foo": 1} + schema = { + "type": [ + {"type": "integer"}, + { + "type": "object", + "properties": {"foo": {"enum": [2]}}, + }, + ], + } + + validator = validators.Draft3Validator(schema) + errors = list(validator.iter_errors(instance)) + self.assertEqual(len(errors), 1) + e = errors[0] + + self.assertEqual(e.validator, "type") + self.assertEqual(e.validator_value, schema["type"]) + self.assertEqual(e.instance, instance) + self.assertEqual(e.schema, schema) + self.assertIsNone(e.parent) + + self.assertEqual(e.path, deque([])) + self.assertEqual(e.relative_path, deque([])) + self.assertEqual(e.absolute_path, deque([])) + self.assertEqual(e.json_path, "$") + + self.assertEqual(e.schema_path, deque(["type"])) + self.assertEqual(e.relative_schema_path, deque(["type"])) + self.assertEqual(e.absolute_schema_path, deque(["type"])) + + self.assertEqual(len(e.context), 2) + + e1, e2 = sorted_errors(e.context) + + self.assertEqual(e1.validator, "type") + self.assertEqual(e1.validator_value, schema["type"][0]["type"]) + self.assertEqual(e1.instance, instance) + self.assertEqual(e1.schema, schema["type"][0]) + self.assertIs(e1.parent, e) + + self.assertEqual(e1.path, deque([])) + self.assertEqual(e1.relative_path, deque([])) + self.assertEqual(e1.absolute_path, deque([])) + self.assertEqual(e1.json_path, "$") + + self.assertEqual(e1.schema_path, deque([0, "type"])) + self.assertEqual(e1.relative_schema_path, deque([0, "type"])) + self.assertEqual(e1.absolute_schema_path, deque(["type", 0, "type"])) + + self.assertFalse(e1.context) + + self.assertEqual(e2.validator, "enum") + self.assertEqual(e2.validator_value, [2]) + self.assertEqual(e2.instance, 1) + self.assertEqual(e2.schema, {"enum": [2]}) + self.assertIs(e2.parent, e) + + self.assertEqual(e2.path, deque(["foo"])) + self.assertEqual(e2.relative_path, deque(["foo"])) + self.assertEqual(e2.absolute_path, deque(["foo"])) + self.assertEqual(e2.json_path, "$.foo") + + self.assertEqual( + e2.schema_path, deque([1, "properties", "foo", "enum"]), + ) + self.assertEqual( + e2.relative_schema_path, deque([1, "properties", "foo", "enum"]), + ) + self.assertEqual( + e2.absolute_schema_path, + deque(["type", 1, "properties", "foo", "enum"]), + ) + + self.assertFalse(e2.context) + + def test_single_nesting(self): + instance = {"foo": 2, "bar": [1], "baz": 15, "quux": "spam"} + schema = { + "properties": { + "foo": {"type": "string"}, + "bar": {"minItems": 2}, + "baz": {"maximum": 10, "enum": [2, 4, 6, 8]}, + }, + } + + validator = validators.Draft3Validator(schema) + errors = validator.iter_errors(instance) + e1, e2, e3, e4 = sorted_errors(errors) + + self.assertEqual(e1.path, deque(["bar"])) + self.assertEqual(e2.path, deque(["baz"])) + self.assertEqual(e3.path, deque(["baz"])) + self.assertEqual(e4.path, deque(["foo"])) + + self.assertEqual(e1.relative_path, deque(["bar"])) + self.assertEqual(e2.relative_path, deque(["baz"])) + self.assertEqual(e3.relative_path, deque(["baz"])) + self.assertEqual(e4.relative_path, deque(["foo"])) + + self.assertEqual(e1.absolute_path, deque(["bar"])) + self.assertEqual(e2.absolute_path, deque(["baz"])) + self.assertEqual(e3.absolute_path, deque(["baz"])) + self.assertEqual(e4.absolute_path, deque(["foo"])) + + self.assertEqual(e1.json_path, "$.bar") + self.assertEqual(e2.json_path, "$.baz") + self.assertEqual(e3.json_path, "$.baz") + self.assertEqual(e4.json_path, "$.foo") + + self.assertEqual(e1.validator, "minItems") + self.assertEqual(e2.validator, "enum") + self.assertEqual(e3.validator, "maximum") + self.assertEqual(e4.validator, "type") + + def test_multiple_nesting(self): + instance = [1, {"foo": 2, "bar": {"baz": [1]}}, "quux"] + schema = { + "type": "string", + "items": { + "type": ["string", "object"], + "properties": { + "foo": {"enum": [1, 3]}, + "bar": { + "type": "array", + "properties": { + "bar": {"required": True}, + "baz": {"minItems": 2}, + }, + }, + }, + }, + } + + validator = validators.Draft3Validator(schema) + errors = validator.iter_errors(instance) + e1, e2, e3, e4, e5, e6 = sorted_errors(errors) + + self.assertEqual(e1.path, deque([])) + self.assertEqual(e2.path, deque([0])) + self.assertEqual(e3.path, deque([1, "bar"])) + self.assertEqual(e4.path, deque([1, "bar", "bar"])) + self.assertEqual(e5.path, deque([1, "bar", "baz"])) + self.assertEqual(e6.path, deque([1, "foo"])) + + self.assertEqual(e1.json_path, "$") + self.assertEqual(e2.json_path, "$[0]") + self.assertEqual(e3.json_path, "$[1].bar") + self.assertEqual(e4.json_path, "$[1].bar.bar") + self.assertEqual(e5.json_path, "$[1].bar.baz") + self.assertEqual(e6.json_path, "$[1].foo") + + self.assertEqual(e1.schema_path, deque(["type"])) + self.assertEqual(e2.schema_path, deque(["items", "type"])) + self.assertEqual( + list(e3.schema_path), ["items", "properties", "bar", "type"], + ) + self.assertEqual( + list(e4.schema_path), + ["items", "properties", "bar", "properties", "bar", "required"], + ) + self.assertEqual( + list(e5.schema_path), + ["items", "properties", "bar", "properties", "baz", "minItems"], + ) + self.assertEqual( + list(e6.schema_path), ["items", "properties", "foo", "enum"], + ) + + self.assertEqual(e1.validator, "type") + self.assertEqual(e2.validator, "type") + self.assertEqual(e3.validator, "type") + self.assertEqual(e4.validator, "required") + self.assertEqual(e5.validator, "minItems") + self.assertEqual(e6.validator, "enum") + + def test_recursive(self): + schema = { + "definitions": { + "node": { + "anyOf": [{ + "type": "object", + "required": ["name", "children"], + "properties": { + "name": { + "type": "string", + }, + "children": { + "type": "object", + "patternProperties": { + "^.*$": { + "$ref": "#/definitions/node", + }, + }, + }, + }, + }], + }, + }, + "type": "object", + "required": ["root"], + "properties": {"root": {"$ref": "#/definitions/node"}}, + } + + instance = { + "root": { + "name": "root", + "children": { + "a": { + "name": "a", + "children": { + "ab": { + "name": "ab", + # missing "children" + }, + }, + }, + }, + }, + } + validator = validators.Draft4Validator(schema) + + e, = validator.iter_errors(instance) + self.assertEqual(e.absolute_path, deque(["root"])) + self.assertEqual( + e.absolute_schema_path, deque(["properties", "root", "anyOf"]), + ) + self.assertEqual(e.json_path, "$.root") + + e1, = e.context + self.assertEqual(e1.absolute_path, deque(["root", "children", "a"])) + self.assertEqual( + e1.absolute_schema_path, deque( + [ + "properties", + "root", + "anyOf", + 0, + "properties", + "children", + "patternProperties", + "^.*$", + "anyOf", + ], + ), + ) + self.assertEqual(e1.json_path, "$.root.children.a") + + e2, = e1.context + self.assertEqual( + e2.absolute_path, deque( + ["root", "children", "a", "children", "ab"], + ), + ) + self.assertEqual( + e2.absolute_schema_path, deque( + [ + "properties", + "root", + "anyOf", + 0, + "properties", + "children", + "patternProperties", + "^.*$", + "anyOf", + 0, + "properties", + "children", + "patternProperties", + "^.*$", + "anyOf", + ], + ), + ) + self.assertEqual(e2.json_path, "$.root.children.a.children.ab") + + def test_additionalProperties(self): + instance = {"bar": "bar", "foo": 2} + schema = {"additionalProperties": {"type": "integer", "minimum": 5}} + + validator = validators.Draft3Validator(schema) + errors = validator.iter_errors(instance) + e1, e2 = sorted_errors(errors) + + self.assertEqual(e1.path, deque(["bar"])) + self.assertEqual(e2.path, deque(["foo"])) + + self.assertEqual(e1.json_path, "$.bar") + self.assertEqual(e2.json_path, "$.foo") + + self.assertEqual(e1.validator, "type") + self.assertEqual(e2.validator, "minimum") + + def test_patternProperties(self): + instance = {"bar": 1, "foo": 2} + schema = { + "patternProperties": { + "bar": {"type": "string"}, + "foo": {"minimum": 5}, + }, + } + + validator = validators.Draft3Validator(schema) + errors = validator.iter_errors(instance) + e1, e2 = sorted_errors(errors) + + self.assertEqual(e1.path, deque(["bar"])) + self.assertEqual(e2.path, deque(["foo"])) + + self.assertEqual(e1.json_path, "$.bar") + self.assertEqual(e2.json_path, "$.foo") + + self.assertEqual(e1.validator, "type") + self.assertEqual(e2.validator, "minimum") + + def test_additionalItems(self): + instance = ["foo", 1] + schema = { + "items": [], + "additionalItems": {"type": "integer", "minimum": 5}, + } + + validator = validators.Draft3Validator(schema) + errors = validator.iter_errors(instance) + e1, e2 = sorted_errors(errors) + + self.assertEqual(e1.path, deque([0])) + self.assertEqual(e2.path, deque([1])) + + self.assertEqual(e1.json_path, "$[0]") + self.assertEqual(e2.json_path, "$[1]") + + self.assertEqual(e1.validator, "type") + self.assertEqual(e2.validator, "minimum") + + def test_additionalItems_with_items(self): + instance = ["foo", "bar", 1] + schema = { + "items": [{}], + "additionalItems": {"type": "integer", "minimum": 5}, + } + + validator = validators.Draft3Validator(schema) + errors = validator.iter_errors(instance) + e1, e2 = sorted_errors(errors) + + self.assertEqual(e1.path, deque([1])) + self.assertEqual(e2.path, deque([2])) + + self.assertEqual(e1.json_path, "$[1]") + self.assertEqual(e2.json_path, "$[2]") + + self.assertEqual(e1.validator, "type") + self.assertEqual(e2.validator, "minimum") + + def test_propertyNames(self): + instance = {"foo": 12} + schema = {"propertyNames": {"not": {"const": "foo"}}} + + validator = validators.Draft7Validator(schema) + error, = validator.iter_errors(instance) + + self.assertEqual(error.validator, "not") + self.assertEqual( + error.message, + "'foo' should not be valid under {'const': 'foo'}", + ) + self.assertEqual(error.path, deque([])) + self.assertEqual(error.json_path, "$") + self.assertEqual(error.schema_path, deque(["propertyNames", "not"])) + + def test_if_then(self): + schema = { + "if": {"const": 12}, + "then": {"const": 13}, + } + + validator = validators.Draft7Validator(schema) + error, = validator.iter_errors(12) + + self.assertEqual(error.validator, "const") + self.assertEqual(error.message, "13 was expected") + self.assertEqual(error.path, deque([])) + self.assertEqual(error.json_path, "$") + self.assertEqual(error.schema_path, deque(["then", "const"])) + + def test_if_else(self): + schema = { + "if": {"const": 12}, + "else": {"const": 13}, + } + + validator = validators.Draft7Validator(schema) + error, = validator.iter_errors(15) + + self.assertEqual(error.validator, "const") + self.assertEqual(error.message, "13 was expected") + self.assertEqual(error.path, deque([])) + self.assertEqual(error.json_path, "$") + self.assertEqual(error.schema_path, deque(["else", "const"])) + + def test_boolean_schema_False(self): + validator = validators.Draft7Validator(False) + error, = validator.iter_errors(12) + + self.assertEqual( + ( + error.message, + error.validator, + error.validator_value, + error.instance, + error.schema, + error.schema_path, + error.json_path, + ), + ( + "False schema does not allow 12", + None, + None, + 12, + False, + deque([]), + "$", + ), + ) + + def test_ref(self): + ref, schema = "someRef", {"additionalProperties": {"type": "integer"}} + validator = validators.Draft7Validator( + {"$ref": ref}, + resolver=validators._RefResolver("", {}, store={ref: schema}), + ) + error, = validator.iter_errors({"foo": "notAnInteger"}) + + self.assertEqual( + ( + error.message, + error.validator, + error.validator_value, + error.instance, + error.absolute_path, + error.schema, + error.schema_path, + error.json_path, + ), + ( + "'notAnInteger' is not of type 'integer'", + "type", + "integer", + "notAnInteger", + deque(["foo"]), + {"type": "integer"}, + deque(["additionalProperties", "type"]), + "$.foo", + ), + ) + + def test_prefixItems(self): + schema = {"prefixItems": [{"type": "string"}, {}, {}, {"maximum": 3}]} + validator = validators.Draft202012Validator(schema) + type_error, min_error = validator.iter_errors([1, 2, "foo", 5]) + self.assertEqual( + ( + type_error.message, + type_error.validator, + type_error.validator_value, + type_error.instance, + type_error.absolute_path, + type_error.schema, + type_error.schema_path, + type_error.json_path, + ), + ( + "1 is not of type 'string'", + "type", + "string", + 1, + deque([0]), + {"type": "string"}, + deque(["prefixItems", 0, "type"]), + "$[0]", + ), + ) + self.assertEqual( + ( + min_error.message, + min_error.validator, + min_error.validator_value, + min_error.instance, + min_error.absolute_path, + min_error.schema, + min_error.schema_path, + min_error.json_path, + ), + ( + "5 is greater than the maximum of 3", + "maximum", + 3, + 5, + deque([3]), + {"maximum": 3}, + deque(["prefixItems", 3, "maximum"]), + "$[3]", + ), + ) + + def test_prefixItems_with_items(self): + schema = { + "items": {"type": "string"}, + "prefixItems": [{}], + } + validator = validators.Draft202012Validator(schema) + e1, e2 = validator.iter_errors(["foo", 2, "bar", 4, "baz"]) + self.assertEqual( + ( + e1.message, + e1.validator, + e1.validator_value, + e1.instance, + e1.absolute_path, + e1.schema, + e1.schema_path, + e1.json_path, + ), + ( + "2 is not of type 'string'", + "type", + "string", + 2, + deque([1]), + {"type": "string"}, + deque(["items", "type"]), + "$[1]", + ), + ) + self.assertEqual( + ( + e2.message, + e2.validator, + e2.validator_value, + e2.instance, + e2.absolute_path, + e2.schema, + e2.schema_path, + e2.json_path, + ), + ( + "4 is not of type 'string'", + "type", + "string", + 4, + deque([3]), + {"type": "string"}, + deque(["items", "type"]), + "$[3]", + ), + ) + + def test_contains_too_many(self): + """ + `contains` + `maxContains` produces only one error, even if there are + many more incorrectly matching elements. + """ + schema = {"contains": {"type": "string"}, "maxContains": 2} + validator = validators.Draft202012Validator(schema) + error, = validator.iter_errors(["foo", 2, "bar", 4, "baz", "quux"]) + self.assertEqual( + ( + error.message, + error.validator, + error.validator_value, + error.instance, + error.absolute_path, + error.schema, + error.schema_path, + error.json_path, + ), + ( + "Too many items match the given schema (expected at most 2)", + "maxContains", + 2, + ["foo", 2, "bar", 4, "baz", "quux"], + deque([]), + {"contains": {"type": "string"}, "maxContains": 2}, + deque(["contains"]), + "$", + ), + ) + + def test_contains_too_few(self): + schema = {"contains": {"type": "string"}, "minContains": 2} + validator = validators.Draft202012Validator(schema) + error, = validator.iter_errors(["foo", 2, 4]) + self.assertEqual( + ( + error.message, + error.validator, + error.validator_value, + error.instance, + error.absolute_path, + error.schema, + error.schema_path, + error.json_path, + ), + ( + ( + "Too few items match the given schema " + "(expected at least 2 but only 1 matched)" + ), + "minContains", + 2, + ["foo", 2, 4], + deque([]), + {"contains": {"type": "string"}, "minContains": 2}, + deque(["contains"]), + "$", + ), + ) + + def test_contains_none(self): + schema = {"contains": {"type": "string"}, "minContains": 2} + validator = validators.Draft202012Validator(schema) + error, = validator.iter_errors([2, 4]) + self.assertEqual( + ( + error.message, + error.validator, + error.validator_value, + error.instance, + error.absolute_path, + error.schema, + error.schema_path, + error.json_path, + ), + ( + "[2, 4] does not contain items matching the given schema", + "contains", + {"type": "string"}, + [2, 4], + deque([]), + {"contains": {"type": "string"}, "minContains": 2}, + deque(["contains"]), + "$", + ), + ) + + def test_ref_sibling(self): + schema = { + "$defs": {"foo": {"required": ["bar"]}}, + "properties": { + "aprop": { + "$ref": "#/$defs/foo", + "required": ["baz"], + }, + }, + } + + validator = validators.Draft202012Validator(schema) + e1, e2 = validator.iter_errors({"aprop": {}}) + self.assertEqual( + ( + e1.message, + e1.validator, + e1.validator_value, + e1.instance, + e1.absolute_path, + e1.schema, + e1.schema_path, + e1.relative_schema_path, + e1.json_path, + ), + ( + "'bar' is a required property", + "required", + ["bar"], + {}, + deque(["aprop"]), + {"required": ["bar"]}, + deque(["properties", "aprop", "required"]), + deque(["properties", "aprop", "required"]), + "$.aprop", + ), + ) + self.assertEqual( + ( + e2.message, + e2.validator, + e2.validator_value, + e2.instance, + e2.absolute_path, + e2.schema, + e2.schema_path, + e2.relative_schema_path, + e2.json_path, + ), + ( + "'baz' is a required property", + "required", + ["baz"], + {}, + deque(["aprop"]), + {"$ref": "#/$defs/foo", "required": ["baz"]}, + deque(["properties", "aprop", "required"]), + deque(["properties", "aprop", "required"]), + "$.aprop", + ), + ) + + +class MetaSchemaTestsMixin: + # TODO: These all belong upstream + def test_invalid_properties(self): + with self.assertRaises(exceptions.SchemaError): + self.Validator.check_schema({"properties": 12}) + + def test_minItems_invalid_string(self): + with self.assertRaises(exceptions.SchemaError): + # needs to be an integer + self.Validator.check_schema({"minItems": "1"}) + + def test_enum_allows_empty_arrays(self): + """ + Technically, all the spec says is they SHOULD have elements, not MUST. + + (As of Draft 6. Previous drafts do say MUST). + + See #529. + """ + if self.Validator in { + validators.Draft3Validator, + validators.Draft4Validator, + }: + with self.assertRaises(exceptions.SchemaError): + self.Validator.check_schema({"enum": []}) + else: + self.Validator.check_schema({"enum": []}) + + def test_enum_allows_non_unique_items(self): + """ + Technically, all the spec says is they SHOULD be unique, not MUST. + + (As of Draft 6. Previous drafts do say MUST). + + See #529. + """ + if self.Validator in { + validators.Draft3Validator, + validators.Draft4Validator, + }: + with self.assertRaises(exceptions.SchemaError): + self.Validator.check_schema({"enum": [12, 12]}) + else: + self.Validator.check_schema({"enum": [12, 12]}) + + def test_schema_with_invalid_regex(self): + with self.assertRaises(exceptions.SchemaError): + self.Validator.check_schema({"pattern": "*notaregex"}) + + def test_schema_with_invalid_regex_with_disabled_format_validation(self): + self.Validator.check_schema( + {"pattern": "*notaregex"}, + format_checker=None, + ) + + +class ValidatorTestMixin(MetaSchemaTestsMixin): + def test_it_implements_the_validator_protocol(self): + self.assertIsInstance(self.Validator({}), protocols.Validator) + + def test_valid_instances_are_valid(self): + schema, instance = self.valid + self.assertTrue(self.Validator(schema).is_valid(instance)) + + def test_invalid_instances_are_not_valid(self): + schema, instance = self.invalid + self.assertFalse(self.Validator(schema).is_valid(instance)) + + def test_non_existent_properties_are_ignored(self): + self.Validator({object(): object()}).validate(instance=object()) + + def test_evolve(self): + schema, format_checker = {"type": "integer"}, FormatChecker() + original = self.Validator( + schema, + format_checker=format_checker, + ) + new = original.evolve( + schema={"type": "string"}, + format_checker=self.Validator.FORMAT_CHECKER, + ) + + expected = self.Validator( + {"type": "string"}, + format_checker=self.Validator.FORMAT_CHECKER, + _resolver=new._resolver, + ) + + self.assertEqual(new, expected) + self.assertNotEqual(new, original) + + def test_evolve_with_subclass(self): + """ + Subclassing validators isn't supported public API, but some users have + done it, because we don't actually error entirely when it's done :/ + + We need to deprecate doing so first to help as many of these users + ensure they can move to supported APIs, but this test ensures that in + the interim, we haven't broken those users. + """ + + with self.assertWarns(DeprecationWarning): + @define + class OhNo(self.Validator): + foo = field(factory=lambda: [1, 2, 3]) + _bar = field(default=37) + + validator = OhNo({}, bar=12) + self.assertEqual(validator.foo, [1, 2, 3]) + + new = validator.evolve(schema={"type": "integer"}) + self.assertEqual(new.foo, [1, 2, 3]) + self.assertEqual(new._bar, 12) + + def test_is_type_is_true_for_valid_type(self): + self.assertTrue(self.Validator({}).is_type("foo", "string")) + + def test_is_type_is_false_for_invalid_type(self): + self.assertFalse(self.Validator({}).is_type("foo", "array")) + + def test_is_type_evades_bool_inheriting_from_int(self): + self.assertFalse(self.Validator({}).is_type(True, "integer")) + self.assertFalse(self.Validator({}).is_type(True, "number")) + + def test_it_can_validate_with_decimals(self): + schema = {"items": {"type": "number"}} + Validator = validators.extend( + self.Validator, + type_checker=self.Validator.TYPE_CHECKER.redefine( + "number", + lambda checker, thing: isinstance( + thing, (int, float, Decimal), + ) and not isinstance(thing, bool), + ), + ) + + validator = Validator(schema) + validator.validate([1, 1.1, Decimal(1) / Decimal(8)]) + + invalid = ["foo", {}, [], True, None] + self.assertEqual( + [error.instance for error in validator.iter_errors(invalid)], + invalid, + ) + + def test_it_returns_true_for_formats_it_does_not_know_about(self): + validator = self.Validator( + {"format": "carrot"}, format_checker=FormatChecker(), + ) + validator.validate("bugs") + + def test_it_does_not_validate_formats_by_default(self): + validator = self.Validator({}) + self.assertIsNone(validator.format_checker) + + def test_it_validates_formats_if_a_checker_is_provided(self): + checker = FormatChecker() + bad = ValueError("Bad!") + + @checker.checks("foo", raises=ValueError) + def check(value): + if value == "good": + return True + elif value == "bad": + raise bad + else: # pragma: no cover + self.fail(f"What is {value}? [Baby Don't Hurt Me]") + + validator = self.Validator( + {"format": "foo"}, format_checker=checker, + ) + + validator.validate("good") + with self.assertRaises(exceptions.ValidationError) as cm: + validator.validate("bad") + + # Make sure original cause is attached + self.assertIs(cm.exception.cause, bad) + + def test_non_string_custom_type(self): + non_string_type = object() + schema = {"type": [non_string_type]} + Crazy = validators.extend( + self.Validator, + type_checker=self.Validator.TYPE_CHECKER.redefine( + non_string_type, + lambda checker, thing: isinstance(thing, int), + ), + ) + Crazy(schema).validate(15) + + def test_it_properly_formats_tuples_in_errors(self): + """ + A tuple instance properly formats validation errors for uniqueItems. + + See #224 + """ + TupleValidator = validators.extend( + self.Validator, + type_checker=self.Validator.TYPE_CHECKER.redefine( + "array", + lambda checker, thing: isinstance(thing, tuple), + ), + ) + with self.assertRaises(exceptions.ValidationError) as e: + TupleValidator({"uniqueItems": True}).validate((1, 1)) + self.assertIn("(1, 1) has non-unique elements", str(e.exception)) + + def test_check_redefined_sequence(self): + """ + Allow array to validate against another defined sequence type + """ + schema = {"type": "array", "uniqueItems": True} + MyMapping = namedtuple("MyMapping", "a, b") + Validator = validators.extend( + self.Validator, + type_checker=self.Validator.TYPE_CHECKER.redefine_many( + { + "array": lambda checker, thing: isinstance( + thing, (list, deque), + ), + "object": lambda checker, thing: isinstance( + thing, (dict, MyMapping), + ), + }, + ), + ) + validator = Validator(schema) + + valid_instances = [ + deque(["a", None, "1", "", True]), + deque([[False], [0]]), + [deque([False]), deque([0])], + [[deque([False])], [deque([0])]], + [[[[[deque([False])]]]], [[[[deque([0])]]]]], + [deque([deque([False])]), deque([deque([0])])], + [MyMapping("a", 0), MyMapping("a", False)], + [ + MyMapping("a", [deque([0])]), + MyMapping("a", [deque([False])]), + ], + [ + MyMapping("a", [MyMapping("a", deque([0]))]), + MyMapping("a", [MyMapping("a", deque([False]))]), + ], + [deque(deque(deque([False]))), deque(deque(deque([0])))], + ] + + for instance in valid_instances: + validator.validate(instance) + + invalid_instances = [ + deque(["a", "b", "a"]), + deque([[False], [False]]), + [deque([False]), deque([False])], + [[deque([False])], [deque([False])]], + [[[[[deque([False])]]]], [[[[deque([False])]]]]], + [deque([deque([False])]), deque([deque([False])])], + [MyMapping("a", False), MyMapping("a", False)], + [ + MyMapping("a", [deque([False])]), + MyMapping("a", [deque([False])]), + ], + [ + MyMapping("a", [MyMapping("a", deque([False]))]), + MyMapping("a", [MyMapping("a", deque([False]))]), + ], + [deque(deque(deque([False]))), deque(deque(deque([False])))], + ] + + for instance in invalid_instances: + with self.assertRaises(exceptions.ValidationError): + validator.validate(instance) + + def test_it_creates_a_ref_resolver_if_not_provided(self): + with self.assertWarns(DeprecationWarning): + resolver = self.Validator({}).resolver + self.assertIsInstance(resolver, validators._RefResolver) + + def test_it_upconverts_from_deprecated_RefResolvers(self): + ref, schema = "someCoolRef", {"type": "integer"} + resolver = validators._RefResolver("", {}, store={ref: schema}) + validator = self.Validator({"$ref": ref}, resolver=resolver) + + with self.assertRaises(exceptions.ValidationError): + validator.validate(None) + + def test_it_upconverts_from_yet_older_deprecated_legacy_RefResolvers(self): + """ + Legacy RefResolvers support only the context manager form of + resolution. + """ + + class LegacyRefResolver: + @contextmanager + def resolving(this, ref): + self.assertEqual(ref, "the ref") + yield {"type": "integer"} + + resolver = LegacyRefResolver() + schema = {"$ref": "the ref"} + + with self.assertRaises(exceptions.ValidationError): + self.Validator(schema, resolver=resolver).validate(None) + + +class AntiDraft6LeakMixin: + """ + Make sure functionality from draft 6 doesn't leak backwards in time. + """ + + def test_True_is_not_a_schema(self): + with self.assertRaises(exceptions.SchemaError) as e: + self.Validator.check_schema(True) + self.assertIn("True is not of type", str(e.exception)) + + def test_False_is_not_a_schema(self): + with self.assertRaises(exceptions.SchemaError) as e: + self.Validator.check_schema(False) + self.assertIn("False is not of type", str(e.exception)) + + def test_True_is_not_a_schema_even_if_you_forget_to_check(self): + with self.assertRaises(Exception) as e: + self.Validator(True).validate(12) + self.assertNotIsInstance(e.exception, exceptions.ValidationError) + + def test_False_is_not_a_schema_even_if_you_forget_to_check(self): + with self.assertRaises(Exception) as e: + self.Validator(False).validate(12) + self.assertNotIsInstance(e.exception, exceptions.ValidationError) + + +class TestDraft3Validator(AntiDraft6LeakMixin, ValidatorTestMixin, TestCase): + Validator = validators.Draft3Validator + valid: tuple[dict, dict] = ({}, {}) + invalid = {"type": "integer"}, "foo" + + def test_any_type_is_valid_for_type_any(self): + validator = self.Validator({"type": "any"}) + validator.validate(object()) + + def test_any_type_is_redefinable(self): + """ + Sigh, because why not. + """ + Crazy = validators.extend( + self.Validator, + type_checker=self.Validator.TYPE_CHECKER.redefine( + "any", lambda checker, thing: isinstance(thing, int), + ), + ) + validator = Crazy({"type": "any"}) + validator.validate(12) + with self.assertRaises(exceptions.ValidationError): + validator.validate("foo") + + def test_is_type_is_true_for_any_type(self): + self.assertTrue(self.Validator({"type": "any"}).is_valid(object())) + + def test_is_type_does_not_evade_bool_if_it_is_being_tested(self): + self.assertTrue(self.Validator({}).is_type(True, "boolean")) + self.assertTrue(self.Validator({"type": "any"}).is_valid(True)) + + +class TestDraft4Validator(AntiDraft6LeakMixin, ValidatorTestMixin, TestCase): + Validator = validators.Draft4Validator + valid: tuple[dict, dict] = ({}, {}) + invalid = {"type": "integer"}, "foo" + + +class TestDraft6Validator(ValidatorTestMixin, TestCase): + Validator = validators.Draft6Validator + valid: tuple[dict, dict] = ({}, {}) + invalid = {"type": "integer"}, "foo" + + +class TestDraft7Validator(ValidatorTestMixin, TestCase): + Validator = validators.Draft7Validator + valid: tuple[dict, dict] = ({}, {}) + invalid = {"type": "integer"}, "foo" + + +class TestDraft201909Validator(ValidatorTestMixin, TestCase): + Validator = validators.Draft201909Validator + valid: tuple[dict, dict] = ({}, {}) + invalid = {"type": "integer"}, "foo" + + +class TestDraft202012Validator(ValidatorTestMixin, TestCase): + Validator = validators.Draft202012Validator + valid: tuple[dict, dict] = ({}, {}) + invalid = {"type": "integer"}, "foo" + + +class TestLatestValidator(TestCase): + """ + These really apply to multiple versions but are easiest to test on one. + """ + + def test_ref_resolvers_may_have_boolean_schemas_stored(self): + ref = "someCoolRef" + schema = {"$ref": ref} + resolver = validators._RefResolver("", {}, store={ref: False}) + validator = validators._LATEST_VERSION(schema, resolver=resolver) + + with self.assertRaises(exceptions.ValidationError): + validator.validate(None) + + +class TestValidatorFor(TestCase): + def test_draft_3(self): + schema = {"$schema": "http://json-schema.org/draft-03/schema"} + self.assertIs( + validators.validator_for(schema), + validators.Draft3Validator, + ) + + schema = {"$schema": "http://json-schema.org/draft-03/schema#"} + self.assertIs( + validators.validator_for(schema), + validators.Draft3Validator, + ) + + def test_draft_4(self): + schema = {"$schema": "http://json-schema.org/draft-04/schema"} + self.assertIs( + validators.validator_for(schema), + validators.Draft4Validator, + ) + + schema = {"$schema": "http://json-schema.org/draft-04/schema#"} + self.assertIs( + validators.validator_for(schema), + validators.Draft4Validator, + ) + + def test_draft_6(self): + schema = {"$schema": "http://json-schema.org/draft-06/schema"} + self.assertIs( + validators.validator_for(schema), + validators.Draft6Validator, + ) + + schema = {"$schema": "http://json-schema.org/draft-06/schema#"} + self.assertIs( + validators.validator_for(schema), + validators.Draft6Validator, + ) + + def test_draft_7(self): + schema = {"$schema": "http://json-schema.org/draft-07/schema"} + self.assertIs( + validators.validator_for(schema), + validators.Draft7Validator, + ) + + schema = {"$schema": "http://json-schema.org/draft-07/schema#"} + self.assertIs( + validators.validator_for(schema), + validators.Draft7Validator, + ) + + def test_draft_201909(self): + schema = {"$schema": "https://json-schema.org/draft/2019-09/schema"} + self.assertIs( + validators.validator_for(schema), + validators.Draft201909Validator, + ) + + schema = {"$schema": "https://json-schema.org/draft/2019-09/schema#"} + self.assertIs( + validators.validator_for(schema), + validators.Draft201909Validator, + ) + + def test_draft_202012(self): + schema = {"$schema": "https://json-schema.org/draft/2020-12/schema"} + self.assertIs( + validators.validator_for(schema), + validators.Draft202012Validator, + ) + + schema = {"$schema": "https://json-schema.org/draft/2020-12/schema#"} + self.assertIs( + validators.validator_for(schema), + validators.Draft202012Validator, + ) + + def test_True(self): + self.assertIs( + validators.validator_for(True), + validators._LATEST_VERSION, + ) + + def test_False(self): + self.assertIs( + validators.validator_for(False), + validators._LATEST_VERSION, + ) + + def test_custom_validator(self): + Validator = validators.create( + meta_schema={"id": "meta schema id"}, + version="12", + id_of=lambda s: s.get("id", ""), + ) + schema = {"$schema": "meta schema id"} + self.assertIs( + validators.validator_for(schema), + Validator, + ) + + def test_custom_validator_draft6(self): + Validator = validators.create( + meta_schema={"$id": "meta schema $id"}, + version="13", + ) + schema = {"$schema": "meta schema $id"} + self.assertIs( + validators.validator_for(schema), + Validator, + ) + + def test_validator_for_jsonschema_default(self): + self.assertIs(validators.validator_for({}), validators._LATEST_VERSION) + + def test_validator_for_custom_default(self): + self.assertIs(validators.validator_for({}, default=None), None) + + def test_warns_if_meta_schema_specified_was_not_found(self): + with self.assertWarns(DeprecationWarning) as cm: + validators.validator_for(schema={"$schema": "unknownSchema"}) + + self.assertEqual(cm.filename, __file__) + self.assertEqual( + str(cm.warning), + "The metaschema specified by $schema was not found. " + "Using the latest draft to validate, but this will raise " + "an error in the future.", + ) + + def test_does_not_warn_if_meta_schema_is_unspecified(self): + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter("always") + validators.validator_for(schema={}, default={}) + self.assertFalse(w) + + def test_validator_for_custom_default_with_schema(self): + schema, default = {"$schema": "mailto:foo@example.com"}, object() + self.assertIs(validators.validator_for(schema, default), default) + + +class TestValidate(TestCase): + def assertUses(self, schema, Validator): + result = [] + with mock.patch.object(Validator, "check_schema", result.append): + validators.validate({}, schema) + self.assertEqual(result, [schema]) + + def test_draft3_validator_is_chosen(self): + self.assertUses( + schema={"$schema": "http://json-schema.org/draft-03/schema#"}, + Validator=validators.Draft3Validator, + ) + # Make sure it works without the empty fragment + self.assertUses( + schema={"$schema": "http://json-schema.org/draft-03/schema"}, + Validator=validators.Draft3Validator, + ) + + def test_draft4_validator_is_chosen(self): + self.assertUses( + schema={"$schema": "http://json-schema.org/draft-04/schema#"}, + Validator=validators.Draft4Validator, + ) + # Make sure it works without the empty fragment + self.assertUses( + schema={"$schema": "http://json-schema.org/draft-04/schema"}, + Validator=validators.Draft4Validator, + ) + + def test_draft6_validator_is_chosen(self): + self.assertUses( + schema={"$schema": "http://json-schema.org/draft-06/schema#"}, + Validator=validators.Draft6Validator, + ) + # Make sure it works without the empty fragment + self.assertUses( + schema={"$schema": "http://json-schema.org/draft-06/schema"}, + Validator=validators.Draft6Validator, + ) + + def test_draft7_validator_is_chosen(self): + self.assertUses( + schema={"$schema": "http://json-schema.org/draft-07/schema#"}, + Validator=validators.Draft7Validator, + ) + # Make sure it works without the empty fragment + self.assertUses( + schema={"$schema": "http://json-schema.org/draft-07/schema"}, + Validator=validators.Draft7Validator, + ) + + def test_draft202012_validator_is_chosen(self): + self.assertUses( + schema={ + "$schema": "https://json-schema.org/draft/2020-12/schema#", + }, + Validator=validators.Draft202012Validator, + ) + # Make sure it works without the empty fragment + self.assertUses( + schema={ + "$schema": "https://json-schema.org/draft/2020-12/schema", + }, + Validator=validators.Draft202012Validator, + ) + + def test_draft202012_validator_is_the_default(self): + self.assertUses(schema={}, Validator=validators.Draft202012Validator) + + def test_validation_error_message(self): + with self.assertRaises(exceptions.ValidationError) as e: + validators.validate(12, {"type": "string"}) + self.assertRegex( + str(e.exception), + "(?s)Failed validating '.*' in schema.*On instance", + ) + + def test_schema_error_message(self): + with self.assertRaises(exceptions.SchemaError) as e: + validators.validate(12, {"type": 12}) + self.assertRegex( + str(e.exception), + "(?s)Failed validating '.*' in metaschema.*On schema", + ) + + def test_it_uses_best_match(self): + schema = { + "oneOf": [ + {"type": "number", "minimum": 20}, + {"type": "array"}, + ], + } + with self.assertRaises(exceptions.ValidationError) as e: + validators.validate(12, schema) + self.assertIn("12 is less than the minimum of 20", str(e.exception)) + + +class TestThreading(TestCase): + """ + Threading-related functionality tests. + + jsonschema doesn't promise thread safety, and its validation behavior + across multiple threads may change at any time, but that means it isn't + safe to share *validators* across threads, not that anytime one has + multiple threads that jsonschema won't work (it certainly is intended to). + + These tests ensure that this minimal level of functionality continues to + work. + """ + + def test_validation_across_a_second_thread(self): + failed = [] + + def validate(): + try: + validators.validate(instance=37, schema=True) + except: # pragma: no cover # noqa: E722 + failed.append(sys.exc_info()) + + validate() # just verify it succeeds + + from threading import Thread + thread = Thread(target=validate) + thread.start() + thread.join() + self.assertEqual((thread.is_alive(), failed), (False, [])) + + +class TestReferencing(TestCase): + def test_registry_with_retrieve(self): + def retrieve(uri): + return DRAFT202012.create_resource({"type": "integer"}) + + registry = referencing.Registry(retrieve=retrieve) + schema = {"$ref": "https://example.com/"} + validator = validators.Draft202012Validator(schema, registry=registry) + + self.assertEqual( + (validator.is_valid(12), validator.is_valid("foo")), + (True, False), + ) + + def test_custom_registries_do_not_autoretrieve_remote_resources(self): + registry = referencing.Registry() + schema = {"$ref": "https://example.com/"} + validator = validators.Draft202012Validator(schema, registry=registry) + + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter("always") + with self.assertRaises(referencing.exceptions.Unresolvable): + validator.validate(12) + self.assertFalse(w) + + +class TestRefResolver(TestCase): + + base_uri = "" + stored_uri = "foo://stored" + stored_schema = {"stored": "schema"} + + def setUp(self): + self.referrer = {} + self.store = {self.stored_uri: self.stored_schema} + self.resolver = validators._RefResolver( + self.base_uri, self.referrer, self.store, + ) + + def test_it_does_not_retrieve_schema_urls_from_the_network(self): + ref = validators.Draft3Validator.META_SCHEMA["id"] + with mock.patch.object(self.resolver, "resolve_remote") as patched: # noqa: SIM117 + with self.resolver.resolving(ref) as resolved: + pass + self.assertEqual(resolved, validators.Draft3Validator.META_SCHEMA) + self.assertFalse(patched.called) + + def test_it_resolves_local_refs(self): + ref = "#/properties/foo" + self.referrer["properties"] = {"foo": object()} + with self.resolver.resolving(ref) as resolved: + self.assertEqual(resolved, self.referrer["properties"]["foo"]) + + def test_it_resolves_local_refs_with_id(self): + schema = {"id": "http://bar/schema#", "a": {"foo": "bar"}} + resolver = validators._RefResolver.from_schema( + schema, + id_of=lambda schema: schema.get("id", ""), + ) + with resolver.resolving("#/a") as resolved: + self.assertEqual(resolved, schema["a"]) + with resolver.resolving("http://bar/schema#/a") as resolved: + self.assertEqual(resolved, schema["a"]) + + def test_it_retrieves_stored_refs(self): + with self.resolver.resolving(self.stored_uri) as resolved: + self.assertIs(resolved, self.stored_schema) + + self.resolver.store["cached_ref"] = {"foo": 12} + with self.resolver.resolving("cached_ref#/foo") as resolved: + self.assertEqual(resolved, 12) + + def test_it_retrieves_unstored_refs_via_requests(self): + ref = "http://bar#baz" + schema = {"baz": 12} + + if "requests" in sys.modules: # pragma: no cover + self.addCleanup( + sys.modules.__setitem__, "requests", sys.modules["requests"], + ) + sys.modules["requests"] = ReallyFakeRequests({"http://bar": schema}) + + with self.resolver.resolving(ref) as resolved: + self.assertEqual(resolved, 12) + + def test_it_retrieves_unstored_refs_via_urlopen(self): + ref = "http://bar#baz" + schema = {"baz": 12} + + if "requests" in sys.modules: # pragma: no cover + self.addCleanup( + sys.modules.__setitem__, "requests", sys.modules["requests"], + ) + sys.modules["requests"] = None + + @contextmanager + def fake_urlopen(url): + self.assertEqual(url, "http://bar") + yield BytesIO(json.dumps(schema).encode("utf8")) + + with mock.patch("urllib.request.urlopen", new=fake_urlopen): # noqa: SIM117 + with self.resolver.resolving(ref) as resolved: + pass + self.assertEqual(resolved, 12) + + def test_it_retrieves_local_refs_via_urlopen(self): + with tempfile.NamedTemporaryFile(delete=False, mode="wt") as tempf: + self.addCleanup(os.remove, tempf.name) + json.dump({"foo": "bar"}, tempf) + + ref = f"file://{pathname2url(tempf.name)}#foo" + with self.resolver.resolving(ref) as resolved: + self.assertEqual(resolved, "bar") + + def test_it_can_construct_a_base_uri_from_a_schema(self): + schema = {"id": "foo"} + resolver = validators._RefResolver.from_schema( + schema, + id_of=lambda schema: schema.get("id", ""), + ) + self.assertEqual(resolver.base_uri, "foo") + self.assertEqual(resolver.resolution_scope, "foo") + with resolver.resolving("") as resolved: + self.assertEqual(resolved, schema) + with resolver.resolving("#") as resolved: + self.assertEqual(resolved, schema) + with resolver.resolving("foo") as resolved: + self.assertEqual(resolved, schema) + with resolver.resolving("foo#") as resolved: + self.assertEqual(resolved, schema) + + def test_it_can_construct_a_base_uri_from_a_schema_without_id(self): + schema = {} + resolver = validators._RefResolver.from_schema(schema) + self.assertEqual(resolver.base_uri, "") + self.assertEqual(resolver.resolution_scope, "") + with resolver.resolving("") as resolved: + self.assertEqual(resolved, schema) + with resolver.resolving("#") as resolved: + self.assertEqual(resolved, schema) + + def test_custom_uri_scheme_handlers(self): + def handler(url): + self.assertEqual(url, ref) + return schema + + schema = {"foo": "bar"} + ref = "foo://bar" + resolver = validators._RefResolver("", {}, handlers={"foo": handler}) + with resolver.resolving(ref) as resolved: + self.assertEqual(resolved, schema) + + def test_cache_remote_on(self): + response = [object()] + + def handler(url): + try: + return response.pop() + except IndexError: # pragma: no cover + self.fail("Response must not have been cached!") + + ref = "foo://bar" + resolver = validators._RefResolver( + "", {}, cache_remote=True, handlers={"foo": handler}, + ) + with resolver.resolving(ref): + pass + with resolver.resolving(ref): + pass + + def test_cache_remote_off(self): + response = [object()] + + def handler(url): + try: + return response.pop() + except IndexError: # pragma: no cover + self.fail("Handler called twice!") + + ref = "foo://bar" + resolver = validators._RefResolver( + "", {}, cache_remote=False, handlers={"foo": handler}, + ) + with resolver.resolving(ref): + pass + + def test_if_you_give_it_junk_you_get_a_resolution_error(self): + error = ValueError("Oh no! What's this?") + + def handler(url): + raise error + + ref = "foo://bar" + resolver = validators._RefResolver("", {}, handlers={"foo": handler}) + with self.assertRaises(exceptions._RefResolutionError) as err: # noqa: SIM117 + with resolver.resolving(ref): + self.fail("Shouldn't get this far!") # pragma: no cover + self.assertEqual(err.exception, exceptions._RefResolutionError(error)) + + def test_helpful_error_message_on_failed_pop_scope(self): + resolver = validators._RefResolver("", {}) + resolver.pop_scope() + with self.assertRaises(exceptions._RefResolutionError) as exc: + resolver.pop_scope() + self.assertIn("Failed to pop the scope", str(exc.exception)) + + def test_pointer_within_schema_with_different_id(self): + """ + See #1085. + """ + schema = validators.Draft7Validator.META_SCHEMA + one = validators._RefResolver("", schema) + validator = validators.Draft7Validator(schema, resolver=one) + self.assertFalse(validator.is_valid({"maxLength": "foo"})) + + another = { + "allOf": [{"$ref": validators.Draft7Validator.META_SCHEMA["$id"]}], + } + two = validators._RefResolver("", another) + validator = validators.Draft7Validator(another, resolver=two) + self.assertFalse(validator.is_valid({"maxLength": "foo"})) + + def test_newly_created_validator_with_ref_resolver(self): + """ + See https://github.com/python-jsonschema/jsonschema/issues/1061#issuecomment-1624266555. + """ + + def handle(uri): + self.assertEqual(uri, "http://example.com/foo") + return {"type": "integer"} + + resolver = validators._RefResolver("", {}, handlers={"http": handle}) + Validator = validators.create( + meta_schema={}, + validators=validators.Draft4Validator.VALIDATORS, + ) + schema = {"$id": "http://example.com/bar", "$ref": "foo"} + validator = Validator(schema, resolver=resolver) + self.assertEqual( + (validator.is_valid({}), validator.is_valid(37)), + (False, True), + ) + + def test_refresolver_with_pointer_in_schema_with_no_id(self): + """ + See https://github.com/python-jsonschema/jsonschema/issues/1124#issuecomment-1632574249. + """ + + schema = { + "properties": {"x": {"$ref": "#/definitions/x"}}, + "definitions": {"x": {"type": "integer"}}, + } + + validator = validators.Draft202012Validator( + schema, + resolver=validators._RefResolver("", schema), + ) + self.assertEqual( + (validator.is_valid({"x": "y"}), validator.is_valid({"x": 37})), + (False, True), + ) + + + +def sorted_errors(errors): + def key(error): + return ( + [str(e) for e in error.path], + [str(e) for e in error.schema_path], + ) + return sorted(errors, key=key) + + +@define +class ReallyFakeRequests: + + _responses: dict[str, Any] + + def get(self, url): + response = self._responses.get(url) + if url is None: # pragma: no cover + raise ValueError("Unknown URL: " + repr(url)) + return _ReallyFakeJSONResponse(json.dumps(response)) + + +@define +class _ReallyFakeJSONResponse: + + _response: str + + def json(self): + return json.loads(self._response) diff --git a/lib/python3.12/site-packages/jsonschema/tests/typing/__init__.py b/lib/python3.12/site-packages/jsonschema/tests/typing/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/lib/python3.12/site-packages/jsonschema/tests/typing/__pycache__/__init__.cpython-312.pyc b/lib/python3.12/site-packages/jsonschema/tests/typing/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8506d86ed2be74dfeead586a851d862960854770 Binary files /dev/null and b/lib/python3.12/site-packages/jsonschema/tests/typing/__pycache__/__init__.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/jsonschema/tests/typing/__pycache__/test_all_concrete_validators_match_protocol.cpython-312.pyc b/lib/python3.12/site-packages/jsonschema/tests/typing/__pycache__/test_all_concrete_validators_match_protocol.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9a26727f212dc6c9567f1ca07f50eaa384026c47 Binary files /dev/null and b/lib/python3.12/site-packages/jsonschema/tests/typing/__pycache__/test_all_concrete_validators_match_protocol.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/jsonschema/tests/typing/test_all_concrete_validators_match_protocol.py b/lib/python3.12/site-packages/jsonschema/tests/typing/test_all_concrete_validators_match_protocol.py new file mode 100644 index 0000000000000000000000000000000000000000..63e8bd4052055e387854ae8a4bb906d78502cb69 --- /dev/null +++ b/lib/python3.12/site-packages/jsonschema/tests/typing/test_all_concrete_validators_match_protocol.py @@ -0,0 +1,38 @@ +""" +This module acts as a test that type checkers will allow each validator +class to be assigned to a variable of type `type[Validator]` + +The assignation is only valid if type checkers recognize each Validator +implementation as a valid implementer of the protocol. +""" +from jsonschema.protocols import Validator +from jsonschema.validators import ( + Draft3Validator, + Draft4Validator, + Draft6Validator, + Draft7Validator, + Draft201909Validator, + Draft202012Validator, +) + +my_validator: type[Validator] + +my_validator = Draft3Validator +my_validator = Draft4Validator +my_validator = Draft6Validator +my_validator = Draft7Validator +my_validator = Draft201909Validator +my_validator = Draft202012Validator + + +# in order to confirm that none of the above were incorrectly typed as 'Any' +# ensure that each of these assignments to a non-validator variable requires an +# ignore +none_var: None + +none_var = Draft3Validator # type: ignore[assignment] +none_var = Draft4Validator # type: ignore[assignment] +none_var = Draft6Validator # type: ignore[assignment] +none_var = Draft7Validator # type: ignore[assignment] +none_var = Draft201909Validator # type: ignore[assignment] +none_var = Draft202012Validator # type: ignore[assignment] diff --git a/lib/python3.12/site-packages/jsonschema/validators.py b/lib/python3.12/site-packages/jsonschema/validators.py new file mode 100644 index 0000000000000000000000000000000000000000..98ddf6bfb58fd28dad54a5b1b506c3abd2322b4a --- /dev/null +++ b/lib/python3.12/site-packages/jsonschema/validators.py @@ -0,0 +1,1410 @@ +""" +Creation and extension of validators, with implementations for existing drafts. +""" +from __future__ import annotations + +from collections import deque +from collections.abc import Iterable, Mapping, Sequence +from functools import lru_cache +from operator import methodcaller +from typing import TYPE_CHECKING +from urllib.parse import unquote, urldefrag, urljoin, urlsplit +from warnings import warn +import contextlib +import json +import reprlib +import warnings + +from attrs import define, field, fields +from jsonschema_specifications import REGISTRY as SPECIFICATIONS +from rpds import HashTrieMap +import referencing.exceptions +import referencing.jsonschema + +from jsonschema import ( + _format, + _keywords, + _legacy_keywords, + _types, + _typing, + _utils, + exceptions, +) + +if TYPE_CHECKING: + from jsonschema.protocols import Validator + +_UNSET = _utils.Unset() + +_VALIDATORS: dict[str, Validator] = {} +_META_SCHEMAS = _utils.URIDict() + + +def __getattr__(name): + if name == "ErrorTree": + warnings.warn( + "Importing ErrorTree from jsonschema.validators is deprecated. " + "Instead import it from jsonschema.exceptions.", + DeprecationWarning, + stacklevel=2, + ) + from jsonschema.exceptions import ErrorTree + return ErrorTree + elif name == "validators": + warnings.warn( + "Accessing jsonschema.validators.validators is deprecated. " + "Use jsonschema.validators.validator_for with a given schema.", + DeprecationWarning, + stacklevel=2, + ) + return _VALIDATORS + elif name == "meta_schemas": + warnings.warn( + "Accessing jsonschema.validators.meta_schemas is deprecated. " + "Use jsonschema.validators.validator_for with a given schema.", + DeprecationWarning, + stacklevel=2, + ) + return _META_SCHEMAS + elif name == "RefResolver": + warnings.warn( + _RefResolver._DEPRECATION_MESSAGE, + DeprecationWarning, + stacklevel=2, + ) + return _RefResolver + raise AttributeError(f"module {__name__} has no attribute {name}") + + +def validates(version): + """ + Register the decorated validator for a ``version`` of the specification. + + Registered validators and their meta schemas will be considered when + parsing :kw:`$schema` keywords' URIs. + + Arguments: + + version (str): + + An identifier to use as the version's name + + Returns: + + collections.abc.Callable: + + a class decorator to decorate the validator with the version + + """ + + def _validates(cls): + _VALIDATORS[version] = cls + meta_schema_id = cls.ID_OF(cls.META_SCHEMA) + _META_SCHEMAS[meta_schema_id] = cls + return cls + return _validates + + +def _warn_for_remote_retrieve(uri: str): + from urllib.request import Request, urlopen + headers = {"User-Agent": "python-jsonschema (deprecated $ref resolution)"} + request = Request(uri, headers=headers) # noqa: S310 + with urlopen(request) as response: # noqa: S310 + warnings.warn( + "Automatically retrieving remote references can be a security " + "vulnerability and is discouraged by the JSON Schema " + "specifications. Relying on this behavior is deprecated " + "and will shortly become an error. If you are sure you want to " + "remotely retrieve your reference and that it is safe to do so, " + "you can find instructions for doing so via referencing.Registry " + "in the referencing documentation " + "(https://referencing.readthedocs.org).", + DeprecationWarning, + stacklevel=9, # Ha ha ha ha magic numbers :/ + ) + return referencing.Resource.from_contents( + json.load(response), + default_specification=referencing.jsonschema.DRAFT202012, + ) + + +_REMOTE_WARNING_REGISTRY = SPECIFICATIONS.combine( + referencing.Registry(retrieve=_warn_for_remote_retrieve), # type: ignore[call-arg] +) + + +def create( + meta_schema: referencing.jsonschema.ObjectSchema, + validators: ( + Mapping[str, _typing.SchemaKeywordValidator] + | Iterable[tuple[str, _typing.SchemaKeywordValidator]] + ) = (), + version: str | None = None, + type_checker: _types.TypeChecker = _types.draft202012_type_checker, + format_checker: _format.FormatChecker = _format.draft202012_format_checker, + id_of: _typing.id_of = referencing.jsonschema.DRAFT202012.id_of, + applicable_validators: _typing.ApplicableValidators = methodcaller( + "items", + ), +) -> type[Validator]: + """ + Create a new validator class. + + Arguments: + + meta_schema: + + the meta schema for the new validator class + + validators: + + a mapping from names to callables, where each callable will + validate the schema property with the given name. + + Each callable should take 4 arguments: + + 1. a validator instance, + 2. the value of the property being validated within the + instance + 3. the instance + 4. the schema + + version: + + an identifier for the version that this validator class will + validate. If provided, the returned validator class will + have its ``__name__`` set to include the version, and also + will have `jsonschema.validators.validates` automatically + called for the given version. + + type_checker: + + a type checker, used when applying the :kw:`type` keyword. + + If unprovided, a `jsonschema.TypeChecker` will be created + with a set of default types typical of JSON Schema drafts. + + format_checker: + + a format checker, used when applying the :kw:`format` keyword. + + If unprovided, a `jsonschema.FormatChecker` will be created + with a set of default formats typical of JSON Schema drafts. + + id_of: + + A function that given a schema, returns its ID. + + applicable_validators: + + A function that, given a schema, returns the list of + applicable schema keywords and associated values + which will be used to validate the instance. + This is mostly used to support pre-draft 7 versions of JSON Schema + which specified behavior around ignoring keywords if they were + siblings of a ``$ref`` keyword. If you're not attempting to + implement similar behavior, you can typically ignore this argument + and leave it at its default. + + Returns: + + a new `jsonschema.protocols.Validator` class + + """ + # preemptively don't shadow the `Validator.format_checker` local + format_checker_arg = format_checker + + specification = referencing.jsonschema.specification_with( + dialect_id=id_of(meta_schema) or "urn:unknown-dialect", + default=referencing.Specification.OPAQUE, + ) + + @define + class Validator: + + VALIDATORS = dict(validators) # noqa: RUF012 + META_SCHEMA = dict(meta_schema) # noqa: RUF012 + TYPE_CHECKER = type_checker + FORMAT_CHECKER = format_checker_arg + ID_OF = staticmethod(id_of) + + _APPLICABLE_VALIDATORS = applicable_validators + _validators = field(init=False, repr=False, eq=False) + + schema: referencing.jsonschema.Schema = field(repr=reprlib.repr) + _ref_resolver = field(default=None, repr=False, alias="resolver") + format_checker: _format.FormatChecker | None = field(default=None) + # TODO: include new meta-schemas added at runtime + _registry: referencing.jsonschema.SchemaRegistry = field( + default=_REMOTE_WARNING_REGISTRY, + kw_only=True, + repr=False, + ) + _resolver = field( + alias="_resolver", + default=None, + kw_only=True, + repr=False, + ) + + def __init_subclass__(cls): + warnings.warn( + ( + "Subclassing validator classes is not intended to " + "be part of their public API. A future version " + "will make doing so an error, as the behavior of " + "subclasses isn't guaranteed to stay the same " + "between releases of jsonschema. Instead, prefer " + "composition of validators, wrapping them in an object " + "owned entirely by the downstream library." + ), + DeprecationWarning, + stacklevel=2, + ) + + def evolve(self, **changes): + cls = self.__class__ + schema = changes.setdefault("schema", self.schema) + NewValidator = validator_for(schema, default=cls) + + for field in fields(cls): # noqa: F402 + if not field.init: + continue + attr_name = field.name + init_name = field.alias + if init_name not in changes: + changes[init_name] = getattr(self, attr_name) + + return NewValidator(**changes) + + cls.evolve = evolve + + def __attrs_post_init__(self): + if self._resolver is None: + registry = self._registry + if registry is not _REMOTE_WARNING_REGISTRY: + registry = SPECIFICATIONS.combine(registry) + resource = specification.create_resource(self.schema) + self._resolver = registry.resolver_with_root(resource) + + if self.schema is True or self.schema is False: + self._validators = [] + else: + self._validators = [ + (self.VALIDATORS[k], k, v) + for k, v in applicable_validators(self.schema) + if k in self.VALIDATORS + ] + + # REMOVEME: Legacy ref resolution state management. + push_scope = getattr(self._ref_resolver, "push_scope", None) + if push_scope is not None: + id = id_of(self.schema) + if id is not None: + push_scope(id) + + @classmethod + def check_schema(cls, schema, format_checker=_UNSET): + Validator = validator_for(cls.META_SCHEMA, default=cls) + if format_checker is _UNSET: + format_checker = Validator.FORMAT_CHECKER + validator = Validator( + schema=cls.META_SCHEMA, + format_checker=format_checker, + ) + for error in validator.iter_errors(schema): + raise exceptions.SchemaError.create_from(error) + + @property + def resolver(self): + warnings.warn( + ( + f"Accessing {self.__class__.__name__}.resolver is " + "deprecated as of v4.18.0, in favor of the " + "https://github.com/python-jsonschema/referencing " + "library, which provides more compliant referencing " + "behavior as well as more flexible APIs for " + "customization." + ), + DeprecationWarning, + stacklevel=2, + ) + if self._ref_resolver is None: + self._ref_resolver = _RefResolver.from_schema( + self.schema, + id_of=id_of, + ) + return self._ref_resolver + + def evolve(self, **changes): + schema = changes.setdefault("schema", self.schema) + NewValidator = validator_for(schema, default=self.__class__) + + for (attr_name, init_name) in evolve_fields: + if init_name not in changes: + changes[init_name] = getattr(self, attr_name) + + return NewValidator(**changes) + + def iter_errors(self, instance, _schema=None): + if _schema is not None: + warnings.warn( + ( + "Passing a schema to Validator.iter_errors " + "is deprecated and will be removed in a future " + "release. Call validator.evolve(schema=new_schema)." + "iter_errors(...) instead." + ), + DeprecationWarning, + stacklevel=2, + ) + validators = [ + (self.VALIDATORS[k], k, v) + for k, v in applicable_validators(_schema) + if k in self.VALIDATORS + ] + else: + _schema, validators = self.schema, self._validators + + if _schema is True: + return + elif _schema is False: + yield exceptions.ValidationError( + f"False schema does not allow {instance!r}", + validator=None, + validator_value=None, + instance=instance, + schema=_schema, + ) + return + + for validator, k, v in validators: + errors = validator(self, v, instance, _schema) or () + for error in errors: + # set details if not already set by the called fn + error._set( + validator=k, + validator_value=v, + instance=instance, + schema=_schema, + type_checker=self.TYPE_CHECKER, + ) + if k not in {"if", "$ref"}: + error.schema_path.appendleft(k) + yield error + + def descend( + self, + instance, + schema, + path=None, + schema_path=None, + resolver=None, + ): + if schema is True: + return + elif schema is False: + yield exceptions.ValidationError( + f"False schema does not allow {instance!r}", + validator=None, + validator_value=None, + instance=instance, + schema=schema, + ) + return + + if self._ref_resolver is not None: + evolved = self.evolve(schema=schema) + else: + if resolver is None: + resolver = self._resolver.in_subresource( + specification.create_resource(schema), + ) + evolved = self.evolve(schema=schema, _resolver=resolver) + + for k, v in applicable_validators(schema): + validator = evolved.VALIDATORS.get(k) + if validator is None: + continue + + errors = validator(evolved, v, instance, schema) or () + for error in errors: + # set details if not already set by the called fn + error._set( + validator=k, + validator_value=v, + instance=instance, + schema=schema, + type_checker=evolved.TYPE_CHECKER, + ) + if k not in {"if", "$ref"}: + error.schema_path.appendleft(k) + if path is not None: + error.path.appendleft(path) + if schema_path is not None: + error.schema_path.appendleft(schema_path) + yield error + + def validate(self, *args, **kwargs): + for error in self.iter_errors(*args, **kwargs): + raise error + + def is_type(self, instance, type): + try: + return self.TYPE_CHECKER.is_type(instance, type) + except exceptions.UndefinedTypeCheck: + exc = exceptions.UnknownType(type, instance, self.schema) + raise exc from None + + def _validate_reference(self, ref, instance): + if self._ref_resolver is None: + try: + resolved = self._resolver.lookup(ref) + except referencing.exceptions.Unresolvable as err: + raise exceptions._WrappedReferencingError(err) from err + + return self.descend( + instance, + resolved.contents, + resolver=resolved.resolver, + ) + else: + resolve = getattr(self._ref_resolver, "resolve", None) + if resolve is None: + with self._ref_resolver.resolving(ref) as resolved: + return self.descend(instance, resolved) + else: + scope, resolved = resolve(ref) + self._ref_resolver.push_scope(scope) + + try: + return list(self.descend(instance, resolved)) + finally: + self._ref_resolver.pop_scope() + + def is_valid(self, instance, _schema=None): + if _schema is not None: + warnings.warn( + ( + "Passing a schema to Validator.is_valid is deprecated " + "and will be removed in a future release. Call " + "validator.evolve(schema=new_schema).is_valid(...) " + "instead." + ), + DeprecationWarning, + stacklevel=2, + ) + self = self.evolve(schema=_schema) + + error = next(self.iter_errors(instance), None) + return error is None + + evolve_fields = [ + (field.name, field.alias) + for field in fields(Validator) + if field.init + ] + + if version is not None: + safe = version.title().replace(" ", "").replace("-", "") + Validator.__name__ = Validator.__qualname__ = f"{safe}Validator" + Validator = validates(version)(Validator) # type: ignore[misc] + + return Validator # type: ignore[return-value] + + +def extend( + validator, + validators=(), + version=None, + type_checker=None, + format_checker=None, +): + """ + Create a new validator class by extending an existing one. + + Arguments: + + validator (jsonschema.protocols.Validator): + + an existing validator class + + validators (collections.abc.Mapping): + + a mapping of new validator callables to extend with, whose + structure is as in `create`. + + .. note:: + + Any validator callables with the same name as an + existing one will (silently) replace the old validator + callable entirely, effectively overriding any validation + done in the "parent" validator class. + + If you wish to instead extend the behavior of a parent's + validator callable, delegate and call it directly in + the new validator function by retrieving it using + ``OldValidator.VALIDATORS["validation_keyword_name"]``. + + version (str): + + a version for the new validator class + + type_checker (jsonschema.TypeChecker): + + a type checker, used when applying the :kw:`type` keyword. + + If unprovided, the type checker of the extended + `jsonschema.protocols.Validator` will be carried along. + + format_checker (jsonschema.FormatChecker): + + a format checker, used when applying the :kw:`format` keyword. + + If unprovided, the format checker of the extended + `jsonschema.protocols.Validator` will be carried along. + + Returns: + + a new `jsonschema.protocols.Validator` class extending the one + provided + + .. note:: Meta Schemas + + The new validator class will have its parent's meta schema. + + If you wish to change or extend the meta schema in the new + validator class, modify ``META_SCHEMA`` directly on the returned + class. Note that no implicit copying is done, so a copy should + likely be made before modifying it, in order to not affect the + old validator. + + """ + all_validators = dict(validator.VALIDATORS) + all_validators.update(validators) + + if type_checker is None: + type_checker = validator.TYPE_CHECKER + if format_checker is None: + format_checker = validator.FORMAT_CHECKER + return create( + meta_schema=validator.META_SCHEMA, + validators=all_validators, + version=version, + type_checker=type_checker, + format_checker=format_checker, + id_of=validator.ID_OF, + applicable_validators=validator._APPLICABLE_VALIDATORS, + ) + + +Draft3Validator = create( + meta_schema=SPECIFICATIONS.contents( + "http://json-schema.org/draft-03/schema#", + ), + validators={ + "$ref": _keywords.ref, + "additionalItems": _legacy_keywords.additionalItems, + "additionalProperties": _keywords.additionalProperties, + "dependencies": _legacy_keywords.dependencies_draft3, + "disallow": _legacy_keywords.disallow_draft3, + "divisibleBy": _keywords.multipleOf, + "enum": _keywords.enum, + "extends": _legacy_keywords.extends_draft3, + "format": _keywords.format, + "items": _legacy_keywords.items_draft3_draft4, + "maxItems": _keywords.maxItems, + "maxLength": _keywords.maxLength, + "maximum": _legacy_keywords.maximum_draft3_draft4, + "minItems": _keywords.minItems, + "minLength": _keywords.minLength, + "minimum": _legacy_keywords.minimum_draft3_draft4, + "pattern": _keywords.pattern, + "patternProperties": _keywords.patternProperties, + "properties": _legacy_keywords.properties_draft3, + "type": _legacy_keywords.type_draft3, + "uniqueItems": _keywords.uniqueItems, + }, + type_checker=_types.draft3_type_checker, + format_checker=_format.draft3_format_checker, + version="draft3", + id_of=referencing.jsonschema.DRAFT3.id_of, + applicable_validators=_legacy_keywords.ignore_ref_siblings, +) + +Draft4Validator = create( + meta_schema=SPECIFICATIONS.contents( + "http://json-schema.org/draft-04/schema#", + ), + validators={ + "$ref": _keywords.ref, + "additionalItems": _legacy_keywords.additionalItems, + "additionalProperties": _keywords.additionalProperties, + "allOf": _keywords.allOf, + "anyOf": _keywords.anyOf, + "dependencies": _legacy_keywords.dependencies_draft4_draft6_draft7, + "enum": _keywords.enum, + "format": _keywords.format, + "items": _legacy_keywords.items_draft3_draft4, + "maxItems": _keywords.maxItems, + "maxLength": _keywords.maxLength, + "maxProperties": _keywords.maxProperties, + "maximum": _legacy_keywords.maximum_draft3_draft4, + "minItems": _keywords.minItems, + "minLength": _keywords.minLength, + "minProperties": _keywords.minProperties, + "minimum": _legacy_keywords.minimum_draft3_draft4, + "multipleOf": _keywords.multipleOf, + "not": _keywords.not_, + "oneOf": _keywords.oneOf, + "pattern": _keywords.pattern, + "patternProperties": _keywords.patternProperties, + "properties": _keywords.properties, + "required": _keywords.required, + "type": _keywords.type, + "uniqueItems": _keywords.uniqueItems, + }, + type_checker=_types.draft4_type_checker, + format_checker=_format.draft4_format_checker, + version="draft4", + id_of=referencing.jsonschema.DRAFT4.id_of, + applicable_validators=_legacy_keywords.ignore_ref_siblings, +) + +Draft6Validator = create( + meta_schema=SPECIFICATIONS.contents( + "http://json-schema.org/draft-06/schema#", + ), + validators={ + "$ref": _keywords.ref, + "additionalItems": _legacy_keywords.additionalItems, + "additionalProperties": _keywords.additionalProperties, + "allOf": _keywords.allOf, + "anyOf": _keywords.anyOf, + "const": _keywords.const, + "contains": _legacy_keywords.contains_draft6_draft7, + "dependencies": _legacy_keywords.dependencies_draft4_draft6_draft7, + "enum": _keywords.enum, + "exclusiveMaximum": _keywords.exclusiveMaximum, + "exclusiveMinimum": _keywords.exclusiveMinimum, + "format": _keywords.format, + "items": _legacy_keywords.items_draft6_draft7_draft201909, + "maxItems": _keywords.maxItems, + "maxLength": _keywords.maxLength, + "maxProperties": _keywords.maxProperties, + "maximum": _keywords.maximum, + "minItems": _keywords.minItems, + "minLength": _keywords.minLength, + "minProperties": _keywords.minProperties, + "minimum": _keywords.minimum, + "multipleOf": _keywords.multipleOf, + "not": _keywords.not_, + "oneOf": _keywords.oneOf, + "pattern": _keywords.pattern, + "patternProperties": _keywords.patternProperties, + "properties": _keywords.properties, + "propertyNames": _keywords.propertyNames, + "required": _keywords.required, + "type": _keywords.type, + "uniqueItems": _keywords.uniqueItems, + }, + type_checker=_types.draft6_type_checker, + format_checker=_format.draft6_format_checker, + version="draft6", + id_of=referencing.jsonschema.DRAFT6.id_of, + applicable_validators=_legacy_keywords.ignore_ref_siblings, +) + +Draft7Validator = create( + meta_schema=SPECIFICATIONS.contents( + "http://json-schema.org/draft-07/schema#", + ), + validators={ + "$ref": _keywords.ref, + "additionalItems": _legacy_keywords.additionalItems, + "additionalProperties": _keywords.additionalProperties, + "allOf": _keywords.allOf, + "anyOf": _keywords.anyOf, + "const": _keywords.const, + "contains": _legacy_keywords.contains_draft6_draft7, + "dependencies": _legacy_keywords.dependencies_draft4_draft6_draft7, + "enum": _keywords.enum, + "exclusiveMaximum": _keywords.exclusiveMaximum, + "exclusiveMinimum": _keywords.exclusiveMinimum, + "format": _keywords.format, + "if": _keywords.if_, + "items": _legacy_keywords.items_draft6_draft7_draft201909, + "maxItems": _keywords.maxItems, + "maxLength": _keywords.maxLength, + "maxProperties": _keywords.maxProperties, + "maximum": _keywords.maximum, + "minItems": _keywords.minItems, + "minLength": _keywords.minLength, + "minProperties": _keywords.minProperties, + "minimum": _keywords.minimum, + "multipleOf": _keywords.multipleOf, + "not": _keywords.not_, + "oneOf": _keywords.oneOf, + "pattern": _keywords.pattern, + "patternProperties": _keywords.patternProperties, + "properties": _keywords.properties, + "propertyNames": _keywords.propertyNames, + "required": _keywords.required, + "type": _keywords.type, + "uniqueItems": _keywords.uniqueItems, + }, + type_checker=_types.draft7_type_checker, + format_checker=_format.draft7_format_checker, + version="draft7", + id_of=referencing.jsonschema.DRAFT7.id_of, + applicable_validators=_legacy_keywords.ignore_ref_siblings, +) + +Draft201909Validator = create( + meta_schema=SPECIFICATIONS.contents( + "https://json-schema.org/draft/2019-09/schema", + ), + validators={ + "$recursiveRef": _legacy_keywords.recursiveRef, + "$ref": _keywords.ref, + "additionalItems": _legacy_keywords.additionalItems, + "additionalProperties": _keywords.additionalProperties, + "allOf": _keywords.allOf, + "anyOf": _keywords.anyOf, + "const": _keywords.const, + "contains": _keywords.contains, + "dependentRequired": _keywords.dependentRequired, + "dependentSchemas": _keywords.dependentSchemas, + "enum": _keywords.enum, + "exclusiveMaximum": _keywords.exclusiveMaximum, + "exclusiveMinimum": _keywords.exclusiveMinimum, + "format": _keywords.format, + "if": _keywords.if_, + "items": _legacy_keywords.items_draft6_draft7_draft201909, + "maxItems": _keywords.maxItems, + "maxLength": _keywords.maxLength, + "maxProperties": _keywords.maxProperties, + "maximum": _keywords.maximum, + "minItems": _keywords.minItems, + "minLength": _keywords.minLength, + "minProperties": _keywords.minProperties, + "minimum": _keywords.minimum, + "multipleOf": _keywords.multipleOf, + "not": _keywords.not_, + "oneOf": _keywords.oneOf, + "pattern": _keywords.pattern, + "patternProperties": _keywords.patternProperties, + "properties": _keywords.properties, + "propertyNames": _keywords.propertyNames, + "required": _keywords.required, + "type": _keywords.type, + "unevaluatedItems": _legacy_keywords.unevaluatedItems_draft2019, + "unevaluatedProperties": ( + _legacy_keywords.unevaluatedProperties_draft2019 + ), + "uniqueItems": _keywords.uniqueItems, + }, + type_checker=_types.draft201909_type_checker, + format_checker=_format.draft201909_format_checker, + version="draft2019-09", +) + +Draft202012Validator = create( + meta_schema=SPECIFICATIONS.contents( + "https://json-schema.org/draft/2020-12/schema", + ), + validators={ + "$dynamicRef": _keywords.dynamicRef, + "$ref": _keywords.ref, + "additionalProperties": _keywords.additionalProperties, + "allOf": _keywords.allOf, + "anyOf": _keywords.anyOf, + "const": _keywords.const, + "contains": _keywords.contains, + "dependentRequired": _keywords.dependentRequired, + "dependentSchemas": _keywords.dependentSchemas, + "enum": _keywords.enum, + "exclusiveMaximum": _keywords.exclusiveMaximum, + "exclusiveMinimum": _keywords.exclusiveMinimum, + "format": _keywords.format, + "if": _keywords.if_, + "items": _keywords.items, + "maxItems": _keywords.maxItems, + "maxLength": _keywords.maxLength, + "maxProperties": _keywords.maxProperties, + "maximum": _keywords.maximum, + "minItems": _keywords.minItems, + "minLength": _keywords.minLength, + "minProperties": _keywords.minProperties, + "minimum": _keywords.minimum, + "multipleOf": _keywords.multipleOf, + "not": _keywords.not_, + "oneOf": _keywords.oneOf, + "pattern": _keywords.pattern, + "patternProperties": _keywords.patternProperties, + "prefixItems": _keywords.prefixItems, + "properties": _keywords.properties, + "propertyNames": _keywords.propertyNames, + "required": _keywords.required, + "type": _keywords.type, + "unevaluatedItems": _keywords.unevaluatedItems, + "unevaluatedProperties": _keywords.unevaluatedProperties, + "uniqueItems": _keywords.uniqueItems, + }, + type_checker=_types.draft202012_type_checker, + format_checker=_format.draft202012_format_checker, + version="draft2020-12", +) + +_LATEST_VERSION: type[Validator] = Draft202012Validator + + +class _RefResolver: + """ + Resolve JSON References. + + Arguments: + + base_uri (str): + + The URI of the referring document + + referrer: + + The actual referring document + + store (dict): + + A mapping from URIs to documents to cache + + cache_remote (bool): + + Whether remote refs should be cached after first resolution + + handlers (dict): + + A mapping from URI schemes to functions that should be used + to retrieve them + + urljoin_cache (:func:`functools.lru_cache`): + + A cache that will be used for caching the results of joining + the resolution scope to subscopes. + + remote_cache (:func:`functools.lru_cache`): + + A cache that will be used for caching the results of + resolved remote URLs. + + Attributes: + + cache_remote (bool): + + Whether remote refs should be cached after first resolution + + .. deprecated:: v4.18.0 + + ``RefResolver`` has been deprecated in favor of `referencing`. + + """ + + _DEPRECATION_MESSAGE = ( + "jsonschema.RefResolver is deprecated as of v4.18.0, in favor of the " + "https://github.com/python-jsonschema/referencing library, which " + "provides more compliant referencing behavior as well as more " + "flexible APIs for customization. A future release will remove " + "RefResolver. Please file a feature request (on referencing) if you " + "are missing an API for the kind of customization you need." + ) + + def __init__( + self, + base_uri, + referrer, + store=HashTrieMap(), + cache_remote=True, + handlers=(), + urljoin_cache=None, + remote_cache=None, + ): + if urljoin_cache is None: + urljoin_cache = lru_cache(1024)(urljoin) + if remote_cache is None: + remote_cache = lru_cache(1024)(self.resolve_from_url) + + self.referrer = referrer + self.cache_remote = cache_remote + self.handlers = dict(handlers) + + self._scopes_stack = [base_uri] + + self.store = _utils.URIDict( + (uri, each.contents) for uri, each in SPECIFICATIONS.items() + ) + self.store.update( + (id, each.META_SCHEMA) for id, each in _META_SCHEMAS.items() + ) + self.store.update(store) + self.store.update( + (schema["$id"], schema) + for schema in store.values() + if isinstance(schema, Mapping) and "$id" in schema + ) + self.store[base_uri] = referrer + + self._urljoin_cache = urljoin_cache + self._remote_cache = remote_cache + + @classmethod + def from_schema( # noqa: D417 + cls, + schema, + id_of=referencing.jsonschema.DRAFT202012.id_of, + *args, + **kwargs, + ): + """ + Construct a resolver from a JSON schema object. + + Arguments: + + schema: + + the referring schema + + Returns: + + `_RefResolver` + + """ + return cls(base_uri=id_of(schema) or "", referrer=schema, *args, **kwargs) # noqa: B026, E501 + + def push_scope(self, scope): + """ + Enter a given sub-scope. + + Treats further dereferences as being performed underneath the + given scope. + """ + self._scopes_stack.append( + self._urljoin_cache(self.resolution_scope, scope), + ) + + def pop_scope(self): + """ + Exit the most recent entered scope. + + Treats further dereferences as being performed underneath the + original scope. + + Don't call this method more times than `push_scope` has been + called. + """ + try: + self._scopes_stack.pop() + except IndexError: + raise exceptions._RefResolutionError( + "Failed to pop the scope from an empty stack. " + "`pop_scope()` should only be called once for every " + "`push_scope()`", + ) from None + + @property + def resolution_scope(self): + """ + Retrieve the current resolution scope. + """ + return self._scopes_stack[-1] + + @property + def base_uri(self): + """ + Retrieve the current base URI, not including any fragment. + """ + uri, _ = urldefrag(self.resolution_scope) + return uri + + @contextlib.contextmanager + def in_scope(self, scope): + """ + Temporarily enter the given scope for the duration of the context. + + .. deprecated:: v4.0.0 + """ + warnings.warn( + "jsonschema.RefResolver.in_scope is deprecated and will be " + "removed in a future release.", + DeprecationWarning, + stacklevel=3, + ) + self.push_scope(scope) + try: + yield + finally: + self.pop_scope() + + @contextlib.contextmanager + def resolving(self, ref): + """ + Resolve the given ``ref`` and enter its resolution scope. + + Exits the scope on exit of this context manager. + + Arguments: + + ref (str): + + The reference to resolve + + """ + url, resolved = self.resolve(ref) + self.push_scope(url) + try: + yield resolved + finally: + self.pop_scope() + + def _find_in_referrer(self, key): + return self._get_subschemas_cache()[key] + + @lru_cache # noqa: B019 + def _get_subschemas_cache(self): + cache = {key: [] for key in _SUBSCHEMAS_KEYWORDS} + for keyword, subschema in _search_schema( + self.referrer, _match_subschema_keywords, + ): + cache[keyword].append(subschema) + return cache + + @lru_cache # noqa: B019 + def _find_in_subschemas(self, url): + subschemas = self._get_subschemas_cache()["$id"] + if not subschemas: + return None + uri, fragment = urldefrag(url) + for subschema in subschemas: + id = subschema["$id"] + if not isinstance(id, str): + continue + target_uri = self._urljoin_cache(self.resolution_scope, id) + if target_uri.rstrip("/") == uri.rstrip("/"): + if fragment: + subschema = self.resolve_fragment(subschema, fragment) + self.store[url] = subschema + return url, subschema + return None + + def resolve(self, ref): + """ + Resolve the given reference. + """ + url = self._urljoin_cache(self.resolution_scope, ref).rstrip("/") + + match = self._find_in_subschemas(url) + if match is not None: + return match + + return url, self._remote_cache(url) + + def resolve_from_url(self, url): + """ + Resolve the given URL. + """ + url, fragment = urldefrag(url) + if not url: + url = self.base_uri + + try: + document = self.store[url] + except KeyError: + try: + document = self.resolve_remote(url) + except Exception as exc: + raise exceptions._RefResolutionError(exc) from exc + + return self.resolve_fragment(document, fragment) + + def resolve_fragment(self, document, fragment): + """ + Resolve a ``fragment`` within the referenced ``document``. + + Arguments: + + document: + + The referent document + + fragment (str): + + a URI fragment to resolve within it + + """ + fragment = fragment.lstrip("/") + + if not fragment: + return document + + if document is self.referrer: + find = self._find_in_referrer + else: + + def find(key): + yield from _search_schema(document, _match_keyword(key)) + + for keyword in ["$anchor", "$dynamicAnchor"]: + for subschema in find(keyword): + if fragment == subschema[keyword]: + return subschema + for keyword in ["id", "$id"]: + for subschema in find(keyword): + if "#" + fragment == subschema[keyword]: + return subschema + + # Resolve via path + parts = unquote(fragment).split("/") if fragment else [] + for part in parts: + part = part.replace("~1", "/").replace("~0", "~") + + if isinstance(document, Sequence): + try: # noqa: SIM105 + part = int(part) + except ValueError: + pass + try: + document = document[part] + except (TypeError, LookupError) as err: + raise exceptions._RefResolutionError( + f"Unresolvable JSON pointer: {fragment!r}", + ) from err + + return document + + def resolve_remote(self, uri): + """ + Resolve a remote ``uri``. + + If called directly, does not check the store first, but after + retrieving the document at the specified URI it will be saved in + the store if :attr:`cache_remote` is True. + + .. note:: + + If the requests_ library is present, ``jsonschema`` will use it to + request the remote ``uri``, so that the correct encoding is + detected and used. + + If it isn't, or if the scheme of the ``uri`` is not ``http`` or + ``https``, UTF-8 is assumed. + + Arguments: + + uri (str): + + The URI to resolve + + Returns: + + The retrieved document + + .. _requests: https://pypi.org/project/requests/ + + """ + try: + import requests + except ImportError: + requests = None + + scheme = urlsplit(uri).scheme + + if scheme in self.handlers: + result = self.handlers[scheme](uri) + elif scheme in ["http", "https"] and requests: + # Requests has support for detecting the correct encoding of + # json over http + result = requests.get(uri).json() + else: + # Otherwise, pass off to urllib and assume utf-8 + from urllib.request import urlopen + with urlopen(uri) as url: # noqa: S310 + result = json.loads(url.read().decode("utf-8")) + + if self.cache_remote: + self.store[uri] = result + return result + + +_SUBSCHEMAS_KEYWORDS = ("$id", "id", "$anchor", "$dynamicAnchor") + + +def _match_keyword(keyword): + + def matcher(value): + if keyword in value: + yield value + + return matcher + + +def _match_subschema_keywords(value): + for keyword in _SUBSCHEMAS_KEYWORDS: + if keyword in value: + yield keyword, value + + +def _search_schema(schema, matcher): + """Breadth-first search routine.""" + values = deque([schema]) + while values: + value = values.pop() + if not isinstance(value, dict): + continue + yield from matcher(value) + values.extendleft(value.values()) + + +def validate(instance, schema, cls=None, *args, **kwargs): # noqa: D417 + """ + Validate an instance under the given schema. + + >>> validate([2, 3, 4], {"maxItems": 2}) + Traceback (most recent call last): + ... + ValidationError: [2, 3, 4] is too long + + :func:`~jsonschema.validators.validate` will first verify that the + provided schema is itself valid, since not doing so can lead to less + obvious error messages and fail in less obvious or consistent ways. + + If you know you have a valid schema already, especially + if you intend to validate multiple instances with + the same schema, you likely would prefer using the + `jsonschema.protocols.Validator.validate` method directly on a + specific validator (e.g. ``Draft202012Validator.validate``). + + + Arguments: + + instance: + + The instance to validate + + schema: + + The schema to validate with + + cls (jsonschema.protocols.Validator): + + The class that will be used to validate the instance. + + If the ``cls`` argument is not provided, two things will happen + in accordance with the specification. First, if the schema has a + :kw:`$schema` keyword containing a known meta-schema [#]_ then the + proper validator will be used. The specification recommends that + all schemas contain :kw:`$schema` properties for this reason. If no + :kw:`$schema` property is found, the default validator class is the + latest released draft. + + Any other provided positional and keyword arguments will be passed + on when instantiating the ``cls``. + + Raises: + + `jsonschema.exceptions.ValidationError`: + + if the instance is invalid + + `jsonschema.exceptions.SchemaError`: + + if the schema itself is invalid + + .. rubric:: Footnotes + .. [#] known by a validator registered with + `jsonschema.validators.validates` + + """ + if cls is None: + cls = validator_for(schema) + + cls.check_schema(schema) + validator = cls(schema, *args, **kwargs) + error = exceptions.best_match(validator.iter_errors(instance)) + if error is not None: + raise error + + +def validator_for( + schema, + default: type[Validator] | _utils.Unset = _UNSET, +) -> type[Validator]: + """ + Retrieve the validator class appropriate for validating the given schema. + + Uses the :kw:`$schema` keyword that should be present in the given + schema to look up the appropriate validator class. + + Arguments: + + schema (collections.abc.Mapping or bool): + + the schema to look at + + default: + + the default to return if the appropriate validator class + cannot be determined. + + If unprovided, the default is to return the latest supported + draft. + + Examples: + + The :kw:`$schema` JSON Schema keyword will control which validator + class is returned: + + >>> schema = { + ... "$schema": "https://json-schema.org/draft/2020-12/schema", + ... "type": "integer", + ... } + >>> jsonschema.validators.validator_for(schema) + + + + Here, a draft 7 schema instead will return the draft 7 validator: + + >>> schema = { + ... "$schema": "http://json-schema.org/draft-07/schema#", + ... "type": "integer", + ... } + >>> jsonschema.validators.validator_for(schema) + + + + Schemas with no ``$schema`` keyword will fallback to the default + argument: + + >>> schema = {"type": "integer"} + >>> jsonschema.validators.validator_for( + ... schema, default=Draft7Validator, + ... ) + + + or if none is provided, to the latest version supported. + Always including the keyword when authoring schemas is highly + recommended. + + """ + DefaultValidator = _LATEST_VERSION if default is _UNSET else default + + if schema is True or schema is False or "$schema" not in schema: + return DefaultValidator # type: ignore[return-value] + if schema["$schema"] not in _META_SCHEMAS and default is _UNSET: + warn( + ( + "The metaschema specified by $schema was not found. " + "Using the latest draft to validate, but this will raise " + "an error in the future." + ), + DeprecationWarning, + stacklevel=2, + ) + return _META_SCHEMAS.get(schema["$schema"], DefaultValidator) diff --git a/lib/python3.12/site-packages/mpmath-1.3.0.dist-info/INSTALLER b/lib/python3.12/site-packages/mpmath-1.3.0.dist-info/INSTALLER new file mode 100644 index 0000000000000000000000000000000000000000..a1b589e38a32041e49332e5e81c2d363dc418d68 --- /dev/null +++ b/lib/python3.12/site-packages/mpmath-1.3.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/lib/python3.12/site-packages/mpmath-1.3.0.dist-info/LICENSE b/lib/python3.12/site-packages/mpmath-1.3.0.dist-info/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..9ecdc7586d08805bc984539f6672476e86e538b6 --- /dev/null +++ b/lib/python3.12/site-packages/mpmath-1.3.0.dist-info/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2005-2021 Fredrik Johansson and mpmath contributors + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + a. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + b. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + c. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. diff --git a/lib/python3.12/site-packages/mpmath-1.3.0.dist-info/METADATA b/lib/python3.12/site-packages/mpmath-1.3.0.dist-info/METADATA new file mode 100644 index 0000000000000000000000000000000000000000..994b48acdba5cd0fdfb28cd1fbb0a84ebf81cba5 --- /dev/null +++ b/lib/python3.12/site-packages/mpmath-1.3.0.dist-info/METADATA @@ -0,0 +1,233 @@ +Metadata-Version: 2.1 +Name: mpmath +Version: 1.3.0 +Summary: Python library for arbitrary-precision floating-point arithmetic +Home-page: http://mpmath.org/ +Author: Fredrik Johansson +Author-email: fredrik.johansson@gmail.com +License: BSD +Project-URL: Source, https://github.com/fredrik-johansson/mpmath +Project-URL: Tracker, https://github.com/fredrik-johansson/mpmath/issues +Project-URL: Documentation, http://mpmath.org/doc/current/ +Classifier: License :: OSI Approved :: BSD License +Classifier: Topic :: Scientific/Engineering :: Mathematics +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +License-File: LICENSE +Provides-Extra: develop +Requires-Dist: pytest (>=4.6) ; extra == 'develop' +Requires-Dist: pycodestyle ; extra == 'develop' +Requires-Dist: pytest-cov ; extra == 'develop' +Requires-Dist: codecov ; extra == 'develop' +Requires-Dist: wheel ; extra == 'develop' +Provides-Extra: docs +Requires-Dist: sphinx ; extra == 'docs' +Provides-Extra: gmpy +Requires-Dist: gmpy2 (>=2.1.0a4) ; (platform_python_implementation != "PyPy") and extra == 'gmpy' +Provides-Extra: tests +Requires-Dist: pytest (>=4.6) ; extra == 'tests' + +mpmath +====== + +|pypi version| |Build status| |Code coverage status| |Zenodo Badge| + +.. |pypi version| image:: https://img.shields.io/pypi/v/mpmath.svg + :target: https://pypi.python.org/pypi/mpmath +.. |Build status| image:: https://github.com/fredrik-johansson/mpmath/workflows/test/badge.svg + :target: https://github.com/fredrik-johansson/mpmath/actions?workflow=test +.. |Code coverage status| image:: https://codecov.io/gh/fredrik-johansson/mpmath/branch/master/graph/badge.svg + :target: https://codecov.io/gh/fredrik-johansson/mpmath +.. |Zenodo Badge| image:: https://zenodo.org/badge/2934512.svg + :target: https://zenodo.org/badge/latestdoi/2934512 + +A Python library for arbitrary-precision floating-point arithmetic. + +Website: http://mpmath.org/ +Main author: Fredrik Johansson + +Mpmath is free software released under the New BSD License (see the +LICENSE file for details) + +0. History and credits +---------------------- + +The following people (among others) have contributed major patches +or new features to mpmath: + +* Pearu Peterson +* Mario Pernici +* Ondrej Certik +* Vinzent Steinberg +* Nimish Telang +* Mike Taschuk +* Case Van Horsen +* Jorn Baayen +* Chris Smith +* Juan Arias de Reyna +* Ioannis Tziakos +* Aaron Meurer +* Stefan Krastanov +* Ken Allen +* Timo Hartmann +* Sergey B Kirpichev +* Kris Kuhlman +* Paul Masson +* Michael Kagalenko +* Jonathan Warner +* Max Gaukler +* Guillermo Navas-Palencia +* Nike Dattani + +Numerous other people have contributed by reporting bugs, +requesting new features, or suggesting improvements to the +documentation. + +For a detailed changelog, including individual contributions, +see the CHANGES file. + +Fredrik's work on mpmath during summer 2008 was sponsored by Google +as part of the Google Summer of Code program. + +Fredrik's work on mpmath during summer 2009 was sponsored by the +American Institute of Mathematics under the support of the National Science +Foundation Grant No. 0757627 (FRG: L-functions and Modular Forms). + +Any opinions, findings, and conclusions or recommendations expressed in this +material are those of the author(s) and do not necessarily reflect the +views of the sponsors. + +Credit also goes to: + +* The authors of the GMP library and the Python wrapper + gmpy, enabling mpmath to become much faster at + high precision +* The authors of MPFR, pari/gp, MPFUN, and other arbitrary- + precision libraries, whose documentation has been helpful + for implementing many of the algorithms in mpmath +* Wikipedia contributors; Abramowitz & Stegun; Gradshteyn & Ryzhik; + Wolfram Research for MathWorld and the Wolfram Functions site. + These are the main references used for special functions + implementations. +* George Brandl for developing the Sphinx documentation tool + used to build mpmath's documentation + +Release history: + +* Version 1.3.0 released on March 7, 2023 +* Version 1.2.0 released on February 1, 2021 +* Version 1.1.0 released on December 11, 2018 +* Version 1.0.0 released on September 27, 2017 +* Version 0.19 released on June 10, 2014 +* Version 0.18 released on December 31, 2013 +* Version 0.17 released on February 1, 2011 +* Version 0.16 released on September 24, 2010 +* Version 0.15 released on June 6, 2010 +* Version 0.14 released on February 5, 2010 +* Version 0.13 released on August 13, 2009 +* Version 0.12 released on June 9, 2009 +* Version 0.11 released on January 26, 2009 +* Version 0.10 released on October 15, 2008 +* Version 0.9 released on August 23, 2008 +* Version 0.8 released on April 20, 2008 +* Version 0.7 released on March 12, 2008 +* Version 0.6 released on January 13, 2008 +* Version 0.5 released on November 24, 2007 +* Version 0.4 released on November 3, 2007 +* Version 0.3 released on October 5, 2007 +* Version 0.2 released on October 2, 2007 +* Version 0.1 released on September 27, 2007 + +1. Download & installation +-------------------------- + +Mpmath requires Python 2.7 or 3.5 (or later versions). It has been tested +with CPython 2.7, 3.5 through 3.7 and for PyPy. + +The latest release of mpmath can be downloaded from the mpmath +website and from https://github.com/fredrik-johansson/mpmath/releases + +It should also be available in the Python Package Index at +https://pypi.python.org/pypi/mpmath + +To install latest release of Mpmath with pip, simply run + +``pip install mpmath`` + +Or unpack the mpmath archive and run + +``python setup.py install`` + +Mpmath can also be installed using + +``python -m easy_install mpmath`` + +The latest development code is available from +https://github.com/fredrik-johansson/mpmath + +See the main documentation for more detailed instructions. + +2. Running tests +---------------- + +The unit tests in mpmath/tests/ can be run via the script +runtests.py, but it is recommended to run them with py.test +(https://pytest.org/), especially +to generate more useful reports in case there are failures. + +You may also want to check out the demo scripts in the demo +directory. + +The master branch is automatically tested by Travis CI. + +3. Documentation +---------------- + +Documentation in reStructuredText format is available in the +doc directory included with the source package. These files +are human-readable, but can be compiled to prettier HTML using +the build.py script (requires Sphinx, http://sphinx.pocoo.org/). + +See setup.txt in the documentation for more information. + +The most recent documentation is also available in HTML format: + +http://mpmath.org/doc/current/ + +4. Known problems +----------------- + +Mpmath is a work in progress. Major issues include: + +* Some functions may return incorrect values when given extremely + large arguments or arguments very close to singularities. + +* Directed rounding works for arithmetic operations. It is implemented + heuristically for other operations, and their results may be off by one + or two units in the last place (even if otherwise accurate). + +* Some IEEE 754 features are not available. Inifinities and NaN are + partially supported; denormal rounding is currently not available + at all. + +* The interface for switching precision and rounding is not finalized. + The current method is not threadsafe. + +5. Help and bug reports +----------------------- + +General questions and comments can be sent to the mpmath mailinglist, +mpmath@googlegroups.com + +You can also report bugs and send patches to the mpmath issue tracker, +https://github.com/fredrik-johansson/mpmath/issues diff --git a/lib/python3.12/site-packages/mpmath-1.3.0.dist-info/RECORD b/lib/python3.12/site-packages/mpmath-1.3.0.dist-info/RECORD new file mode 100644 index 0000000000000000000000000000000000000000..79513be77d1067ef3596f8414cf6648423a7fc3d --- /dev/null +++ b/lib/python3.12/site-packages/mpmath-1.3.0.dist-info/RECORD @@ -0,0 +1,180 @@ +mpmath-1.3.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +mpmath-1.3.0.dist-info/LICENSE,sha256=wmyugdpFCOXiSZhXd6M4IfGDIj67dNf4z7-Q_n7vL7c,1537 +mpmath-1.3.0.dist-info/METADATA,sha256=RLZupES5wNGa6UgV01a_BHrmtoDBkmi1wmVofNaoFAY,8630 +mpmath-1.3.0.dist-info/RECORD,, +mpmath-1.3.0.dist-info/WHEEL,sha256=2wepM1nk4DS4eFpYrW1TTqPcoGNfHhhO_i5m4cOimbo,92 +mpmath-1.3.0.dist-info/top_level.txt,sha256=BUVWrh8EVlkOhM1n3X9S8msTaVcC-3s6Sjt60avHYus,7 +mpmath/__init__.py,sha256=skFYTSwfwDBLChAV6pI3SdewgAQR3UBtyrfIK_Jdn-g,8765 +mpmath/__pycache__/__init__.cpython-312.pyc,, +mpmath/__pycache__/ctx_base.cpython-312.pyc,, +mpmath/__pycache__/ctx_fp.cpython-312.pyc,, +mpmath/__pycache__/ctx_iv.cpython-312.pyc,, +mpmath/__pycache__/ctx_mp.cpython-312.pyc,, +mpmath/__pycache__/ctx_mp_python.cpython-312.pyc,, +mpmath/__pycache__/function_docs.cpython-312.pyc,, +mpmath/__pycache__/identification.cpython-312.pyc,, +mpmath/__pycache__/math2.cpython-312.pyc,, +mpmath/__pycache__/rational.cpython-312.pyc,, +mpmath/__pycache__/usertools.cpython-312.pyc,, +mpmath/__pycache__/visualization.cpython-312.pyc,, +mpmath/calculus/__init__.py,sha256=UAgCIJ1YmaeyTqpNzjBlCZGeIzLtUZMEEpl99VWNjus,162 +mpmath/calculus/__pycache__/__init__.cpython-312.pyc,, +mpmath/calculus/__pycache__/approximation.cpython-312.pyc,, +mpmath/calculus/__pycache__/calculus.cpython-312.pyc,, +mpmath/calculus/__pycache__/differentiation.cpython-312.pyc,, +mpmath/calculus/__pycache__/extrapolation.cpython-312.pyc,, +mpmath/calculus/__pycache__/inverselaplace.cpython-312.pyc,, +mpmath/calculus/__pycache__/odes.cpython-312.pyc,, +mpmath/calculus/__pycache__/optimization.cpython-312.pyc,, +mpmath/calculus/__pycache__/polynomials.cpython-312.pyc,, +mpmath/calculus/__pycache__/quadrature.cpython-312.pyc,, +mpmath/calculus/approximation.py,sha256=vyzu3YI6r63Oq1KFHrQz02mGXAcH23emqNYhJuUaFZ4,8817 +mpmath/calculus/calculus.py,sha256=A0gSp0hxSyEDfugJViY3CeWalF-vK701YftzrjSQzQ4,112 +mpmath/calculus/differentiation.py,sha256=2L6CBj8xtX9iip98NPbKsLtwtRjxi571wYmTMHFeL90,20226 +mpmath/calculus/extrapolation.py,sha256=xM0rvk2DFEF4iR1Jhl-Y3aS93iW9VVJX7y9IGpmzC-A,73306 +mpmath/calculus/inverselaplace.py,sha256=5-pn8N_t0PtgBTXixsXZ4xxrihK2J5gYsVfTKfDx4gA,36056 +mpmath/calculus/odes.py,sha256=gaHiw7IJjsONNTAa6izFPZpmcg9uyTp8MULnGdzTIGo,9908 +mpmath/calculus/optimization.py,sha256=bKnShXElBOmVOIOlFeksDsYCp9fYSmYwKmXDt0z26MM,32856 +mpmath/calculus/polynomials.py,sha256=D16BhU_SHbVi06IxNwABHR-H77IylndNsN3muPTuFYs,7877 +mpmath/calculus/quadrature.py,sha256=n-avtS8E43foV-5tr5lofgOBaiMUYE8AJjQcWI9QcKk,42432 +mpmath/ctx_base.py,sha256=rfjmfMyA55x8R_cWFINUwWVTElfZmyx5erKDdauSEVw,15985 +mpmath/ctx_fp.py,sha256=ctUjx_NoU0iFWk05cXDYCL2ZtLZOlWs1n6Zao3pbG2g,6572 +mpmath/ctx_iv.py,sha256=tqdMr-GDfkZk1EhoGeCAajy7pQv-RWtrVqhYjfI8r4g,17211 +mpmath/ctx_mp.py,sha256=d3r4t7xHNqSFtmqsA9Btq1Npy3WTM-pcM2_jeCyECxY,49452 +mpmath/ctx_mp_python.py,sha256=3olYWo4lk1SnQ0A_IaZ181qqG8u5pxGat_v-L4Qtn3Y,37815 +mpmath/function_docs.py,sha256=g4PP8n6ILXmHcLyA50sxK6Tmp_Z4_pRN-wDErU8D1i4,283512 +mpmath/functions/__init__.py,sha256=YXVdhqv-6LKm6cr5xxtTNTtuD9zDPKGQl8GmS0xz2xo,330 +mpmath/functions/__pycache__/__init__.cpython-312.pyc,, +mpmath/functions/__pycache__/bessel.cpython-312.pyc,, +mpmath/functions/__pycache__/elliptic.cpython-312.pyc,, +mpmath/functions/__pycache__/expintegrals.cpython-312.pyc,, +mpmath/functions/__pycache__/factorials.cpython-312.pyc,, +mpmath/functions/__pycache__/functions.cpython-312.pyc,, +mpmath/functions/__pycache__/hypergeometric.cpython-312.pyc,, +mpmath/functions/__pycache__/orthogonal.cpython-312.pyc,, +mpmath/functions/__pycache__/qfunctions.cpython-312.pyc,, +mpmath/functions/__pycache__/rszeta.cpython-312.pyc,, +mpmath/functions/__pycache__/signals.cpython-312.pyc,, +mpmath/functions/__pycache__/theta.cpython-312.pyc,, +mpmath/functions/__pycache__/zeta.cpython-312.pyc,, +mpmath/functions/__pycache__/zetazeros.cpython-312.pyc,, +mpmath/functions/bessel.py,sha256=dUPLu8frlK-vmf3-irX_7uvwyw4xccv6EIizmIZ88kM,37938 +mpmath/functions/elliptic.py,sha256=qz0yVMb4lWEeOTDL_DWz5u5awmGIPKAsuZFJXgwHJNU,42237 +mpmath/functions/expintegrals.py,sha256=75X_MRdYc1F_X73bgNiOJqwRlS2hqAzcFLl3RM2tCDc,11644 +mpmath/functions/factorials.py,sha256=8_6kCR7e4k1GwxiAOJu0NRadeF4jA28qx4hidhu4ILk,5273 +mpmath/functions/functions.py,sha256=ub2JExvqzCWLkm5yAm72Fr6fdWmZZUknq9_3w9MEigI,18100 +mpmath/functions/hypergeometric.py,sha256=Z0OMAMC4ylK42n_SnamyFVnUx6zHLyCLCoJDSZ1JrHY,51570 +mpmath/functions/orthogonal.py,sha256=FabkxKfBoSseA5flWu1a3re-2BYaew9augqIsT8LaLw,16097 +mpmath/functions/qfunctions.py,sha256=a3EHGKQt_jMd4x9I772Jz-TGFnGY-arWqPvZGz9QSe0,7633 +mpmath/functions/rszeta.py,sha256=yuUVp4ilIyDmXyE3WTBxDDjwfEJNypJnbPS-xPH5How,46184 +mpmath/functions/signals.py,sha256=ELotwQaW1CDpv-eeJzOZ5c23NhfaZcj9_Gkb3psvS0Q,703 +mpmath/functions/theta.py,sha256=KggOocczoMG6_HMoal4oEP7iZ4SKOou9JFE-WzY2r3M,37320 +mpmath/functions/zeta.py,sha256=ue7JY7GXA0oX8q08sQJl2CSRrZ7kOt8HsftpVjnTwrE,36410 +mpmath/functions/zetazeros.py,sha256=uq6TVyZBcY2MLX7VSdVfn0TOkowBLM9fXtnySEwaNzw,30858 +mpmath/identification.py,sha256=7aMdngRAaeL_MafDUNbmEIlGQSklHDZ8pmPFt-OLgkw,29253 +mpmath/libmp/__init__.py,sha256=UCDjLZw4brbklaCmSixCcPdLdHkz8sF_-6F_wr0duAg,3790 +mpmath/libmp/__pycache__/__init__.cpython-312.pyc,, +mpmath/libmp/__pycache__/backend.cpython-312.pyc,, +mpmath/libmp/__pycache__/gammazeta.cpython-312.pyc,, +mpmath/libmp/__pycache__/libelefun.cpython-312.pyc,, +mpmath/libmp/__pycache__/libhyper.cpython-312.pyc,, +mpmath/libmp/__pycache__/libintmath.cpython-312.pyc,, +mpmath/libmp/__pycache__/libmpc.cpython-312.pyc,, +mpmath/libmp/__pycache__/libmpf.cpython-312.pyc,, +mpmath/libmp/__pycache__/libmpi.cpython-312.pyc,, +mpmath/libmp/backend.py,sha256=26A8pUkaGov26vrrFNQVyWJ5LDtK8sl3UHrYLecaTjA,3360 +mpmath/libmp/gammazeta.py,sha256=Xqdw6PMoswDaSca_sOs-IglRuk3fb8c9p43M_lbcrlc,71469 +mpmath/libmp/libelefun.py,sha256=joBZP4FOdxPfieWso1LPtSr6dHydpG_LQiF_bYQYWMg,43861 +mpmath/libmp/libhyper.py,sha256=J9fmdDF6u27EcssEWvBuVaAa3hFjPvPN1SgRgu1dEbc,36624 +mpmath/libmp/libintmath.py,sha256=aIRT0rkUZ_sdGQf3TNCLd-pBMvtQWjssbvFLfK7U0jc,16688 +mpmath/libmp/libmpc.py,sha256=KBndUjs5YVS32-Id3fflDfYgpdW1Prx6zfo8Ez5Qbrs,26875 +mpmath/libmp/libmpf.py,sha256=vpP0kNVkScbCVoZogJ4Watl4I7Ce0d4dzHVjfVe57so,45021 +mpmath/libmp/libmpi.py,sha256=u0I5Eiwkqa-4-dXETi5k7MuaxBeZbvCAPFtl93U9YF0,27622 +mpmath/math2.py,sha256=O5Dglg81SsW0wfHDUJcXOD8-cCaLvbVIvyw0sVmRbpI,18561 +mpmath/matrices/__init__.py,sha256=ETzGDciYbq9ftiKwaMbJ15EI-KNXHrzRb-ZHehhqFjs,94 +mpmath/matrices/__pycache__/__init__.cpython-312.pyc,, +mpmath/matrices/__pycache__/calculus.cpython-312.pyc,, +mpmath/matrices/__pycache__/eigen.cpython-312.pyc,, +mpmath/matrices/__pycache__/eigen_symmetric.cpython-312.pyc,, +mpmath/matrices/__pycache__/linalg.cpython-312.pyc,, +mpmath/matrices/__pycache__/matrices.cpython-312.pyc,, +mpmath/matrices/calculus.py,sha256=PNRq-p2nxgT-fzC54K2depi8ddhdx6Q86G8qpUiHeUY,18609 +mpmath/matrices/eigen.py,sha256=GbDXI3CixzEdXxr1G86uUWkAngAvd-05MmSQ-Tsu_5k,24394 +mpmath/matrices/eigen_symmetric.py,sha256=FPKPeQr1cGYw6Y6ea32a1YdEWQDLP6JlQHEA2WfNLYg,58534 +mpmath/matrices/linalg.py,sha256=04C3ijzMFom7ob5fXBCDfyPPdo3BIboIeE8x2A6vqF0,26958 +mpmath/matrices/matrices.py,sha256=o78Eq62EHQnxcsR0LBoWDEGREOoN4L2iDM1q3dQrw0o,32331 +mpmath/rational.py,sha256=64d56fvZXngYZT7nOAHeFRUX77eJ1A0R3rpfWBU-mSo,5976 +mpmath/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +mpmath/tests/__pycache__/__init__.cpython-312.pyc,, +mpmath/tests/__pycache__/extratest_gamma.cpython-312.pyc,, +mpmath/tests/__pycache__/extratest_zeta.cpython-312.pyc,, +mpmath/tests/__pycache__/runtests.cpython-312.pyc,, +mpmath/tests/__pycache__/test_basic_ops.cpython-312.pyc,, +mpmath/tests/__pycache__/test_bitwise.cpython-312.pyc,, +mpmath/tests/__pycache__/test_calculus.cpython-312.pyc,, +mpmath/tests/__pycache__/test_compatibility.cpython-312.pyc,, +mpmath/tests/__pycache__/test_convert.cpython-312.pyc,, +mpmath/tests/__pycache__/test_diff.cpython-312.pyc,, +mpmath/tests/__pycache__/test_division.cpython-312.pyc,, +mpmath/tests/__pycache__/test_eigen.cpython-312.pyc,, +mpmath/tests/__pycache__/test_eigen_symmetric.cpython-312.pyc,, +mpmath/tests/__pycache__/test_elliptic.cpython-312.pyc,, +mpmath/tests/__pycache__/test_fp.cpython-312.pyc,, +mpmath/tests/__pycache__/test_functions.cpython-312.pyc,, +mpmath/tests/__pycache__/test_functions2.cpython-312.pyc,, +mpmath/tests/__pycache__/test_gammazeta.cpython-312.pyc,, +mpmath/tests/__pycache__/test_hp.cpython-312.pyc,, +mpmath/tests/__pycache__/test_identify.cpython-312.pyc,, +mpmath/tests/__pycache__/test_interval.cpython-312.pyc,, +mpmath/tests/__pycache__/test_levin.cpython-312.pyc,, +mpmath/tests/__pycache__/test_linalg.cpython-312.pyc,, +mpmath/tests/__pycache__/test_matrices.cpython-312.pyc,, +mpmath/tests/__pycache__/test_mpmath.cpython-312.pyc,, +mpmath/tests/__pycache__/test_ode.cpython-312.pyc,, +mpmath/tests/__pycache__/test_pickle.cpython-312.pyc,, +mpmath/tests/__pycache__/test_power.cpython-312.pyc,, +mpmath/tests/__pycache__/test_quad.cpython-312.pyc,, +mpmath/tests/__pycache__/test_rootfinding.cpython-312.pyc,, +mpmath/tests/__pycache__/test_special.cpython-312.pyc,, +mpmath/tests/__pycache__/test_str.cpython-312.pyc,, +mpmath/tests/__pycache__/test_summation.cpython-312.pyc,, +mpmath/tests/__pycache__/test_trig.cpython-312.pyc,, +mpmath/tests/__pycache__/test_visualization.cpython-312.pyc,, +mpmath/tests/__pycache__/torture.cpython-312.pyc,, +mpmath/tests/extratest_gamma.py,sha256=xidhXUelILcxtiPGoTBHjqUOKIJzEaZ_v3nntGQyWZQ,7228 +mpmath/tests/extratest_zeta.py,sha256=sg10j9RhjBpV2EdUqyYhGV2ERWvM--EvwwGIz6HTmlw,1003 +mpmath/tests/runtests.py,sha256=7NUV82F3K_5AhU8mCLUFf5OibtT7uloFCwPyM3l71wM,5189 +mpmath/tests/test_basic_ops.py,sha256=dsB8DRG-GrPzBaZ-bIauYabaeqXbfqBo9SIP9BqcTSs,15348 +mpmath/tests/test_bitwise.py,sha256=-nLYhgQbhDza3SQM63BhktYntACagqMYx9ib3dPnTKM,7686 +mpmath/tests/test_calculus.py,sha256=4oxtNfMpO4RLLoOzrv7r9-h8BcqfBsJIE6UpsHe7c4w,9187 +mpmath/tests/test_compatibility.py,sha256=_t3ASZ3jhfAMnN1voWX7PDNIDzn-3PokkJGIdT1x7y0,2306 +mpmath/tests/test_convert.py,sha256=JPcDcTJIWh5prIxjx5DM1aNWgqlUoF2KpHvAgK3uHi4,8834 +mpmath/tests/test_diff.py,sha256=qjiF8NxQ8vueuZ5ZHGPQ-kjcj_I7Jh_fEdFtaA8DzEI,2466 +mpmath/tests/test_division.py,sha256=6lUeZfmaBWvvszdqlWLMHgXPjVsxvW1WZpd4-jFWCpU,5340 +mpmath/tests/test_eigen.py,sha256=2mnqVATGbsJkvSVHPpitfAk881twFfb3LsO3XikV9Hs,3905 +mpmath/tests/test_eigen_symmetric.py,sha256=v0VimCicIU2owASDMBaP-t-30uq-pXcsglt95KBtNO4,8778 +mpmath/tests/test_elliptic.py,sha256=Kjiwq9Bb6N_OOzzWewGQ1M_PMa7vRs42V0t90gloZxo,26225 +mpmath/tests/test_fp.py,sha256=AJo0FTyH4BuUnUsv176LD956om308KGYndy-b54KGxM,89997 +mpmath/tests/test_functions.py,sha256=b47VywdomoOX6KmMmz9-iv2IqVIydwKSuUw2pWlFHrY,30955 +mpmath/tests/test_functions2.py,sha256=vlw2RWhL1oTcifnOMDx1a_YzN96UgNNIE5STeKRv1HY,96990 +mpmath/tests/test_gammazeta.py,sha256=AB34O0DV7AlEf9Z4brnCadeQU5-uAwhWRw5FZas65DA,27917 +mpmath/tests/test_hp.py,sha256=6hcENu6Te2klPEiTSeLBIRPlH7PADlJwFKbx8xpnOhg,10461 +mpmath/tests/test_identify.py,sha256=lGUIPfrB2paTg0cFUo64GmMzF77F9gs9FQjX7gxGHV8,692 +mpmath/tests/test_interval.py,sha256=TjYd7a9ca6iRJiLjw06isLeZTuGoGAPmgleDZ0cYfJ0,17527 +mpmath/tests/test_levin.py,sha256=P8M11yV1dj_gdSNv5xuwCzFiF86QyRDtPMjURy6wJ28,5090 +mpmath/tests/test_linalg.py,sha256=miKEnwB8iwWV13hi1bF1cg3hgB4rTKOR0fvDVfWmXds,10440 +mpmath/tests/test_matrices.py,sha256=qyA4Ml2CvNvW034lzB01G6wVgNr7UrgZqh2wkMXtpzM,7944 +mpmath/tests/test_mpmath.py,sha256=LVyJUeofiaxW-zLKWVBCz59L9UQsjlW0Ts9_oBiEv_4,196 +mpmath/tests/test_ode.py,sha256=zAxexBH4fnmFNO4bvEHbug1NJWC5zqfFaVDlYijowkY,1822 +mpmath/tests/test_pickle.py,sha256=Y8CKmDLFsJHUqG8CDaBw5ilrPP4YT1xijVduLpQ7XFE,401 +mpmath/tests/test_power.py,sha256=sz_K02SmNxpa6Kb1uJLN_N4tXTJGdQ___vPRshEN7Gk,5227 +mpmath/tests/test_quad.py,sha256=49Ltft0vZ_kdKLL5s-Kj-BzAVoF5LPVEUeNUzdOkghI,3893 +mpmath/tests/test_rootfinding.py,sha256=umQegEaKHmYOEl5jEyoD-VLKDtXsTJJkepKEr4c0dC0,3132 +mpmath/tests/test_special.py,sha256=YbMIoMIkJEvvKYIzS0CXthJFG0--j6un7-tcE6b7FPM,2848 +mpmath/tests/test_str.py,sha256=0WsGD9hMPRi8zcuYMA9Cu2mOvQiCFskPwMsMf8lBDK4,544 +mpmath/tests/test_summation.py,sha256=fdNlsvRVOsbWxbhlyDLDaEO2S8kTJrRMKIvB5-aNci0,2035 +mpmath/tests/test_trig.py,sha256=zPtkIEnZaThxcWur4k7BX8-2Jmj-AhO191Svv7ANYUU,4799 +mpmath/tests/test_visualization.py,sha256=1PqtkoUx-WsKYgTRiu5o9pBc85kwhf1lzU2eobDQCJM,944 +mpmath/tests/torture.py,sha256=LD95oES7JY2KroELK-m-jhvtbvZaKChnt0Cq7kFMNCw,7868 +mpmath/usertools.py,sha256=a-TDw7XSRsPdBEffxOooDV4WDFfuXnO58P75dcAD87I,3029 +mpmath/visualization.py,sha256=pnnbjcd9AhFVRBZavYX5gjx4ytK_kXoDDisYR6EpXhs,10627 diff --git a/lib/python3.12/site-packages/mpmath-1.3.0.dist-info/WHEEL b/lib/python3.12/site-packages/mpmath-1.3.0.dist-info/WHEEL new file mode 100644 index 0000000000000000000000000000000000000000..57e3d840d59a650ac5bccbad5baeec47d155f0ad --- /dev/null +++ b/lib/python3.12/site-packages/mpmath-1.3.0.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.38.4) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/lib/python3.12/site-packages/mpmath-1.3.0.dist-info/top_level.txt b/lib/python3.12/site-packages/mpmath-1.3.0.dist-info/top_level.txt new file mode 100644 index 0000000000000000000000000000000000000000..dda7c273a8dd1c6adffa9d2d9901e0ce6876f4ac --- /dev/null +++ b/lib/python3.12/site-packages/mpmath-1.3.0.dist-info/top_level.txt @@ -0,0 +1 @@ +mpmath diff --git a/lib/python3.12/site-packages/mpmath/__init__.py b/lib/python3.12/site-packages/mpmath/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..46a7c6f7c0875548f264612b604a9e1574b00a84 --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/__init__.py @@ -0,0 +1,468 @@ +__version__ = '1.3.0' + +from .usertools import monitor, timing + +from .ctx_fp import FPContext +from .ctx_mp import MPContext +from .ctx_iv import MPIntervalContext + +fp = FPContext() +mp = MPContext() +iv = MPIntervalContext() + +fp._mp = mp +mp._mp = mp +iv._mp = mp +mp._fp = fp +fp._fp = fp +mp._iv = iv +fp._iv = iv +iv._iv = iv + +# XXX: extremely bad pickle hack +from . import ctx_mp as _ctx_mp +_ctx_mp._mpf_module.mpf = mp.mpf +_ctx_mp._mpf_module.mpc = mp.mpc + +make_mpf = mp.make_mpf +make_mpc = mp.make_mpc + +extraprec = mp.extraprec +extradps = mp.extradps +workprec = mp.workprec +workdps = mp.workdps +autoprec = mp.autoprec +maxcalls = mp.maxcalls +memoize = mp.memoize + +mag = mp.mag + +bernfrac = mp.bernfrac + +qfrom = mp.qfrom +mfrom = mp.mfrom +kfrom = mp.kfrom +taufrom = mp.taufrom +qbarfrom = mp.qbarfrom +ellipfun = mp.ellipfun +jtheta = mp.jtheta +kleinj = mp.kleinj +eta = mp.eta + +qp = mp.qp +qhyper = mp.qhyper +qgamma = mp.qgamma +qfac = mp.qfac + +nint_distance = mp.nint_distance + +plot = mp.plot +cplot = mp.cplot +splot = mp.splot + +odefun = mp.odefun + +jacobian = mp.jacobian +findroot = mp.findroot +multiplicity = mp.multiplicity + +isinf = mp.isinf +isnan = mp.isnan +isnormal = mp.isnormal +isint = mp.isint +isfinite = mp.isfinite +almosteq = mp.almosteq +nan = mp.nan +rand = mp.rand + +absmin = mp.absmin +absmax = mp.absmax + +fraction = mp.fraction + +linspace = mp.linspace +arange = mp.arange + +mpmathify = convert = mp.convert +mpc = mp.mpc + +mpi = iv._mpi + +nstr = mp.nstr +nprint = mp.nprint +chop = mp.chop + +fneg = mp.fneg +fadd = mp.fadd +fsub = mp.fsub +fmul = mp.fmul +fdiv = mp.fdiv +fprod = mp.fprod + +quad = mp.quad +quadgl = mp.quadgl +quadts = mp.quadts +quadosc = mp.quadosc +quadsubdiv = mp.quadsubdiv + +invertlaplace = mp.invertlaplace +invlaptalbot = mp.invlaptalbot +invlapstehfest = mp.invlapstehfest +invlapdehoog = mp.invlapdehoog + +pslq = mp.pslq +identify = mp.identify +findpoly = mp.findpoly + +richardson = mp.richardson +shanks = mp.shanks +levin = mp.levin +cohen_alt = mp.cohen_alt +nsum = mp.nsum +nprod = mp.nprod +difference = mp.difference +diff = mp.diff +diffs = mp.diffs +diffs_prod = mp.diffs_prod +diffs_exp = mp.diffs_exp +diffun = mp.diffun +differint = mp.differint +taylor = mp.taylor +pade = mp.pade +polyval = mp.polyval +polyroots = mp.polyroots +fourier = mp.fourier +fourierval = mp.fourierval +sumem = mp.sumem +sumap = mp.sumap +chebyfit = mp.chebyfit +limit = mp.limit + +matrix = mp.matrix +eye = mp.eye +diag = mp.diag +zeros = mp.zeros +ones = mp.ones +hilbert = mp.hilbert +randmatrix = mp.randmatrix +swap_row = mp.swap_row +extend = mp.extend +norm = mp.norm +mnorm = mp.mnorm + +lu_solve = mp.lu_solve +lu = mp.lu +qr = mp.qr +unitvector = mp.unitvector +inverse = mp.inverse +residual = mp.residual +qr_solve = mp.qr_solve +cholesky = mp.cholesky +cholesky_solve = mp.cholesky_solve +det = mp.det +cond = mp.cond +hessenberg = mp.hessenberg +schur = mp.schur +eig = mp.eig +eig_sort = mp.eig_sort +eigsy = mp.eigsy +eighe = mp.eighe +eigh = mp.eigh +svd_r = mp.svd_r +svd_c = mp.svd_c +svd = mp.svd +gauss_quadrature = mp.gauss_quadrature + +expm = mp.expm +sqrtm = mp.sqrtm +powm = mp.powm +logm = mp.logm +sinm = mp.sinm +cosm = mp.cosm + +mpf = mp.mpf +j = mp.j +exp = mp.exp +expj = mp.expj +expjpi = mp.expjpi +ln = mp.ln +im = mp.im +re = mp.re +inf = mp.inf +ninf = mp.ninf +sign = mp.sign + +eps = mp.eps +pi = mp.pi +ln2 = mp.ln2 +ln10 = mp.ln10 +phi = mp.phi +e = mp.e +euler = mp.euler +catalan = mp.catalan +khinchin = mp.khinchin +glaisher = mp.glaisher +apery = mp.apery +degree = mp.degree +twinprime = mp.twinprime +mertens = mp.mertens + +ldexp = mp.ldexp +frexp = mp.frexp + +fsum = mp.fsum +fdot = mp.fdot + +sqrt = mp.sqrt +cbrt = mp.cbrt +exp = mp.exp +ln = mp.ln +log = mp.log +log10 = mp.log10 +power = mp.power +cos = mp.cos +sin = mp.sin +tan = mp.tan +cosh = mp.cosh +sinh = mp.sinh +tanh = mp.tanh +acos = mp.acos +asin = mp.asin +atan = mp.atan +asinh = mp.asinh +acosh = mp.acosh +atanh = mp.atanh +sec = mp.sec +csc = mp.csc +cot = mp.cot +sech = mp.sech +csch = mp.csch +coth = mp.coth +asec = mp.asec +acsc = mp.acsc +acot = mp.acot +asech = mp.asech +acsch = mp.acsch +acoth = mp.acoth +cospi = mp.cospi +sinpi = mp.sinpi +sinc = mp.sinc +sincpi = mp.sincpi +cos_sin = mp.cos_sin +cospi_sinpi = mp.cospi_sinpi +fabs = mp.fabs +re = mp.re +im = mp.im +conj = mp.conj +floor = mp.floor +ceil = mp.ceil +nint = mp.nint +frac = mp.frac +root = mp.root +nthroot = mp.nthroot +hypot = mp.hypot +fmod = mp.fmod +ldexp = mp.ldexp +frexp = mp.frexp +sign = mp.sign +arg = mp.arg +phase = mp.phase +polar = mp.polar +rect = mp.rect +degrees = mp.degrees +radians = mp.radians +atan2 = mp.atan2 +fib = mp.fib +fibonacci = mp.fibonacci +lambertw = mp.lambertw +zeta = mp.zeta +altzeta = mp.altzeta +gamma = mp.gamma +rgamma = mp.rgamma +factorial = mp.factorial +fac = mp.fac +fac2 = mp.fac2 +beta = mp.beta +betainc = mp.betainc +psi = mp.psi +#psi0 = mp.psi0 +#psi1 = mp.psi1 +#psi2 = mp.psi2 +#psi3 = mp.psi3 +polygamma = mp.polygamma +digamma = mp.digamma +#trigamma = mp.trigamma +#tetragamma = mp.tetragamma +#pentagamma = mp.pentagamma +harmonic = mp.harmonic +bernoulli = mp.bernoulli +bernfrac = mp.bernfrac +stieltjes = mp.stieltjes +hurwitz = mp.hurwitz +dirichlet = mp.dirichlet +bernpoly = mp.bernpoly +eulerpoly = mp.eulerpoly +eulernum = mp.eulernum +polylog = mp.polylog +clsin = mp.clsin +clcos = mp.clcos +gammainc = mp.gammainc +gammaprod = mp.gammaprod +binomial = mp.binomial +rf = mp.rf +ff = mp.ff +hyper = mp.hyper +hyp0f1 = mp.hyp0f1 +hyp1f1 = mp.hyp1f1 +hyp1f2 = mp.hyp1f2 +hyp2f1 = mp.hyp2f1 +hyp2f2 = mp.hyp2f2 +hyp2f0 = mp.hyp2f0 +hyp2f3 = mp.hyp2f3 +hyp3f2 = mp.hyp3f2 +hyperu = mp.hyperu +hypercomb = mp.hypercomb +meijerg = mp.meijerg +appellf1 = mp.appellf1 +appellf2 = mp.appellf2 +appellf3 = mp.appellf3 +appellf4 = mp.appellf4 +hyper2d = mp.hyper2d +bihyper = mp.bihyper +erf = mp.erf +erfc = mp.erfc +erfi = mp.erfi +erfinv = mp.erfinv +npdf = mp.npdf +ncdf = mp.ncdf +expint = mp.expint +e1 = mp.e1 +ei = mp.ei +li = mp.li +ci = mp.ci +si = mp.si +chi = mp.chi +shi = mp.shi +fresnels = mp.fresnels +fresnelc = mp.fresnelc +airyai = mp.airyai +airybi = mp.airybi +airyaizero = mp.airyaizero +airybizero = mp.airybizero +scorergi = mp.scorergi +scorerhi = mp.scorerhi +ellipk = mp.ellipk +ellipe = mp.ellipe +ellipf = mp.ellipf +ellippi = mp.ellippi +elliprc = mp.elliprc +elliprj = mp.elliprj +elliprf = mp.elliprf +elliprd = mp.elliprd +elliprg = mp.elliprg +agm = mp.agm +jacobi = mp.jacobi +chebyt = mp.chebyt +chebyu = mp.chebyu +legendre = mp.legendre +legenp = mp.legenp +legenq = mp.legenq +hermite = mp.hermite +pcfd = mp.pcfd +pcfu = mp.pcfu +pcfv = mp.pcfv +pcfw = mp.pcfw +gegenbauer = mp.gegenbauer +laguerre = mp.laguerre +spherharm = mp.spherharm +besselj = mp.besselj +j0 = mp.j0 +j1 = mp.j1 +besseli = mp.besseli +bessely = mp.bessely +besselk = mp.besselk +besseljzero = mp.besseljzero +besselyzero = mp.besselyzero +hankel1 = mp.hankel1 +hankel2 = mp.hankel2 +struveh = mp.struveh +struvel = mp.struvel +angerj = mp.angerj +webere = mp.webere +lommels1 = mp.lommels1 +lommels2 = mp.lommels2 +whitm = mp.whitm +whitw = mp.whitw +ber = mp.ber +bei = mp.bei +ker = mp.ker +kei = mp.kei +coulombc = mp.coulombc +coulombf = mp.coulombf +coulombg = mp.coulombg +barnesg = mp.barnesg +superfac = mp.superfac +hyperfac = mp.hyperfac +loggamma = mp.loggamma +siegeltheta = mp.siegeltheta +siegelz = mp.siegelz +grampoint = mp.grampoint +zetazero = mp.zetazero +riemannr = mp.riemannr +primepi = mp.primepi +primepi2 = mp.primepi2 +primezeta = mp.primezeta +bell = mp.bell +polyexp = mp.polyexp +expm1 = mp.expm1 +log1p = mp.log1p +powm1 = mp.powm1 +unitroots = mp.unitroots +cyclotomic = mp.cyclotomic +mangoldt = mp.mangoldt +secondzeta = mp.secondzeta +nzeros = mp.nzeros +backlunds = mp.backlunds +lerchphi = mp.lerchphi +stirling1 = mp.stirling1 +stirling2 = mp.stirling2 +squarew = mp.squarew +trianglew = mp.trianglew +sawtoothw = mp.sawtoothw +unit_triangle = mp.unit_triangle +sigmoid = mp.sigmoid + +# be careful when changing this name, don't use test*! +def runtests(): + """ + Run all mpmath tests and print output. + """ + import os.path + from inspect import getsourcefile + from .tests import runtests as tests + testdir = os.path.dirname(os.path.abspath(getsourcefile(tests))) + importdir = os.path.abspath(testdir + '/../..') + tests.testit(importdir, testdir) + +def doctests(filter=[]): + import sys + from timeit import default_timer as clock + for i, arg in enumerate(sys.argv): + if '__init__.py' in arg: + filter = [sn for sn in sys.argv[i+1:] if not sn.startswith("-")] + break + import doctest + globs = globals().copy() + for obj in globs: #sorted(globs.keys()): + if filter: + if not sum([pat in obj for pat in filter]): + continue + sys.stdout.write(str(obj) + " ") + sys.stdout.flush() + t1 = clock() + doctest.run_docstring_examples(globs[obj], {}, verbose=("-v" in sys.argv)) + t2 = clock() + print(round(t2-t1, 3)) + +if __name__ == '__main__': + doctests() diff --git a/lib/python3.12/site-packages/mpmath/__pycache__/__init__.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dbf667a0c6b7fab9fc576d9ca3a9cc7fb5f37d52 Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/__pycache__/__init__.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/__pycache__/ctx_base.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/__pycache__/ctx_base.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3de127373d7e8a00361b45eb95b360fe44f873e6 Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/__pycache__/ctx_base.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/__pycache__/ctx_fp.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/__pycache__/ctx_fp.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..00b7ad00523af8cb0a6c34ff95c6e5a731938a01 Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/__pycache__/ctx_fp.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/__pycache__/ctx_iv.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/__pycache__/ctx_iv.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3cfe2cf301367e4fcb3f5fa9307bbc80ecc903aa Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/__pycache__/ctx_iv.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/__pycache__/ctx_mp.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/__pycache__/ctx_mp.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0429d086b2201019e71397c151af275d44086229 Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/__pycache__/ctx_mp.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/__pycache__/ctx_mp_python.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/__pycache__/ctx_mp_python.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..079c7c9f0655b7b8e43b74025cad801d8b0b152d Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/__pycache__/ctx_mp_python.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/__pycache__/identification.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/__pycache__/identification.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..95daedc604fb53ba7c44399a855a206c4b032e4e Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/__pycache__/identification.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/__pycache__/math2.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/__pycache__/math2.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..16733fbef1ac8c07e65840247282c2ad5b016d12 Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/__pycache__/math2.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/__pycache__/rational.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/__pycache__/rational.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0d6cf13a6dd41a6a70263381fd3dce3b0fe9c202 Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/__pycache__/rational.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/__pycache__/usertools.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/__pycache__/usertools.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b6906f67953073924ba60eff818bd96a1d2bfdb3 Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/__pycache__/usertools.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/__pycache__/visualization.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/__pycache__/visualization.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..553196c1d69088f89bc0e23fbc2cc2e31b403e99 Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/__pycache__/visualization.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/calculus/__init__.py b/lib/python3.12/site-packages/mpmath/calculus/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..040a3806b968f75b8d1a88ae37a4979fe83d466a --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/calculus/__init__.py @@ -0,0 +1,6 @@ +from . import calculus +# XXX: hack to set methods +from . import approximation +from . import differentiation +from . import extrapolation +from . import polynomials diff --git a/lib/python3.12/site-packages/mpmath/calculus/__pycache__/__init__.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/calculus/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..68b935c7202ecf98869b254e7193c7c2fcf1e383 Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/calculus/__pycache__/__init__.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/calculus/__pycache__/approximation.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/calculus/__pycache__/approximation.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e8a66be66c2ae5e3c97d4c21e243972f6d5ccbf0 Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/calculus/__pycache__/approximation.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/calculus/__pycache__/calculus.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/calculus/__pycache__/calculus.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6ce9ebc81d7eb83945286061ebe718f9b4511247 Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/calculus/__pycache__/calculus.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/calculus/__pycache__/differentiation.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/calculus/__pycache__/differentiation.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..37e2f1b2f6223cc5e9cf240283ffe6c13f34f3f2 Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/calculus/__pycache__/differentiation.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/calculus/__pycache__/extrapolation.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/calculus/__pycache__/extrapolation.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..822c2b54b3525e4d256f69237dec1d1ba94e278e Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/calculus/__pycache__/extrapolation.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/calculus/__pycache__/inverselaplace.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/calculus/__pycache__/inverselaplace.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7c124abfe1d0de0222c4112acb9778be58dbd7c8 Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/calculus/__pycache__/inverselaplace.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/calculus/__pycache__/odes.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/calculus/__pycache__/odes.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e307f3e9af52ae63e74ee32aafdae629f784b794 Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/calculus/__pycache__/odes.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/calculus/__pycache__/optimization.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/calculus/__pycache__/optimization.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..df90546290907e3d930c2bdf3f9e6cf9a91df6c5 Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/calculus/__pycache__/optimization.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/calculus/__pycache__/polynomials.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/calculus/__pycache__/polynomials.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..221c2301a2f96a522f18bbbd329c73bf67edbe93 Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/calculus/__pycache__/polynomials.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/calculus/__pycache__/quadrature.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/calculus/__pycache__/quadrature.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7fba3e048677a4ef50e790d61195164a507f1ed3 Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/calculus/__pycache__/quadrature.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/calculus/approximation.py b/lib/python3.12/site-packages/mpmath/calculus/approximation.py new file mode 100644 index 0000000000000000000000000000000000000000..7ca5cc598fb53491cb6ae4a41a40477c58544d53 --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/calculus/approximation.py @@ -0,0 +1,246 @@ +from ..libmp.backend import xrange +from .calculus import defun + +#----------------------------------------------------------------------------# +# Approximation methods # +#----------------------------------------------------------------------------# + +# The Chebyshev approximation formula is given at: +# http://mathworld.wolfram.com/ChebyshevApproximationFormula.html + +# The only major changes in the following code is that we return the +# expanded polynomial coefficients instead of Chebyshev coefficients, +# and that we automatically transform [a,b] -> [-1,1] and back +# for convenience. + +# Coefficient in Chebyshev approximation +def chebcoeff(ctx,f,a,b,j,N): + s = ctx.mpf(0) + h = ctx.mpf(0.5) + for k in range(1, N+1): + t = ctx.cospi((k-h)/N) + s += f(t*(b-a)*h + (b+a)*h) * ctx.cospi(j*(k-h)/N) + return 2*s/N + +# Generate Chebyshev polynomials T_n(ax+b) in expanded form +def chebT(ctx, a=1, b=0): + Tb = [1] + yield Tb + Ta = [b, a] + while 1: + yield Ta + # Recurrence: T[n+1](ax+b) = 2*(ax+b)*T[n](ax+b) - T[n-1](ax+b) + Tmp = [0] + [2*a*t for t in Ta] + for i, c in enumerate(Ta): Tmp[i] += 2*b*c + for i, c in enumerate(Tb): Tmp[i] -= c + Ta, Tb = Tmp, Ta + +@defun +def chebyfit(ctx, f, interval, N, error=False): + r""" + Computes a polynomial of degree `N-1` that approximates the + given function `f` on the interval `[a, b]`. With ``error=True``, + :func:`~mpmath.chebyfit` also returns an accurate estimate of the + maximum absolute error; that is, the maximum value of + `|f(x) - P(x)|` for `x \in [a, b]`. + + :func:`~mpmath.chebyfit` uses the Chebyshev approximation formula, + which gives a nearly optimal solution: that is, the maximum + error of the approximating polynomial is very close to + the smallest possible for any polynomial of the same degree. + + Chebyshev approximation is very useful if one needs repeated + evaluation of an expensive function, such as function defined + implicitly by an integral or a differential equation. (For + example, it could be used to turn a slow mpmath function + into a fast machine-precision version of the same.) + + **Examples** + + Here we use :func:`~mpmath.chebyfit` to generate a low-degree approximation + of `f(x) = \cos(x)`, valid on the interval `[1, 2]`:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> poly, err = chebyfit(cos, [1, 2], 5, error=True) + >>> nprint(poly) + [0.00291682, 0.146166, -0.732491, 0.174141, 0.949553] + >>> nprint(err, 12) + 1.61351758081e-5 + + The polynomial can be evaluated using ``polyval``:: + + >>> nprint(polyval(poly, 1.6), 12) + -0.0291858904138 + >>> nprint(cos(1.6), 12) + -0.0291995223013 + + Sampling the true error at 1000 points shows that the error + estimate generated by ``chebyfit`` is remarkably good:: + + >>> error = lambda x: abs(cos(x) - polyval(poly, x)) + >>> nprint(max([error(1+n/1000.) for n in range(1000)]), 12) + 1.61349954245e-5 + + **Choice of degree** + + The degree `N` can be set arbitrarily high, to obtain an + arbitrarily good approximation. As a rule of thumb, an + `N`-term Chebyshev approximation is good to `N/(b-a)` decimal + places on a unit interval (although this depends on how + well-behaved `f` is). The cost grows accordingly: ``chebyfit`` + evaluates the function `(N^2)/2` times to compute the + coefficients and an additional `N` times to estimate the error. + + **Possible issues** + + One should be careful to use a sufficiently high working + precision both when calling ``chebyfit`` and when evaluating + the resulting polynomial, as the polynomial is sometimes + ill-conditioned. It is for example difficult to reach + 15-digit accuracy when evaluating the polynomial using + machine precision floats, no matter the theoretical + accuracy of the polynomial. (The option to return the + coefficients in Chebyshev form should be made available + in the future.) + + It is important to note the Chebyshev approximation works + poorly if `f` is not smooth. A function containing singularities, + rapid oscillation, etc can be approximated more effectively by + multiplying it by a weight function that cancels out the + nonsmooth features, or by dividing the interval into several + segments. + """ + a, b = ctx._as_points(interval) + orig = ctx.prec + try: + ctx.prec = orig + int(N**0.5) + 20 + c = [chebcoeff(ctx,f,a,b,k,N) for k in range(N)] + d = [ctx.zero] * N + d[0] = -c[0]/2 + h = ctx.mpf(0.5) + T = chebT(ctx, ctx.mpf(2)/(b-a), ctx.mpf(-1)*(b+a)/(b-a)) + for (k, Tk) in zip(range(N), T): + for i in range(len(Tk)): + d[i] += c[k]*Tk[i] + d = d[::-1] + # Estimate maximum error + err = ctx.zero + for k in range(N): + x = ctx.cos(ctx.pi*k/N) * (b-a)*h + (b+a)*h + err = max(err, abs(f(x) - ctx.polyval(d, x))) + finally: + ctx.prec = orig + if error: + return d, +err + else: + return d + +@defun +def fourier(ctx, f, interval, N): + r""" + Computes the Fourier series of degree `N` of the given function + on the interval `[a, b]`. More precisely, :func:`~mpmath.fourier` returns + two lists `(c, s)` of coefficients (the cosine series and sine + series, respectively), such that + + .. math :: + + f(x) \sim \sum_{k=0}^N + c_k \cos(k m x) + s_k \sin(k m x) + + where `m = 2 \pi / (b-a)`. + + Note that many texts define the first coefficient as `2 c_0` instead + of `c_0`. The easiest way to evaluate the computed series correctly + is to pass it to :func:`~mpmath.fourierval`. + + **Examples** + + The function `f(x) = x` has a simple Fourier series on the standard + interval `[-\pi, \pi]`. The cosine coefficients are all zero (because + the function has odd symmetry), and the sine coefficients are + rational numbers:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> c, s = fourier(lambda x: x, [-pi, pi], 5) + >>> nprint(c) + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] + >>> nprint(s) + [0.0, 2.0, -1.0, 0.666667, -0.5, 0.4] + + This computes a Fourier series of a nonsymmetric function on + a nonstandard interval:: + + >>> I = [-1, 1.5] + >>> f = lambda x: x**2 - 4*x + 1 + >>> cs = fourier(f, I, 4) + >>> nprint(cs[0]) + [0.583333, 1.12479, -1.27552, 0.904708, -0.441296] + >>> nprint(cs[1]) + [0.0, -2.6255, 0.580905, 0.219974, -0.540057] + + It is instructive to plot a function along with its truncated + Fourier series:: + + >>> plot([f, lambda x: fourierval(cs, I, x)], I) #doctest: +SKIP + + Fourier series generally converge slowly (and may not converge + pointwise). For example, if `f(x) = \cosh(x)`, a 10-term Fourier + series gives an `L^2` error corresponding to 2-digit accuracy:: + + >>> I = [-1, 1] + >>> cs = fourier(cosh, I, 9) + >>> g = lambda x: (cosh(x) - fourierval(cs, I, x))**2 + >>> nprint(sqrt(quad(g, I))) + 0.00467963 + + :func:`~mpmath.fourier` uses numerical quadrature. For nonsmooth functions, + the accuracy (and speed) can be improved by including all singular + points in the interval specification:: + + >>> nprint(fourier(abs, [-1, 1], 0), 10) + ([0.5000441648], [0.0]) + >>> nprint(fourier(abs, [-1, 0, 1], 0), 10) + ([0.5], [0.0]) + + """ + interval = ctx._as_points(interval) + a = interval[0] + b = interval[-1] + L = b-a + cos_series = [] + sin_series = [] + cutoff = ctx.eps*10 + for n in xrange(N+1): + m = 2*n*ctx.pi/L + an = 2*ctx.quadgl(lambda t: f(t)*ctx.cos(m*t), interval)/L + bn = 2*ctx.quadgl(lambda t: f(t)*ctx.sin(m*t), interval)/L + if n == 0: + an /= 2 + if abs(an) < cutoff: an = ctx.zero + if abs(bn) < cutoff: bn = ctx.zero + cos_series.append(an) + sin_series.append(bn) + return cos_series, sin_series + +@defun +def fourierval(ctx, series, interval, x): + """ + Evaluates a Fourier series (in the format computed by + by :func:`~mpmath.fourier` for the given interval) at the point `x`. + + The series should be a pair `(c, s)` where `c` is the + cosine series and `s` is the sine series. The two lists + need not have the same length. + """ + cs, ss = series + ab = ctx._as_points(interval) + a = interval[0] + b = interval[-1] + m = 2*ctx.pi/(ab[-1]-ab[0]) + s = ctx.zero + s += ctx.fsum(cs[n]*ctx.cos(m*n*x) for n in xrange(len(cs)) if cs[n]) + s += ctx.fsum(ss[n]*ctx.sin(m*n*x) for n in xrange(len(ss)) if ss[n]) + return s diff --git a/lib/python3.12/site-packages/mpmath/calculus/calculus.py b/lib/python3.12/site-packages/mpmath/calculus/calculus.py new file mode 100644 index 0000000000000000000000000000000000000000..24256f121d6c07e5ce954f2a5f5024f156f64016 --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/calculus/calculus.py @@ -0,0 +1,6 @@ +class CalculusMethods(object): + pass + +def defun(f): + setattr(CalculusMethods, f.__name__, f) + return f diff --git a/lib/python3.12/site-packages/mpmath/calculus/differentiation.py b/lib/python3.12/site-packages/mpmath/calculus/differentiation.py new file mode 100644 index 0000000000000000000000000000000000000000..f8186bebd4476476eded9e4b2f8dc3eb23ef5ff9 --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/calculus/differentiation.py @@ -0,0 +1,647 @@ +from ..libmp.backend import xrange +from .calculus import defun + +try: + iteritems = dict.iteritems +except AttributeError: + iteritems = dict.items + +#----------------------------------------------------------------------------# +# Differentiation # +#----------------------------------------------------------------------------# + +@defun +def difference(ctx, s, n): + r""" + Given a sequence `(s_k)` containing at least `n+1` items, returns the + `n`-th forward difference, + + .. math :: + + \Delta^n = \sum_{k=0}^{\infty} (-1)^{k+n} {n \choose k} s_k. + """ + n = int(n) + d = ctx.zero + b = (-1) ** (n & 1) + for k in xrange(n+1): + d += b * s[k] + b = (b * (k-n)) // (k+1) + return d + +def hsteps(ctx, f, x, n, prec, **options): + singular = options.get('singular') + addprec = options.get('addprec', 10) + direction = options.get('direction', 0) + workprec = (prec+2*addprec) * (n+1) + orig = ctx.prec + try: + ctx.prec = workprec + h = options.get('h') + if h is None: + if options.get('relative'): + hextramag = int(ctx.mag(x)) + else: + hextramag = 0 + h = ctx.ldexp(1, -prec-addprec-hextramag) + else: + h = ctx.convert(h) + # Directed: steps x, x+h, ... x+n*h + direction = options.get('direction', 0) + if direction: + h *= ctx.sign(direction) + steps = xrange(n+1) + norm = h + # Central: steps x-n*h, x-(n-2)*h ..., x, ..., x+(n-2)*h, x+n*h + else: + steps = xrange(-n, n+1, 2) + norm = (2*h) + # Perturb + if singular: + x += 0.5*h + values = [f(x+k*h) for k in steps] + return values, norm, workprec + finally: + ctx.prec = orig + + +@defun +def diff(ctx, f, x, n=1, **options): + r""" + Numerically computes the derivative of `f`, `f'(x)`, or generally for + an integer `n \ge 0`, the `n`-th derivative `f^{(n)}(x)`. + A few basic examples are:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> diff(lambda x: x**2 + x, 1.0) + 3.0 + >>> diff(lambda x: x**2 + x, 1.0, 2) + 2.0 + >>> diff(lambda x: x**2 + x, 1.0, 3) + 0.0 + >>> nprint([diff(exp, 3, n) for n in range(5)]) # exp'(x) = exp(x) + [20.0855, 20.0855, 20.0855, 20.0855, 20.0855] + + Even more generally, given a tuple of arguments `(x_1, \ldots, x_k)` + and order `(n_1, \ldots, n_k)`, the partial derivative + `f^{(n_1,\ldots,n_k)}(x_1,\ldots,x_k)` is evaluated. For example:: + + >>> diff(lambda x,y: 3*x*y + 2*y - x, (0.25, 0.5), (0,1)) + 2.75 + >>> diff(lambda x,y: 3*x*y + 2*y - x, (0.25, 0.5), (1,1)) + 3.0 + + **Options** + + The following optional keyword arguments are recognized: + + ``method`` + Supported methods are ``'step'`` or ``'quad'``: derivatives may be + computed using either a finite difference with a small step + size `h` (default), or numerical quadrature. + ``direction`` + Direction of finite difference: can be -1 for a left + difference, 0 for a central difference (default), or +1 + for a right difference; more generally can be any complex number. + ``addprec`` + Extra precision for `h` used to account for the function's + sensitivity to perturbations (default = 10). + ``relative`` + Choose `h` relative to the magnitude of `x`, rather than an + absolute value; useful for large or tiny `x` (default = False). + ``h`` + As an alternative to ``addprec`` and ``relative``, manually + select the step size `h`. + ``singular`` + If True, evaluation exactly at the point `x` is avoided; this is + useful for differentiating functions with removable singularities. + Default = False. + ``radius`` + Radius of integration contour (with ``method = 'quad'``). + Default = 0.25. A larger radius typically is faster and more + accurate, but it must be chosen so that `f` has no + singularities within the radius from the evaluation point. + + A finite difference requires `n+1` function evaluations and must be + performed at `(n+1)` times the target precision. Accordingly, `f` must + support fast evaluation at high precision. + + With integration, a larger number of function evaluations is + required, but not much extra precision is required. For high order + derivatives, this method may thus be faster if f is very expensive to + evaluate at high precision. + + **Further examples** + + The direction option is useful for computing left- or right-sided + derivatives of nonsmooth functions:: + + >>> diff(abs, 0, direction=0) + 0.0 + >>> diff(abs, 0, direction=1) + 1.0 + >>> diff(abs, 0, direction=-1) + -1.0 + + More generally, if the direction is nonzero, a right difference + is computed where the step size is multiplied by sign(direction). + For example, with direction=+j, the derivative from the positive + imaginary direction will be computed:: + + >>> diff(abs, 0, direction=j) + (0.0 - 1.0j) + + With integration, the result may have a small imaginary part + even even if the result is purely real:: + + >>> diff(sqrt, 1, method='quad') # doctest:+ELLIPSIS + (0.5 - 4.59...e-26j) + >>> chop(_) + 0.5 + + Adding precision to obtain an accurate value:: + + >>> diff(cos, 1e-30) + 0.0 + >>> diff(cos, 1e-30, h=0.0001) + -9.99999998328279e-31 + >>> diff(cos, 1e-30, addprec=100) + -1.0e-30 + + """ + partial = False + try: + orders = list(n) + x = list(x) + partial = True + except TypeError: + pass + if partial: + x = [ctx.convert(_) for _ in x] + return _partial_diff(ctx, f, x, orders, options) + method = options.get('method', 'step') + if n == 0 and method != 'quad' and not options.get('singular'): + return f(ctx.convert(x)) + prec = ctx.prec + try: + if method == 'step': + values, norm, workprec = hsteps(ctx, f, x, n, prec, **options) + ctx.prec = workprec + v = ctx.difference(values, n) / norm**n + elif method == 'quad': + ctx.prec += 10 + radius = ctx.convert(options.get('radius', 0.25)) + def g(t): + rei = radius*ctx.expj(t) + z = x + rei + return f(z) / rei**n + d = ctx.quadts(g, [0, 2*ctx.pi]) + v = d * ctx.factorial(n) / (2*ctx.pi) + else: + raise ValueError("unknown method: %r" % method) + finally: + ctx.prec = prec + return +v + +def _partial_diff(ctx, f, xs, orders, options): + if not orders: + return f() + if not sum(orders): + return f(*xs) + i = 0 + for i in range(len(orders)): + if orders[i]: + break + order = orders[i] + def fdiff_inner(*f_args): + def inner(t): + return f(*(f_args[:i] + (t,) + f_args[i+1:])) + return ctx.diff(inner, f_args[i], order, **options) + orders[i] = 0 + return _partial_diff(ctx, fdiff_inner, xs, orders, options) + +@defun +def diffs(ctx, f, x, n=None, **options): + r""" + Returns a generator that yields the sequence of derivatives + + .. math :: + + f(x), f'(x), f''(x), \ldots, f^{(k)}(x), \ldots + + With ``method='step'``, :func:`~mpmath.diffs` uses only `O(k)` + function evaluations to generate the first `k` derivatives, + rather than the roughly `O(k^2)` evaluations + required if one calls :func:`~mpmath.diff` `k` separate times. + + With `n < \infty`, the generator stops as soon as the + `n`-th derivative has been generated. If the exact number of + needed derivatives is known in advance, this is further + slightly more efficient. + + Options are the same as for :func:`~mpmath.diff`. + + **Examples** + + >>> from mpmath import * + >>> mp.dps = 15 + >>> nprint(list(diffs(cos, 1, 5))) + [0.540302, -0.841471, -0.540302, 0.841471, 0.540302, -0.841471] + >>> for i, d in zip(range(6), diffs(cos, 1)): + ... print("%s %s" % (i, d)) + ... + 0 0.54030230586814 + 1 -0.841470984807897 + 2 -0.54030230586814 + 3 0.841470984807897 + 4 0.54030230586814 + 5 -0.841470984807897 + + """ + if n is None: + n = ctx.inf + else: + n = int(n) + if options.get('method', 'step') != 'step': + k = 0 + while k < n + 1: + yield ctx.diff(f, x, k, **options) + k += 1 + return + singular = options.get('singular') + if singular: + yield ctx.diff(f, x, 0, singular=True) + else: + yield f(ctx.convert(x)) + if n < 1: + return + if n == ctx.inf: + A, B = 1, 2 + else: + A, B = 1, n+1 + while 1: + callprec = ctx.prec + y, norm, workprec = hsteps(ctx, f, x, B, callprec, **options) + for k in xrange(A, B): + try: + ctx.prec = workprec + d = ctx.difference(y, k) / norm**k + finally: + ctx.prec = callprec + yield +d + if k >= n: + return + A, B = B, int(A*1.4+1) + B = min(B, n) + +def iterable_to_function(gen): + gen = iter(gen) + data = [] + def f(k): + for i in xrange(len(data), k+1): + data.append(next(gen)) + return data[k] + return f + +@defun +def diffs_prod(ctx, factors): + r""" + Given a list of `N` iterables or generators yielding + `f_k(x), f'_k(x), f''_k(x), \ldots` for `k = 1, \ldots, N`, + generate `g(x), g'(x), g''(x), \ldots` where + `g(x) = f_1(x) f_2(x) \cdots f_N(x)`. + + At high precision and for large orders, this is typically more efficient + than numerical differentiation if the derivatives of each `f_k(x)` + admit direct computation. + + Note: This function does not increase the working precision internally, + so guard digits may have to be added externally for full accuracy. + + **Examples** + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> f = lambda x: exp(x)*cos(x)*sin(x) + >>> u = diffs(f, 1) + >>> v = mp.diffs_prod([diffs(exp,1), diffs(cos,1), diffs(sin,1)]) + >>> next(u); next(v) + 1.23586333600241 + 1.23586333600241 + >>> next(u); next(v) + 0.104658952245596 + 0.104658952245596 + >>> next(u); next(v) + -5.96999877552086 + -5.96999877552086 + >>> next(u); next(v) + -12.4632923122697 + -12.4632923122697 + + """ + N = len(factors) + if N == 1: + for c in factors[0]: + yield c + else: + u = iterable_to_function(ctx.diffs_prod(factors[:N//2])) + v = iterable_to_function(ctx.diffs_prod(factors[N//2:])) + n = 0 + while 1: + #yield sum(binomial(n,k)*u(n-k)*v(k) for k in xrange(n+1)) + s = u(n) * v(0) + a = 1 + for k in xrange(1,n+1): + a = a * (n-k+1) // k + s += a * u(n-k) * v(k) + yield s + n += 1 + +def dpoly(n, _cache={}): + """ + nth differentiation polynomial for exp (Faa di Bruno's formula). + + TODO: most exponents are zero, so maybe a sparse representation + would be better. + """ + if n in _cache: + return _cache[n] + if not _cache: + _cache[0] = {(0,):1} + R = dpoly(n-1) + R = dict((c+(0,),v) for (c,v) in iteritems(R)) + Ra = {} + for powers, count in iteritems(R): + powers1 = (powers[0]+1,) + powers[1:] + if powers1 in Ra: + Ra[powers1] += count + else: + Ra[powers1] = count + for powers, count in iteritems(R): + if not sum(powers): + continue + for k,p in enumerate(powers): + if p: + powers2 = powers[:k] + (p-1,powers[k+1]+1) + powers[k+2:] + if powers2 in Ra: + Ra[powers2] += p*count + else: + Ra[powers2] = p*count + _cache[n] = Ra + return _cache[n] + +@defun +def diffs_exp(ctx, fdiffs): + r""" + Given an iterable or generator yielding `f(x), f'(x), f''(x), \ldots` + generate `g(x), g'(x), g''(x), \ldots` where `g(x) = \exp(f(x))`. + + At high precision and for large orders, this is typically more efficient + than numerical differentiation if the derivatives of `f(x)` + admit direct computation. + + Note: This function does not increase the working precision internally, + so guard digits may have to be added externally for full accuracy. + + **Examples** + + The derivatives of the gamma function can be computed using + logarithmic differentiation:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> + >>> def diffs_loggamma(x): + ... yield loggamma(x) + ... i = 0 + ... while 1: + ... yield psi(i,x) + ... i += 1 + ... + >>> u = diffs_exp(diffs_loggamma(3)) + >>> v = diffs(gamma, 3) + >>> next(u); next(v) + 2.0 + 2.0 + >>> next(u); next(v) + 1.84556867019693 + 1.84556867019693 + >>> next(u); next(v) + 2.49292999190269 + 2.49292999190269 + >>> next(u); next(v) + 3.44996501352367 + 3.44996501352367 + + """ + fn = iterable_to_function(fdiffs) + f0 = ctx.exp(fn(0)) + yield f0 + i = 1 + while 1: + s = ctx.mpf(0) + for powers, c in iteritems(dpoly(i)): + s += c*ctx.fprod(fn(k+1)**p for (k,p) in enumerate(powers) if p) + yield s * f0 + i += 1 + +@defun +def differint(ctx, f, x, n=1, x0=0): + r""" + Calculates the Riemann-Liouville differintegral, or fractional + derivative, defined by + + .. math :: + + \,_{x_0}{\mathbb{D}}^n_xf(x) = \frac{1}{\Gamma(m-n)} \frac{d^m}{dx^m} + \int_{x_0}^{x}(x-t)^{m-n-1}f(t)dt + + where `f` is a given (presumably well-behaved) function, + `x` is the evaluation point, `n` is the order, and `x_0` is + the reference point of integration (`m` is an arbitrary + parameter selected automatically). + + With `n = 1`, this is just the standard derivative `f'(x)`; with `n = 2`, + the second derivative `f''(x)`, etc. With `n = -1`, it gives + `\int_{x_0}^x f(t) dt`, with `n = -2` + it gives `\int_{x_0}^x \left( \int_{x_0}^t f(u) du \right) dt`, etc. + + As `n` is permitted to be any number, this operator generalizes + iterated differentiation and iterated integration to a single + operator with a continuous order parameter. + + **Examples** + + There is an exact formula for the fractional derivative of a + monomial `x^p`, which may be used as a reference. For example, + the following gives a half-derivative (order 0.5):: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> x = mpf(3); p = 2; n = 0.5 + >>> differint(lambda t: t**p, x, n) + 7.81764019044672 + >>> gamma(p+1)/gamma(p-n+1) * x**(p-n) + 7.81764019044672 + + Another useful test function is the exponential function, whose + integration / differentiation formula easy generalizes + to arbitrary order. Here we first compute a third derivative, + and then a triply nested integral. (The reference point `x_0` + is set to `-\infty` to avoid nonzero endpoint terms.):: + + >>> differint(lambda x: exp(pi*x), -1.5, 3) + 0.278538406900792 + >>> exp(pi*-1.5) * pi**3 + 0.278538406900792 + >>> differint(lambda x: exp(pi*x), 3.5, -3, -inf) + 1922.50563031149 + >>> exp(pi*3.5) / pi**3 + 1922.50563031149 + + However, for noninteger `n`, the differentiation formula for the + exponential function must be modified to give the same result as the + Riemann-Liouville differintegral:: + + >>> x = mpf(3.5) + >>> c = pi + >>> n = 1+2*j + >>> differint(lambda x: exp(c*x), x, n) + (-123295.005390743 + 140955.117867654j) + >>> x**(-n) * exp(c)**x * (x*c)**n * gammainc(-n, 0, x*c) / gamma(-n) + (-123295.005390743 + 140955.117867654j) + + + """ + m = max(int(ctx.ceil(ctx.re(n)))+1, 1) + r = m-n-1 + g = lambda x: ctx.quad(lambda t: (x-t)**r * f(t), [x0, x]) + return ctx.diff(g, x, m) / ctx.gamma(m-n) + +@defun +def diffun(ctx, f, n=1, **options): + r""" + Given a function `f`, returns a function `g(x)` that evaluates the nth + derivative `f^{(n)}(x)`:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> cos2 = diffun(sin) + >>> sin2 = diffun(sin, 4) + >>> cos(1.3), cos2(1.3) + (0.267498828624587, 0.267498828624587) + >>> sin(1.3), sin2(1.3) + (0.963558185417193, 0.963558185417193) + + The function `f` must support arbitrary precision evaluation. + See :func:`~mpmath.diff` for additional details and supported + keyword options. + """ + if n == 0: + return f + def g(x): + return ctx.diff(f, x, n, **options) + return g + +@defun +def taylor(ctx, f, x, n, **options): + r""" + Produces a degree-`n` Taylor polynomial around the point `x` of the + given function `f`. The coefficients are returned as a list. + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> nprint(chop(taylor(sin, 0, 5))) + [0.0, 1.0, 0.0, -0.166667, 0.0, 0.00833333] + + The coefficients are computed using high-order numerical + differentiation. The function must be possible to evaluate + to arbitrary precision. See :func:`~mpmath.diff` for additional details + and supported keyword options. + + Note that to evaluate the Taylor polynomial as an approximation + of `f`, e.g. with :func:`~mpmath.polyval`, the coefficients must be reversed, + and the point of the Taylor expansion must be subtracted from + the argument: + + >>> p = taylor(exp, 2.0, 10) + >>> polyval(p[::-1], 2.5 - 2.0) + 12.1824939606092 + >>> exp(2.5) + 12.1824939607035 + + """ + gen = enumerate(ctx.diffs(f, x, n, **options)) + if options.get("chop", True): + return [ctx.chop(d)/ctx.factorial(i) for i, d in gen] + else: + return [d/ctx.factorial(i) for i, d in gen] + +@defun +def pade(ctx, a, L, M): + r""" + Computes a Pade approximation of degree `(L, M)` to a function. + Given at least `L+M+1` Taylor coefficients `a` approximating + a function `A(x)`, :func:`~mpmath.pade` returns coefficients of + polynomials `P, Q` satisfying + + .. math :: + + P = \sum_{k=0}^L p_k x^k + + Q = \sum_{k=0}^M q_k x^k + + Q_0 = 1 + + A(x) Q(x) = P(x) + O(x^{L+M+1}) + + `P(x)/Q(x)` can provide a good approximation to an analytic function + beyond the radius of convergence of its Taylor series (example + from G.A. Baker 'Essentials of Pade Approximants' Academic Press, + Ch.1A):: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> one = mpf(1) + >>> def f(x): + ... return sqrt((one + 2*x)/(one + x)) + ... + >>> a = taylor(f, 0, 6) + >>> p, q = pade(a, 3, 3) + >>> x = 10 + >>> polyval(p[::-1], x)/polyval(q[::-1], x) + 1.38169105566806 + >>> f(x) + 1.38169855941551 + + """ + # To determine L+1 coefficients of P and M coefficients of Q + # L+M+1 coefficients of A must be provided + if len(a) < L+M+1: + raise ValueError("L+M+1 Coefficients should be provided") + + if M == 0: + if L == 0: + return [ctx.one], [ctx.one] + else: + return a[:L+1], [ctx.one] + + # Solve first + # a[L]*q[1] + ... + a[L-M+1]*q[M] = -a[L+1] + # ... + # a[L+M-1]*q[1] + ... + a[L]*q[M] = -a[L+M] + A = ctx.matrix(M) + for j in range(M): + for i in range(min(M, L+j+1)): + A[j, i] = a[L+j-i] + v = -ctx.matrix(a[(L+1):(L+M+1)]) + x = ctx.lu_solve(A, v) + q = [ctx.one] + list(x) + # compute p + p = [0]*(L+1) + for i in range(L+1): + s = a[i] + for j in range(1, min(M,i) + 1): + s += q[j]*a[i-j] + p[i] = s + return p, q diff --git a/lib/python3.12/site-packages/mpmath/calculus/extrapolation.py b/lib/python3.12/site-packages/mpmath/calculus/extrapolation.py new file mode 100644 index 0000000000000000000000000000000000000000..7df0fea3c62c9b71ee24d3f39fd9b7fd3318ed23 --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/calculus/extrapolation.py @@ -0,0 +1,2115 @@ +try: + from itertools import izip +except ImportError: + izip = zip + +from ..libmp.backend import xrange +from .calculus import defun + +try: + next = next +except NameError: + next = lambda _: _.next() + +@defun +def richardson(ctx, seq): + r""" + Given a list ``seq`` of the first `N` elements of a slowly convergent + infinite sequence, :func:`~mpmath.richardson` computes the `N`-term + Richardson extrapolate for the limit. + + :func:`~mpmath.richardson` returns `(v, c)` where `v` is the estimated + limit and `c` is the magnitude of the largest weight used during the + computation. The weight provides an estimate of the precision + lost to cancellation. Due to cancellation effects, the sequence must + be typically be computed at a much higher precision than the target + accuracy of the extrapolation. + + **Applicability and issues** + + The `N`-step Richardson extrapolation algorithm used by + :func:`~mpmath.richardson` is described in [1]. + + Richardson extrapolation only works for a specific type of sequence, + namely one converging like partial sums of + `P(1)/Q(1) + P(2)/Q(2) + \ldots` where `P` and `Q` are polynomials. + When the sequence does not convergence at such a rate + :func:`~mpmath.richardson` generally produces garbage. + + Richardson extrapolation has the advantage of being fast: the `N`-term + extrapolate requires only `O(N)` arithmetic operations, and usually + produces an estimate that is accurate to `O(N)` digits. Contrast with + the Shanks transformation (see :func:`~mpmath.shanks`), which requires + `O(N^2)` operations. + + :func:`~mpmath.richardson` is unable to produce an estimate for the + approximation error. One way to estimate the error is to perform + two extrapolations with slightly different `N` and comparing the + results. + + Richardson extrapolation does not work for oscillating sequences. + As a simple workaround, :func:`~mpmath.richardson` detects if the last + three elements do not differ monotonically, and in that case + applies extrapolation only to the even-index elements. + + **Example** + + Applying Richardson extrapolation to the Leibniz series for `\pi`:: + + >>> from mpmath import * + >>> mp.dps = 30; mp.pretty = True + >>> S = [4*sum(mpf(-1)**n/(2*n+1) for n in range(m)) + ... for m in range(1,30)] + >>> v, c = richardson(S[:10]) + >>> v + 3.2126984126984126984126984127 + >>> nprint([v-pi, c]) + [0.0711058, 2.0] + + >>> v, c = richardson(S[:30]) + >>> v + 3.14159265468624052829954206226 + >>> nprint([v-pi, c]) + [1.09645e-9, 20833.3] + + **References** + + 1. [BenderOrszag]_ pp. 375-376 + + """ + if len(seq) < 3: + raise ValueError("seq should be of minimum length 3") + if ctx.sign(seq[-1]-seq[-2]) != ctx.sign(seq[-2]-seq[-3]): + seq = seq[::2] + N = len(seq)//2-1 + s = ctx.zero + # The general weight is c[k] = (N+k)**N * (-1)**(k+N) / k! / (N-k)! + # To avoid repeated factorials, we simplify the quotient + # of successive weights to obtain a recurrence relation + c = (-1)**N * N**N / ctx.mpf(ctx._ifac(N)) + maxc = 1 + for k in xrange(N+1): + s += c * seq[N+k] + maxc = max(abs(c), maxc) + c *= (k-N)*ctx.mpf(k+N+1)**N + c /= ((1+k)*ctx.mpf(k+N)**N) + return s, maxc + +@defun +def shanks(ctx, seq, table=None, randomized=False): + r""" + Given a list ``seq`` of the first `N` elements of a slowly + convergent infinite sequence `(A_k)`, :func:`~mpmath.shanks` computes the iterated + Shanks transformation `S(A), S(S(A)), \ldots, S^{N/2}(A)`. The Shanks + transformation often provides strong convergence acceleration, + especially if the sequence is oscillating. + + The iterated Shanks transformation is computed using the Wynn + epsilon algorithm (see [1]). :func:`~mpmath.shanks` returns the full + epsilon table generated by Wynn's algorithm, which can be read + off as follows: + + * The table is a list of lists forming a lower triangular matrix, + where higher row and column indices correspond to more accurate + values. + * The columns with even index hold dummy entries (required for the + computation) and the columns with odd index hold the actual + extrapolates. + * The last element in the last row is typically the most + accurate estimate of the limit. + * The difference to the third last element in the last row + provides an estimate of the approximation error. + * The magnitude of the second last element provides an estimate + of the numerical accuracy lost to cancellation. + + For convenience, so the extrapolation is stopped at an odd index + so that ``shanks(seq)[-1][-1]`` always gives an estimate of the + limit. + + Optionally, an existing table can be passed to :func:`~mpmath.shanks`. + This can be used to efficiently extend a previous computation after + new elements have been appended to the sequence. The table will + then be updated in-place. + + **The Shanks transformation** + + The Shanks transformation is defined as follows (see [2]): given + the input sequence `(A_0, A_1, \ldots)`, the transformed sequence is + given by + + .. math :: + + S(A_k) = \frac{A_{k+1}A_{k-1}-A_k^2}{A_{k+1}+A_{k-1}-2 A_k} + + The Shanks transformation gives the exact limit `A_{\infty}` in a + single step if `A_k = A + a q^k`. Note in particular that it + extrapolates the exact sum of a geometric series in a single step. + + Applying the Shanks transformation once often improves convergence + substantially for an arbitrary sequence, but the optimal effect is + obtained by applying it iteratively: + `S(S(A_k)), S(S(S(A_k))), \ldots`. + + Wynn's epsilon algorithm provides an efficient way to generate + the table of iterated Shanks transformations. It reduces the + computation of each element to essentially a single division, at + the cost of requiring dummy elements in the table. See [1] for + details. + + **Precision issues** + + Due to cancellation effects, the sequence must be typically be + computed at a much higher precision than the target accuracy + of the extrapolation. + + If the Shanks transformation converges to the exact limit (such + as if the sequence is a geometric series), then a division by + zero occurs. By default, :func:`~mpmath.shanks` handles this case by + terminating the iteration and returning the table it has + generated so far. With *randomized=True*, it will instead + replace the zero by a pseudorandom number close to zero. + (TODO: find a better solution to this problem.) + + **Examples** + + We illustrate by applying Shanks transformation to the Leibniz + series for `\pi`:: + + >>> from mpmath import * + >>> mp.dps = 50 + >>> S = [4*sum(mpf(-1)**n/(2*n+1) for n in range(m)) + ... for m in range(1,30)] + >>> + >>> T = shanks(S[:7]) + >>> for row in T: + ... nprint(row) + ... + [-0.75] + [1.25, 3.16667] + [-1.75, 3.13333, -28.75] + [2.25, 3.14524, 82.25, 3.14234] + [-2.75, 3.13968, -177.75, 3.14139, -969.937] + [3.25, 3.14271, 327.25, 3.14166, 3515.06, 3.14161] + + The extrapolated accuracy is about 4 digits, and about 4 digits + may have been lost due to cancellation:: + + >>> L = T[-1] + >>> nprint([abs(L[-1] - pi), abs(L[-1] - L[-3]), abs(L[-2])]) + [2.22532e-5, 4.78309e-5, 3515.06] + + Now we extend the computation:: + + >>> T = shanks(S[:25], T) + >>> L = T[-1] + >>> nprint([abs(L[-1] - pi), abs(L[-1] - L[-3]), abs(L[-2])]) + [3.75527e-19, 1.48478e-19, 2.96014e+17] + + The value for pi is now accurate to 18 digits. About 18 digits may + also have been lost to cancellation. + + Here is an example with a geometric series, where the convergence + is immediate (the sum is exactly 1):: + + >>> mp.dps = 15 + >>> for row in shanks([0.5, 0.75, 0.875, 0.9375, 0.96875]): + ... nprint(row) + [4.0] + [8.0, 1.0] + + **References** + + 1. [GravesMorris]_ + + 2. [BenderOrszag]_ pp. 368-375 + + """ + if len(seq) < 2: + raise ValueError("seq should be of minimum length 2") + if table: + START = len(table) + else: + START = 0 + table = [] + STOP = len(seq) - 1 + if STOP & 1: + STOP -= 1 + one = ctx.one + eps = +ctx.eps + if randomized: + from random import Random + rnd = Random() + rnd.seed(START) + for i in xrange(START, STOP): + row = [] + for j in xrange(i+1): + if j == 0: + a, b = 0, seq[i+1]-seq[i] + else: + if j == 1: + a = seq[i] + else: + a = table[i-1][j-2] + b = row[j-1] - table[i-1][j-1] + if not b: + if randomized: + b = (1 + rnd.getrandbits(10))*eps + elif i & 1: + return table[:-1] + else: + return table + row.append(a + one/b) + table.append(row) + return table + + +class levin_class: + # levin: Copyright 2013 Timo Hartmann (thartmann15 at gmail.com) + r""" + This interface implements Levin's (nonlinear) sequence transformation for + convergence acceleration and summation of divergent series. It performs + better than the Shanks/Wynn-epsilon algorithm for logarithmic convergent + or alternating divergent series. + + Let *A* be the series we want to sum: + + .. math :: + + A = \sum_{k=0}^{\infty} a_k + + Attention: all `a_k` must be non-zero! + + Let `s_n` be the partial sums of this series: + + .. math :: + + s_n = \sum_{k=0}^n a_k. + + **Methods** + + Calling ``levin`` returns an object with the following methods. + + ``update(...)`` works with the list of individual terms `a_k` of *A*, and + ``update_step(...)`` works with the list of partial sums `s_k` of *A*: + + .. code :: + + v, e = ...update([a_0, a_1,..., a_k]) + v, e = ...update_psum([s_0, s_1,..., s_k]) + + ``step(...)`` works with the individual terms `a_k` and ``step_psum(...)`` + works with the partial sums `s_k`: + + .. code :: + + v, e = ...step(a_k) + v, e = ...step_psum(s_k) + + *v* is the current estimate for *A*, and *e* is an error estimate which is + simply the difference between the current estimate and the last estimate. + One should not mix ``update``, ``update_psum``, ``step`` and ``step_psum``. + + **A word of caution** + + One can only hope for good results (i.e. convergence acceleration or + resummation) if the `s_n` have some well defind asymptotic behavior for + large `n` and are not erratic or random. Furthermore one usually needs very + high working precision because of the numerical cancellation. If the working + precision is insufficient, levin may produce silently numerical garbage. + Furthermore even if the Levin-transformation converges, in the general case + there is no proof that the result is mathematically sound. Only for very + special classes of problems one can prove that the Levin-transformation + converges to the expected result (for example Stieltjes-type integrals). + Furthermore the Levin-transform is quite expensive (i.e. slow) in comparison + to Shanks/Wynn-epsilon, Richardson & co. + In summary one can say that the Levin-transformation is powerful but + unreliable and that it may need a copious amount of working precision. + + The Levin transform has several variants differing in the choice of weights. + Some variants are better suited for the possible flavours of convergence + behaviour of *A* than other variants: + + .. code :: + + convergence behaviour levin-u levin-t levin-v shanks/wynn-epsilon + + logarithmic + - + - + linear + + + + + alternating divergent + + + + + + "+" means the variant is suitable,"-" means the variant is not suitable; + for comparison the Shanks/Wynn-epsilon transform is listed, too. + + The variant is controlled though the variant keyword (i.e. ``variant="u"``, + ``variant="t"`` or ``variant="v"``). Overall "u" is probably the best choice. + + Finally it is possible to use the Sidi-S transform instead of the Levin transform + by using the keyword ``method='sidi'``. The Sidi-S transform works better than the + Levin transformation for some divergent series (see the examples). + + Parameters: + + .. code :: + + method "levin" or "sidi" chooses either the Levin or the Sidi-S transformation + variant "u","t" or "v" chooses the weight variant. + + The Levin transform is also accessible through the nsum interface. + ``method="l"`` or ``method="levin"`` select the normal Levin transform while + ``method="sidi"`` + selects the Sidi-S transform. The variant is in both cases selected through the + levin_variant keyword. The stepsize in :func:`~mpmath.nsum` must not be chosen too large, otherwise + it will miss the point where the Levin transform converges resulting in numerical + overflow/garbage. For highly divergent series a copious amount of working precision + must be chosen. + + **Examples** + + First we sum the zeta function:: + + >>> from mpmath import mp + >>> mp.prec = 53 + >>> eps = mp.mpf(mp.eps) + >>> with mp.extraprec(2 * mp.prec): # levin needs a high working precision + ... L = mp.levin(method = "levin", variant = "u") + ... S, s, n = [], 0, 1 + ... while 1: + ... s += mp.one / (n * n) + ... n += 1 + ... S.append(s) + ... v, e = L.update_psum(S) + ... if e < eps: + ... break + ... if n > 1000: raise RuntimeError("iteration limit exceeded") + >>> print(mp.chop(v - mp.pi ** 2 / 6)) + 0.0 + >>> w = mp.nsum(lambda n: 1 / (n*n), [1, mp.inf], method = "levin", levin_variant = "u") + >>> print(mp.chop(v - w)) + 0.0 + + Now we sum the zeta function outside its range of convergence + (attention: This does not work at the negative integers!):: + + >>> eps = mp.mpf(mp.eps) + >>> with mp.extraprec(2 * mp.prec): # levin needs a high working precision + ... L = mp.levin(method = "levin", variant = "v") + ... A, n = [], 1 + ... while 1: + ... s = mp.mpf(n) ** (2 + 3j) + ... n += 1 + ... A.append(s) + ... v, e = L.update(A) + ... if e < eps: + ... break + ... if n > 1000: raise RuntimeError("iteration limit exceeded") + >>> print(mp.chop(v - mp.zeta(-2-3j))) + 0.0 + >>> w = mp.nsum(lambda n: n ** (2 + 3j), [1, mp.inf], method = "levin", levin_variant = "v") + >>> print(mp.chop(v - w)) + 0.0 + + Now we sum the divergent asymptotic expansion of an integral related to the + exponential integral (see also [2] p.373). The Sidi-S transform works best here:: + + >>> z = mp.mpf(10) + >>> exact = mp.quad(lambda x: mp.exp(-x)/(1+x/z),[0,mp.inf]) + >>> # exact = z * mp.exp(z) * mp.expint(1,z) # this is the symbolic expression for the integral + >>> eps = mp.mpf(mp.eps) + >>> with mp.extraprec(2 * mp.prec): # high working precisions are mandatory for divergent resummation + ... L = mp.levin(method = "sidi", variant = "t") + ... n = 0 + ... while 1: + ... s = (-1)**n * mp.fac(n) * z ** (-n) + ... v, e = L.step(s) + ... n += 1 + ... if e < eps: + ... break + ... if n > 1000: raise RuntimeError("iteration limit exceeded") + >>> print(mp.chop(v - exact)) + 0.0 + >>> w = mp.nsum(lambda n: (-1) ** n * mp.fac(n) * z ** (-n), [0, mp.inf], method = "sidi", levin_variant = "t") + >>> print(mp.chop(v - w)) + 0.0 + + Another highly divergent integral is also summable:: + + >>> z = mp.mpf(2) + >>> eps = mp.mpf(mp.eps) + >>> exact = mp.quad(lambda x: mp.exp( -x * x / 2 - z * x ** 4), [0,mp.inf]) * 2 / mp.sqrt(2 * mp.pi) + >>> # exact = mp.exp(mp.one / (32 * z)) * mp.besselk(mp.one / 4, mp.one / (32 * z)) / (4 * mp.sqrt(z * mp.pi)) # this is the symbolic expression for the integral + >>> with mp.extraprec(7 * mp.prec): # we need copious amount of precision to sum this highly divergent series + ... L = mp.levin(method = "levin", variant = "t") + ... n, s = 0, 0 + ... while 1: + ... s += (-z)**n * mp.fac(4 * n) / (mp.fac(n) * mp.fac(2 * n) * (4 ** n)) + ... n += 1 + ... v, e = L.step_psum(s) + ... if e < eps: + ... break + ... if n > 1000: raise RuntimeError("iteration limit exceeded") + >>> print(mp.chop(v - exact)) + 0.0 + >>> w = mp.nsum(lambda n: (-z)**n * mp.fac(4 * n) / (mp.fac(n) * mp.fac(2 * n) * (4 ** n)), + ... [0, mp.inf], method = "levin", levin_variant = "t", workprec = 8*mp.prec, steps = [2] + [1 for x in xrange(1000)]) + >>> print(mp.chop(v - w)) + 0.0 + + These examples run with 15-20 decimal digits precision. For higher precision the + working precision must be raised. + + **Examples for nsum** + + Here we calculate Euler's constant as the constant term in the Laurent + expansion of `\zeta(s)` at `s=1`. This sum converges extremly slowly because of + the logarithmic convergence behaviour of the Dirichlet series for zeta:: + + >>> mp.dps = 30 + >>> z = mp.mpf(10) ** (-10) + >>> a = mp.nsum(lambda n: n**(-(1+z)), [1, mp.inf], method = "l") - 1 / z + >>> print(mp.chop(a - mp.euler, tol = 1e-10)) + 0.0 + + The Sidi-S transform performs excellently for the alternating series of `\log(2)`:: + + >>> a = mp.nsum(lambda n: (-1)**(n-1) / n, [1, mp.inf], method = "sidi") + >>> print(mp.chop(a - mp.log(2))) + 0.0 + + Hypergeometric series can also be summed outside their range of convergence. + The stepsize in :func:`~mpmath.nsum` must not be chosen too large, otherwise it will miss the + point where the Levin transform converges resulting in numerical overflow/garbage:: + + >>> z = 2 + 1j + >>> exact = mp.hyp2f1(2 / mp.mpf(3), 4 / mp.mpf(3), 1 / mp.mpf(3), z) + >>> f = lambda n: mp.rf(2 / mp.mpf(3), n) * mp.rf(4 / mp.mpf(3), n) * z**n / (mp.rf(1 / mp.mpf(3), n) * mp.fac(n)) + >>> v = mp.nsum(f, [0, mp.inf], method = "levin", steps = [10 for x in xrange(1000)]) + >>> print(mp.chop(exact-v)) + 0.0 + + References: + + [1] E.J. Weniger - "Nonlinear Sequence Transformations for the Acceleration of + Convergence and the Summation of Divergent Series" arXiv:math/0306302 + + [2] A. Sidi - "Pratical Extrapolation Methods" + + [3] H.H.H. Homeier - "Scalar Levin-Type Sequence Transformations" arXiv:math/0005209 + + """ + + def __init__(self, method = "levin", variant = "u"): + self.variant = variant + self.n = 0 + self.a0 = 0 + self.theta = 1 + self.A = [] + self.B = [] + self.last = 0 + self.last_s = False + + if method == "levin": + self.factor = self.factor_levin + elif method == "sidi": + self.factor = self.factor_sidi + else: + raise ValueError("levin: unknown method \"%s\"" % method) + + def factor_levin(self, i): + # original levin + # [1] p.50,e.7.5-7 (with n-j replaced by i) + return (self.theta + i) * (self.theta + self.n - 1) ** (self.n - i - 2) / self.ctx.mpf(self.theta + self.n) ** (self.n - i - 1) + + def factor_sidi(self, i): + # sidi analogon to levin (factorial series) + # [1] p.59,e.8.3-16 (with n-j replaced by i) + return (self.theta + self.n - 1) * (self.theta + self.n - 2) / self.ctx.mpf((self.theta + 2 * self.n - i - 2) * (self.theta + 2 * self.n - i - 3)) + + def run(self, s, a0, a1 = 0): + if self.variant=="t": + # levin t + w=a0 + elif self.variant=="u": + # levin u + w=a0*(self.theta+self.n) + elif self.variant=="v": + # levin v + w=a0*a1/(a0-a1) + else: + assert False, "unknown variant" + + if w==0: + raise ValueError("levin: zero weight") + + self.A.append(s/w) + self.B.append(1/w) + + for i in range(self.n-1,-1,-1): + if i==self.n-1: + f=1 + else: + f=self.factor(i) + + self.A[i]=self.A[i+1]-f*self.A[i] + self.B[i]=self.B[i+1]-f*self.B[i] + + self.n+=1 + + ########################################################################### + + def update_psum(self,S): + """ + This routine applies the convergence acceleration to the list of partial sums. + + A = sum(a_k, k = 0..infinity) + s_n = sum(a_k, k = 0..n) + + v, e = ...update_psum([s_0, s_1,..., s_k]) + + output: + v current estimate of the series A + e an error estimate which is simply the difference between the current + estimate and the last estimate. + """ + + if self.variant!="v": + if self.n==0: + self.run(S[0],S[0]) + while self.n>> from mpmath import mp + >>> AC = mp.cohen_alt() + >>> S, s, n = [], 0, 1 + >>> while 1: + ... s += -((-1) ** n) * mp.one / (n * n) + ... n += 1 + ... S.append(s) + ... v, e = AC.update_psum(S) + ... if e < mp.eps: + ... break + ... if n > 1000: raise RuntimeError("iteration limit exceeded") + >>> print(mp.chop(v - mp.pi ** 2 / 12)) + 0.0 + + Here we compute the product `\prod_{n=1}^{\infty} \Gamma(1+1/(2n-1)) / \Gamma(1+1/(2n))`:: + + >>> A = [] + >>> AC = mp.cohen_alt() + >>> n = 1 + >>> while 1: + ... A.append( mp.loggamma(1 + mp.one / (2 * n - 1))) + ... A.append(-mp.loggamma(1 + mp.one / (2 * n))) + ... n += 1 + ... v, e = AC.update(A) + ... if e < mp.eps: + ... break + ... if n > 1000: raise RuntimeError("iteration limit exceeded") + >>> v = mp.exp(v) + >>> print(mp.chop(v - 1.06215090557106, tol = 1e-12)) + 0.0 + + ``cohen_alt`` is also accessible through the :func:`~mpmath.nsum` interface:: + + >>> v = mp.nsum(lambda n: (-1)**(n-1) / n, [1, mp.inf], method = "a") + >>> print(mp.chop(v - mp.log(2))) + 0.0 + >>> v = mp.nsum(lambda n: (-1)**n / (2 * n + 1), [0, mp.inf], method = "a") + >>> print(mp.chop(v - mp.pi / 4)) + 0.0 + >>> v = mp.nsum(lambda n: (-1)**n * mp.log(n) * n, [1, mp.inf], method = "a") + >>> print(mp.chop(v - mp.diff(lambda s: mp.altzeta(s), -1))) + 0.0 + + """ + + def __init__(self): + self.last=0 + + def update(self, A): + """ + This routine applies the convergence acceleration to the list of individual terms. + + A = sum(a_k, k = 0..infinity) + + v, e = ...update([a_0, a_1,..., a_k]) + + output: + v current estimate of the series A + e an error estimate which is simply the difference between the current + estimate and the last estimate. + """ + + n = len(A) + d = (3 + self.ctx.sqrt(8)) ** n + d = (d + 1 / d) / 2 + b = -self.ctx.one + c = -d + s = 0 + + for k in xrange(n): + c = b - c + if k % 2 == 0: + s = s + c * A[k] + else: + s = s - c * A[k] + b = 2 * (k + n) * (k - n) * b / ((2 * k + 1) * (k + self.ctx.one)) + + value = s / d + + err = abs(value - self.last) + self.last = value + + return value, err + + def update_psum(self, S): + """ + This routine applies the convergence acceleration to the list of partial sums. + + A = sum(a_k, k = 0..infinity) + s_n = sum(a_k ,k = 0..n) + + v, e = ...update_psum([s_0, s_1,..., s_k]) + + output: + v current estimate of the series A + e an error estimate which is simply the difference between the current + estimate and the last estimate. + """ + + n = len(S) + d = (3 + self.ctx.sqrt(8)) ** n + d = (d + 1 / d) / 2 + b = self.ctx.one + s = 0 + + for k in xrange(n): + b = 2 * (n + k) * (n - k) * b / ((2 * k + 1) * (k + self.ctx.one)) + s += b * S[k] + + value = s / d + + err = abs(value - self.last) + self.last = value + + return value, err + +def cohen_alt(ctx): + L = cohen_alt_class() + L.ctx = ctx + return L + +cohen_alt.__doc__ = cohen_alt_class.__doc__ +defun(cohen_alt) + + +@defun +def sumap(ctx, f, interval, integral=None, error=False): + r""" + Evaluates an infinite series of an analytic summand *f* using the + Abel-Plana formula + + .. math :: + + \sum_{k=0}^{\infty} f(k) = \int_0^{\infty} f(t) dt + \frac{1}{2} f(0) + + i \int_0^{\infty} \frac{f(it)-f(-it)}{e^{2\pi t}-1} dt. + + Unlike the Euler-Maclaurin formula (see :func:`~mpmath.sumem`), + the Abel-Plana formula does not require derivatives. However, + it only works when `|f(it)-f(-it)|` does not + increase too rapidly with `t`. + + **Examples** + + The Abel-Plana formula is particularly useful when the summand + decreases like a power of `k`; for example when the sum is a pure + zeta function:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> sumap(lambda k: 1/k**2.5, [1,inf]) + 1.34148725725091717975677 + >>> zeta(2.5) + 1.34148725725091717975677 + >>> sumap(lambda k: 1/(k+1j)**(2.5+2.5j), [1,inf]) + (-3.385361068546473342286084 - 0.7432082105196321803869551j) + >>> zeta(2.5+2.5j, 1+1j) + (-3.385361068546473342286084 - 0.7432082105196321803869551j) + + If the series is alternating, numerical quadrature along the real + line is likely to give poor results, so it is better to evaluate + the first term symbolically whenever possible: + + >>> n=3; z=-0.75 + >>> I = expint(n,-log(z)) + >>> chop(sumap(lambda k: z**k / k**n, [1,inf], integral=I)) + -0.6917036036904594510141448 + >>> polylog(n,z) + -0.6917036036904594510141448 + + """ + prec = ctx.prec + try: + ctx.prec += 10 + a, b = interval + if b != ctx.inf: + raise ValueError("b should be equal to ctx.inf") + g = lambda x: f(x+a) + if integral is None: + i1, err1 = ctx.quad(g, [0,ctx.inf], error=True) + else: + i1, err1 = integral, 0 + j = ctx.j + p = ctx.pi * 2 + if ctx._is_real_type(i1): + h = lambda t: -2 * ctx.im(g(j*t)) / ctx.expm1(p*t) + else: + h = lambda t: j*(g(j*t)-g(-j*t)) / ctx.expm1(p*t) + i2, err2 = ctx.quad(h, [0,ctx.inf], error=True) + err = err1+err2 + v = i1+i2+0.5*g(ctx.mpf(0)) + finally: + ctx.prec = prec + if error: + return +v, err + return +v + + +@defun +def sumem(ctx, f, interval, tol=None, reject=10, integral=None, + adiffs=None, bdiffs=None, verbose=False, error=False, + _fast_abort=False): + r""" + Uses the Euler-Maclaurin formula to compute an approximation accurate + to within ``tol`` (which defaults to the present epsilon) of the sum + + .. math :: + + S = \sum_{k=a}^b f(k) + + where `(a,b)` are given by ``interval`` and `a` or `b` may be + infinite. The approximation is + + .. math :: + + S \sim \int_a^b f(x) \,dx + \frac{f(a)+f(b)}{2} + + \sum_{k=1}^{\infty} \frac{B_{2k}}{(2k)!} + \left(f^{(2k-1)}(b)-f^{(2k-1)}(a)\right). + + The last sum in the Euler-Maclaurin formula is not generally + convergent (a notable exception is if `f` is a polynomial, in + which case Euler-Maclaurin actually gives an exact result). + + The summation is stopped as soon as the quotient between two + consecutive terms falls below *reject*. That is, by default + (*reject* = 10), the summation is continued as long as each + term adds at least one decimal. + + Although not convergent, convergence to a given tolerance can + often be "forced" if `b = \infty` by summing up to `a+N` and then + applying the Euler-Maclaurin formula to the sum over the range + `(a+N+1, \ldots, \infty)`. This procedure is implemented by + :func:`~mpmath.nsum`. + + By default numerical quadrature and differentiation is used. + If the symbolic values of the integral and endpoint derivatives + are known, it is more efficient to pass the value of the + integral explicitly as ``integral`` and the derivatives + explicitly as ``adiffs`` and ``bdiffs``. The derivatives + should be given as iterables that yield + `f(a), f'(a), f''(a), \ldots` (and the equivalent for `b`). + + **Examples** + + Summation of an infinite series, with automatic and symbolic + integral and derivative values (the second should be much faster):: + + >>> from mpmath import * + >>> mp.dps = 50; mp.pretty = True + >>> sumem(lambda n: 1/n**2, [32, inf]) + 0.03174336652030209012658168043874142714132886413417 + >>> I = mpf(1)/32 + >>> D = adiffs=((-1)**n*fac(n+1)*32**(-2-n) for n in range(999)) + >>> sumem(lambda n: 1/n**2, [32, inf], integral=I, adiffs=D) + 0.03174336652030209012658168043874142714132886413417 + + An exact evaluation of a finite polynomial sum:: + + >>> sumem(lambda n: n**5-12*n**2+3*n, [-100000, 200000]) + 10500155000624963999742499550000.0 + >>> print(sum(n**5-12*n**2+3*n for n in range(-100000, 200001))) + 10500155000624963999742499550000 + + """ + tol = tol or +ctx.eps + interval = ctx._as_points(interval) + a = ctx.convert(interval[0]) + b = ctx.convert(interval[-1]) + err = ctx.zero + prev = 0 + M = 10000 + if a == ctx.ninf: adiffs = (0 for n in xrange(M)) + else: adiffs = adiffs or ctx.diffs(f, a) + if b == ctx.inf: bdiffs = (0 for n in xrange(M)) + else: bdiffs = bdiffs or ctx.diffs(f, b) + orig = ctx.prec + #verbose = 1 + try: + ctx.prec += 10 + s = ctx.zero + for k, (da, db) in enumerate(izip(adiffs, bdiffs)): + if k & 1: + term = (db-da) * ctx.bernoulli(k+1) / ctx.factorial(k+1) + mag = abs(term) + if verbose: + print("term", k, "magnitude =", ctx.nstr(mag)) + if k > 4 and mag < tol: + s += term + break + elif k > 4 and abs(prev) / mag < reject: + err += mag + if _fast_abort: + return [s, (s, err)][error] + if verbose: + print("Failed to converge") + break + else: + s += term + prev = term + # Endpoint correction + if a != ctx.ninf: s += f(a)/2 + if b != ctx.inf: s += f(b)/2 + # Tail integral + if verbose: + print("Integrating f(x) from x = %s to %s" % (ctx.nstr(a), ctx.nstr(b))) + if integral: + s += integral + else: + integral, ierr = ctx.quad(f, interval, error=True) + if verbose: + print("Integration error:", ierr) + s += integral + err += ierr + finally: + ctx.prec = orig + if error: + return s, err + else: + return s + +@defun +def adaptive_extrapolation(ctx, update, emfun, kwargs): + option = kwargs.get + if ctx._fixed_precision: + tol = option('tol', ctx.eps*2**10) + else: + tol = option('tol', ctx.eps/2**10) + verbose = option('verbose', False) + maxterms = option('maxterms', ctx.dps*10) + method = set(option('method', 'r+s').split('+')) + skip = option('skip', 0) + steps = iter(option('steps', xrange(10, 10**9, 10))) + strict = option('strict') + #steps = (10 for i in xrange(1000)) + summer=[] + if 'd' in method or 'direct' in method: + TRY_RICHARDSON = TRY_SHANKS = TRY_EULER_MACLAURIN = False + else: + TRY_RICHARDSON = ('r' in method) or ('richardson' in method) + TRY_SHANKS = ('s' in method) or ('shanks' in method) + TRY_EULER_MACLAURIN = ('e' in method) or \ + ('euler-maclaurin' in method) + + def init_levin(m): + variant = kwargs.get("levin_variant", "u") + if isinstance(variant, str): + if variant == "all": + variant = ["u", "v", "t"] + else: + variant = [variant] + for s in variant: + L = levin_class(method = m, variant = s) + L.ctx = ctx + L.name = m + "(" + s + ")" + summer.append(L) + + if ('l' in method) or ('levin' in method): + init_levin("levin") + + if ('sidi' in method): + init_levin("sidi") + + if ('a' in method) or ('alternating' in method): + L = cohen_alt_class() + L.ctx = ctx + L.name = "alternating" + summer.append(L) + + last_richardson_value = 0 + shanks_table = [] + index = 0 + step = 10 + partial = [] + best = ctx.zero + orig = ctx.prec + try: + if 'workprec' in kwargs: + ctx.prec = kwargs['workprec'] + elif TRY_RICHARDSON or TRY_SHANKS or len(summer)!=0: + ctx.prec = (ctx.prec+10) * 4 + else: + ctx.prec += 30 + while 1: + if index >= maxterms: + break + + # Get new batch of terms + try: + step = next(steps) + except StopIteration: + pass + if verbose: + print("-"*70) + print("Adding terms #%i-#%i" % (index, index+step)) + update(partial, xrange(index, index+step)) + index += step + + # Check direct error + best = partial[-1] + error = abs(best - partial[-2]) + if verbose: + print("Direct error: %s" % ctx.nstr(error)) + if error <= tol: + return best + + # Check each extrapolation method + if TRY_RICHARDSON: + value, maxc = ctx.richardson(partial) + # Convergence + richardson_error = abs(value - last_richardson_value) + if verbose: + print("Richardson error: %s" % ctx.nstr(richardson_error)) + # Convergence + if richardson_error <= tol: + return value + last_richardson_value = value + # Unreliable due to cancellation + if ctx.eps*maxc > tol: + if verbose: + print("Ran out of precision for Richardson") + TRY_RICHARDSON = False + if richardson_error < error: + error = richardson_error + best = value + if TRY_SHANKS: + shanks_table = ctx.shanks(partial, shanks_table, randomized=True) + row = shanks_table[-1] + if len(row) == 2: + est1 = row[-1] + shanks_error = 0 + else: + est1, maxc, est2 = row[-1], abs(row[-2]), row[-3] + shanks_error = abs(est1-est2) + if verbose: + print("Shanks error: %s" % ctx.nstr(shanks_error)) + if shanks_error <= tol: + return est1 + if ctx.eps*maxc > tol: + if verbose: + print("Ran out of precision for Shanks") + TRY_SHANKS = False + if shanks_error < error: + error = shanks_error + best = est1 + for L in summer: + est, lerror = L.update_psum(partial) + if verbose: + print("%s error: %s" % (L.name, ctx.nstr(lerror))) + if lerror <= tol: + return est + if lerror < error: + error = lerror + best = est + if TRY_EULER_MACLAURIN: + if ctx.almosteq(ctx.mpc(ctx.sign(partial[-1]) / ctx.sign(partial[-2])), -1): + if verbose: + print ("NOT using Euler-Maclaurin: the series appears" + " to be alternating, so numerical\n quadrature" + " will most likely fail") + TRY_EULER_MACLAURIN = False + else: + value, em_error = emfun(index, tol) + value += partial[-1] + if verbose: + print("Euler-Maclaurin error: %s" % ctx.nstr(em_error)) + if em_error <= tol: + return value + if em_error < error: + best = value + finally: + ctx.prec = orig + if strict: + raise ctx.NoConvergence + if verbose: + print("Warning: failed to converge to target accuracy") + return best + +@defun +def nsum(ctx, f, *intervals, **options): + r""" + Computes the sum + + .. math :: S = \sum_{k=a}^b f(k) + + where `(a, b)` = *interval*, and where `a = -\infty` and/or + `b = \infty` are allowed, or more generally + + .. math :: S = \sum_{k_1=a_1}^{b_1} \cdots + \sum_{k_n=a_n}^{b_n} f(k_1,\ldots,k_n) + + if multiple intervals are given. + + Two examples of infinite series that can be summed by :func:`~mpmath.nsum`, + where the first converges rapidly and the second converges slowly, + are:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> nsum(lambda n: 1/fac(n), [0, inf]) + 2.71828182845905 + >>> nsum(lambda n: 1/n**2, [1, inf]) + 1.64493406684823 + + When appropriate, :func:`~mpmath.nsum` applies convergence acceleration to + accurately estimate the sums of slowly convergent series. If the series is + finite, :func:`~mpmath.nsum` currently does not attempt to perform any + extrapolation, and simply calls :func:`~mpmath.fsum`. + + Multidimensional infinite series are reduced to a single-dimensional + series over expanding hypercubes; if both infinite and finite dimensions + are present, the finite ranges are moved innermost. For more advanced + control over the summation order, use nested calls to :func:`~mpmath.nsum`, + or manually rewrite the sum as a single-dimensional series. + + **Options** + + *tol* + Desired maximum final error. Defaults roughly to the + epsilon of the working precision. + + *method* + Which summation algorithm to use (described below). + Default: ``'richardson+shanks'``. + + *maxterms* + Cancel after at most this many terms. Default: 10*dps. + + *steps* + An iterable giving the number of terms to add between + each extrapolation attempt. The default sequence is + [10, 20, 30, 40, ...]. For example, if you know that + approximately 100 terms will be required, efficiency might be + improved by setting this to [100, 10]. Then the first + extrapolation will be performed after 100 terms, the second + after 110, etc. + + *verbose* + Print details about progress. + + *ignore* + If enabled, any term that raises ``ArithmeticError`` + or ``ValueError`` (e.g. through division by zero) is replaced + by a zero. This is convenient for lattice sums with + a singular term near the origin. + + **Methods** + + Unfortunately, an algorithm that can efficiently sum any infinite + series does not exist. :func:`~mpmath.nsum` implements several different + algorithms that each work well in different cases. The *method* + keyword argument selects a method. + + The default method is ``'r+s'``, i.e. both Richardson extrapolation + and Shanks transformation is attempted. A slower method that + handles more cases is ``'r+s+e'``. For very high precision + summation, or if the summation needs to be fast (for example if + multiple sums need to be evaluated), it is a good idea to + investigate which one method works best and only use that. + + ``'richardson'`` / ``'r'``: + Uses Richardson extrapolation. Provides useful extrapolation + when `f(k) \sim P(k)/Q(k)` or when `f(k) \sim (-1)^k P(k)/Q(k)` + for polynomials `P` and `Q`. See :func:`~mpmath.richardson` for + additional information. + + ``'shanks'`` / ``'s'``: + Uses Shanks transformation. Typically provides useful + extrapolation when `f(k) \sim c^k` or when successive terms + alternate signs. Is able to sum some divergent series. + See :func:`~mpmath.shanks` for additional information. + + ``'levin'`` / ``'l'``: + Uses the Levin transformation. It performs better than the Shanks + transformation for logarithmic convergent or alternating divergent + series. The ``'levin_variant'``-keyword selects the variant. Valid + choices are "u", "t", "v" and "all" whereby "all" uses all three + u,t and v simultanously (This is good for performance comparison in + conjunction with "verbose=True"). Instead of the Levin transform one can + also use the Sidi-S transform by selecting the method ``'sidi'``. + See :func:`~mpmath.levin` for additional details. + + ``'alternating'`` / ``'a'``: + This is the convergence acceleration of alternating series developped + by Cohen, Villegras and Zagier. + See :func:`~mpmath.cohen_alt` for additional details. + + ``'euler-maclaurin'`` / ``'e'``: + Uses the Euler-Maclaurin summation formula to approximate + the remainder sum by an integral. This requires high-order + numerical derivatives and numerical integration. The advantage + of this algorithm is that it works regardless of the + decay rate of `f`, as long as `f` is sufficiently smooth. + See :func:`~mpmath.sumem` for additional information. + + ``'direct'`` / ``'d'``: + Does not perform any extrapolation. This can be used + (and should only be used for) rapidly convergent series. + The summation automatically stops when the terms + decrease below the target tolerance. + + **Basic examples** + + A finite sum:: + + >>> nsum(lambda k: 1/k, [1, 6]) + 2.45 + + Summation of a series going to negative infinity and a doubly + infinite series:: + + >>> nsum(lambda k: 1/k**2, [-inf, -1]) + 1.64493406684823 + >>> nsum(lambda k: 1/(1+k**2), [-inf, inf]) + 3.15334809493716 + + :func:`~mpmath.nsum` handles sums of complex numbers:: + + >>> nsum(lambda k: (0.5+0.25j)**k, [0, inf]) + (1.6 + 0.8j) + + The following sum converges very rapidly, so it is most + efficient to sum it by disabling convergence acceleration:: + + >>> mp.dps = 1000 + >>> a = nsum(lambda k: -(-1)**k * k**2 / fac(2*k), [1, inf], + ... method='direct') + >>> b = (cos(1)+sin(1))/4 + >>> abs(a-b) < mpf('1e-998') + True + + **Examples with Richardson extrapolation** + + Richardson extrapolation works well for sums over rational + functions, as well as their alternating counterparts:: + + >>> mp.dps = 50 + >>> nsum(lambda k: 1 / k**3, [1, inf], + ... method='richardson') + 1.2020569031595942853997381615114499907649862923405 + >>> zeta(3) + 1.2020569031595942853997381615114499907649862923405 + + >>> nsum(lambda n: (n + 3)/(n**3 + n**2), [1, inf], + ... method='richardson') + 2.9348022005446793094172454999380755676568497036204 + >>> pi**2/2-2 + 2.9348022005446793094172454999380755676568497036204 + + >>> nsum(lambda k: (-1)**k / k**3, [1, inf], + ... method='richardson') + -0.90154267736969571404980362113358749307373971925537 + >>> -3*zeta(3)/4 + -0.90154267736969571404980362113358749307373971925538 + + **Examples with Shanks transformation** + + The Shanks transformation works well for geometric series + and typically provides excellent acceleration for Taylor + series near the border of their disk of convergence. + Here we apply it to a series for `\log(2)`, which can be + seen as the Taylor series for `\log(1+x)` with `x = 1`:: + + >>> nsum(lambda k: -(-1)**k/k, [1, inf], + ... method='shanks') + 0.69314718055994530941723212145817656807550013436025 + >>> log(2) + 0.69314718055994530941723212145817656807550013436025 + + Here we apply it to a slowly convergent geometric series:: + + >>> nsum(lambda k: mpf('0.995')**k, [0, inf], + ... method='shanks') + 200.0 + + Finally, Shanks' method works very well for alternating series + where `f(k) = (-1)^k g(k)`, and often does so regardless of + the exact decay rate of `g(k)`:: + + >>> mp.dps = 15 + >>> nsum(lambda k: (-1)**(k+1) / k**1.5, [1, inf], + ... method='shanks') + 0.765147024625408 + >>> (2-sqrt(2))*zeta(1.5)/2 + 0.765147024625408 + + The following slowly convergent alternating series has no known + closed-form value. Evaluating the sum a second time at higher + precision indicates that the value is probably correct:: + + >>> nsum(lambda k: (-1)**k / log(k), [2, inf], + ... method='shanks') + 0.924299897222939 + >>> mp.dps = 30 + >>> nsum(lambda k: (-1)**k / log(k), [2, inf], + ... method='shanks') + 0.92429989722293885595957018136 + + **Examples with Levin transformation** + + The following example calculates Euler's constant as the constant term in + the Laurent expansion of zeta(s) at s=1. This sum converges extremly slow + because of the logarithmic convergence behaviour of the Dirichlet series + for zeta. + + >>> mp.dps = 30 + >>> z = mp.mpf(10) ** (-10) + >>> a = mp.nsum(lambda n: n**(-(1+z)), [1, mp.inf], method = "levin") - 1 / z + >>> print(mp.chop(a - mp.euler, tol = 1e-10)) + 0.0 + + Now we sum the zeta function outside its range of convergence + (attention: This does not work at the negative integers!): + + >>> mp.dps = 15 + >>> w = mp.nsum(lambda n: n ** (2 + 3j), [1, mp.inf], method = "levin", levin_variant = "v") + >>> print(mp.chop(w - mp.zeta(-2-3j))) + 0.0 + + The next example resummates an asymptotic series expansion of an integral + related to the exponential integral. + + >>> mp.dps = 15 + >>> z = mp.mpf(10) + >>> # exact = mp.quad(lambda x: mp.exp(-x)/(1+x/z),[0,mp.inf]) + >>> exact = z * mp.exp(z) * mp.expint(1,z) # this is the symbolic expression for the integral + >>> w = mp.nsum(lambda n: (-1) ** n * mp.fac(n) * z ** (-n), [0, mp.inf], method = "sidi", levin_variant = "t") + >>> print(mp.chop(w - exact)) + 0.0 + + Following highly divergent asymptotic expansion needs some care. Firstly we + need copious amount of working precision. Secondly the stepsize must not be + chosen to large, otherwise nsum may miss the point where the Levin transform + converges and reach the point where only numerical garbage is produced due to + numerical cancellation. + + >>> mp.dps = 15 + >>> z = mp.mpf(2) + >>> # exact = mp.quad(lambda x: mp.exp( -x * x / 2 - z * x ** 4), [0,mp.inf]) * 2 / mp.sqrt(2 * mp.pi) + >>> exact = mp.exp(mp.one / (32 * z)) * mp.besselk(mp.one / 4, mp.one / (32 * z)) / (4 * mp.sqrt(z * mp.pi)) # this is the symbolic expression for the integral + >>> w = mp.nsum(lambda n: (-z)**n * mp.fac(4 * n) / (mp.fac(n) * mp.fac(2 * n) * (4 ** n)), + ... [0, mp.inf], method = "levin", levin_variant = "t", workprec = 8*mp.prec, steps = [2] + [1 for x in xrange(1000)]) + >>> print(mp.chop(w - exact)) + 0.0 + + The hypergeoemtric function can also be summed outside its range of convergence: + + >>> mp.dps = 15 + >>> z = 2 + 1j + >>> exact = mp.hyp2f1(2 / mp.mpf(3), 4 / mp.mpf(3), 1 / mp.mpf(3), z) + >>> f = lambda n: mp.rf(2 / mp.mpf(3), n) * mp.rf(4 / mp.mpf(3), n) * z**n / (mp.rf(1 / mp.mpf(3), n) * mp.fac(n)) + >>> v = mp.nsum(f, [0, mp.inf], method = "levin", steps = [10 for x in xrange(1000)]) + >>> print(mp.chop(exact-v)) + 0.0 + + **Examples with Cohen's alternating series resummation** + + The next example sums the alternating zeta function: + + >>> v = mp.nsum(lambda n: (-1)**(n-1) / n, [1, mp.inf], method = "a") + >>> print(mp.chop(v - mp.log(2))) + 0.0 + + The derivate of the alternating zeta function outside its range of + convergence: + + >>> v = mp.nsum(lambda n: (-1)**n * mp.log(n) * n, [1, mp.inf], method = "a") + >>> print(mp.chop(v - mp.diff(lambda s: mp.altzeta(s), -1))) + 0.0 + + **Examples with Euler-Maclaurin summation** + + The sum in the following example has the wrong rate of convergence + for either Richardson or Shanks to be effective. + + >>> f = lambda k: log(k)/k**2.5 + >>> mp.dps = 15 + >>> nsum(f, [1, inf], method='euler-maclaurin') + 0.38734195032621 + >>> -diff(zeta, 2.5) + 0.38734195032621 + + Increasing ``steps`` improves speed at higher precision:: + + >>> mp.dps = 50 + >>> nsum(f, [1, inf], method='euler-maclaurin', steps=[250]) + 0.38734195032620997271199237593105101319948228874688 + >>> -diff(zeta, 2.5) + 0.38734195032620997271199237593105101319948228874688 + + **Divergent series** + + The Shanks transformation is able to sum some *divergent* + series. In particular, it is often able to sum Taylor series + beyond their radius of convergence (this is due to a relation + between the Shanks transformation and Pade approximations; + see :func:`~mpmath.pade` for an alternative way to evaluate divergent + Taylor series). Furthermore the Levin-transform examples above + contain some divergent series resummation. + + Here we apply it to `\log(1+x)` far outside the region of + convergence:: + + >>> mp.dps = 50 + >>> nsum(lambda k: -(-9)**k/k, [1, inf], + ... method='shanks') + 2.3025850929940456840179914546843642076011014886288 + >>> log(10) + 2.3025850929940456840179914546843642076011014886288 + + A particular type of divergent series that can be summed + using the Shanks transformation is geometric series. + The result is the same as using the closed-form formula + for an infinite geometric series:: + + >>> mp.dps = 15 + >>> for n in range(-8, 8): + ... if n == 1: + ... continue + ... print("%s %s %s" % (mpf(n), mpf(1)/(1-n), + ... nsum(lambda k: n**k, [0, inf], method='shanks'))) + ... + -8.0 0.111111111111111 0.111111111111111 + -7.0 0.125 0.125 + -6.0 0.142857142857143 0.142857142857143 + -5.0 0.166666666666667 0.166666666666667 + -4.0 0.2 0.2 + -3.0 0.25 0.25 + -2.0 0.333333333333333 0.333333333333333 + -1.0 0.5 0.5 + 0.0 1.0 1.0 + 2.0 -1.0 -1.0 + 3.0 -0.5 -0.5 + 4.0 -0.333333333333333 -0.333333333333333 + 5.0 -0.25 -0.25 + 6.0 -0.2 -0.2 + 7.0 -0.166666666666667 -0.166666666666667 + + **Multidimensional sums** + + Any combination of finite and infinite ranges is allowed for the + summation indices:: + + >>> mp.dps = 15 + >>> nsum(lambda x,y: x+y, [2,3], [4,5]) + 28.0 + >>> nsum(lambda x,y: x/2**y, [1,3], [1,inf]) + 6.0 + >>> nsum(lambda x,y: y/2**x, [1,inf], [1,3]) + 6.0 + >>> nsum(lambda x,y,z: z/(2**x*2**y), [1,inf], [1,inf], [3,4]) + 7.0 + >>> nsum(lambda x,y,z: y/(2**x*2**z), [1,inf], [3,4], [1,inf]) + 7.0 + >>> nsum(lambda x,y,z: x/(2**z*2**y), [3,4], [1,inf], [1,inf]) + 7.0 + + Some nice examples of double series with analytic solutions or + reductions to single-dimensional series (see [1]):: + + >>> nsum(lambda m, n: 1/2**(m*n), [1,inf], [1,inf]) + 1.60669515241529 + >>> nsum(lambda n: 1/(2**n-1), [1,inf]) + 1.60669515241529 + + >>> nsum(lambda i,j: (-1)**(i+j)/(i**2+j**2), [1,inf], [1,inf]) + 0.278070510848213 + >>> pi*(pi-3*ln2)/12 + 0.278070510848213 + + >>> nsum(lambda i,j: (-1)**(i+j)/(i+j)**2, [1,inf], [1,inf]) + 0.129319852864168 + >>> altzeta(2) - altzeta(1) + 0.129319852864168 + + >>> nsum(lambda i,j: (-1)**(i+j)/(i+j)**3, [1,inf], [1,inf]) + 0.0790756439455825 + >>> altzeta(3) - altzeta(2) + 0.0790756439455825 + + >>> nsum(lambda m,n: m**2*n/(3**m*(n*3**m+m*3**n)), + ... [1,inf], [1,inf]) + 0.28125 + >>> mpf(9)/32 + 0.28125 + + >>> nsum(lambda i,j: fac(i-1)*fac(j-1)/fac(i+j), + ... [1,inf], [1,inf], workprec=400) + 1.64493406684823 + >>> zeta(2) + 1.64493406684823 + + A hard example of a multidimensional sum is the Madelung constant + in three dimensions (see [2]). The defining sum converges very + slowly and only conditionally, so :func:`~mpmath.nsum` is lucky to + obtain an accurate value through convergence acceleration. The + second evaluation below uses a much more efficient, rapidly + convergent 2D sum:: + + >>> nsum(lambda x,y,z: (-1)**(x+y+z)/(x*x+y*y+z*z)**0.5, + ... [-inf,inf], [-inf,inf], [-inf,inf], ignore=True) + -1.74756459463318 + >>> nsum(lambda x,y: -12*pi*sech(0.5*pi * \ + ... sqrt((2*x+1)**2+(2*y+1)**2))**2, [0,inf], [0,inf]) + -1.74756459463318 + + Another example of a lattice sum in 2D:: + + >>> nsum(lambda x,y: (-1)**(x+y) / (x**2+y**2), [-inf,inf], + ... [-inf,inf], ignore=True) + -2.1775860903036 + >>> -pi*ln2 + -2.1775860903036 + + An example of an Eisenstein series:: + + >>> nsum(lambda m,n: (m+n*1j)**(-4), [-inf,inf], [-inf,inf], + ... ignore=True) + (3.1512120021539 + 0.0j) + + **References** + + 1. [Weisstein]_ http://mathworld.wolfram.com/DoubleSeries.html, + 2. [Weisstein]_ http://mathworld.wolfram.com/MadelungConstants.html + + """ + infinite, g = standardize(ctx, f, intervals, options) + if not infinite: + return +g() + + def update(partial_sums, indices): + if partial_sums: + psum = partial_sums[-1] + else: + psum = ctx.zero + for k in indices: + psum = psum + g(ctx.mpf(k)) + partial_sums.append(psum) + + prec = ctx.prec + + def emfun(point, tol): + workprec = ctx.prec + ctx.prec = prec + 10 + v = ctx.sumem(g, [point, ctx.inf], tol, error=1) + ctx.prec = workprec + return v + + return +ctx.adaptive_extrapolation(update, emfun, options) + + +def wrapsafe(f): + def g(*args): + try: + return f(*args) + except (ArithmeticError, ValueError): + return 0 + return g + +def standardize(ctx, f, intervals, options): + if options.get("ignore"): + f = wrapsafe(f) + finite = [] + infinite = [] + for k, points in enumerate(intervals): + a, b = ctx._as_points(points) + if b < a: + return False, (lambda: ctx.zero) + if a == ctx.ninf or b == ctx.inf: + infinite.append((k, (a,b))) + else: + finite.append((k, (int(a), int(b)))) + if finite: + f = fold_finite(ctx, f, finite) + if not infinite: + return False, lambda: f(*([0]*len(intervals))) + if infinite: + f = standardize_infinite(ctx, f, infinite) + f = fold_infinite(ctx, f, infinite) + args = [0] * len(intervals) + d = infinite[0][0] + def g(k): + args[d] = k + return f(*args) + return True, g + +# backwards compatible itertools.product +def cartesian_product(args): + pools = map(tuple, args) + result = [[]] + for pool in pools: + result = [x+[y] for x in result for y in pool] + for prod in result: + yield tuple(prod) + +def fold_finite(ctx, f, intervals): + if not intervals: + return f + indices = [v[0] for v in intervals] + points = [v[1] for v in intervals] + ranges = [xrange(a, b+1) for (a,b) in points] + def g(*args): + args = list(args) + s = ctx.zero + for xs in cartesian_product(ranges): + for dim, x in zip(indices, xs): + args[dim] = ctx.mpf(x) + s += f(*args) + return s + #print "Folded finite", indices + return g + +# Standardize each interval to [0,inf] +def standardize_infinite(ctx, f, intervals): + if not intervals: + return f + dim, [a,b] = intervals[-1] + if a == ctx.ninf: + if b == ctx.inf: + def g(*args): + args = list(args) + k = args[dim] + if k: + s = f(*args) + args[dim] = -k + s += f(*args) + return s + else: + return f(*args) + else: + def g(*args): + args = list(args) + args[dim] = b - args[dim] + return f(*args) + else: + def g(*args): + args = list(args) + args[dim] += a + return f(*args) + #print "Standardized infinity along dimension", dim, a, b + return standardize_infinite(ctx, g, intervals[:-1]) + +def fold_infinite(ctx, f, intervals): + if len(intervals) < 2: + return f + dim1 = intervals[-2][0] + dim2 = intervals[-1][0] + # Assume intervals are [0,inf] x [0,inf] x ... + def g(*args): + args = list(args) + #args.insert(dim2, None) + n = int(args[dim1]) + s = ctx.zero + #y = ctx.mpf(n) + args[dim2] = ctx.mpf(n) #y + for x in xrange(n+1): + args[dim1] = ctx.mpf(x) + s += f(*args) + args[dim1] = ctx.mpf(n) #ctx.mpf(n) + for y in xrange(n): + args[dim2] = ctx.mpf(y) + s += f(*args) + return s + #print "Folded infinite from", len(intervals), "to", (len(intervals)-1) + return fold_infinite(ctx, g, intervals[:-1]) + +@defun +def nprod(ctx, f, interval, nsum=False, **kwargs): + r""" + Computes the product + + .. math :: + + P = \prod_{k=a}^b f(k) + + where `(a, b)` = *interval*, and where `a = -\infty` and/or + `b = \infty` are allowed. + + By default, :func:`~mpmath.nprod` uses the same extrapolation methods as + :func:`~mpmath.nsum`, except applied to the partial products rather than + partial sums, and the same keyword options as for :func:`~mpmath.nsum` are + supported. If ``nsum=True``, the product is instead computed via + :func:`~mpmath.nsum` as + + .. math :: + + P = \exp\left( \sum_{k=a}^b \log(f(k)) \right). + + This is slower, but can sometimes yield better results. It is + also required (and used automatically) when Euler-Maclaurin + summation is requested. + + **Examples** + + A simple finite product:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> nprod(lambda k: k, [1, 4]) + 24.0 + + A large number of infinite products have known exact values, + and can therefore be used as a reference. Most of the following + examples are taken from MathWorld [1]. + + A few infinite products with simple values are:: + + >>> 2*nprod(lambda k: (4*k**2)/(4*k**2-1), [1, inf]) + 3.141592653589793238462643 + >>> nprod(lambda k: (1+1/k)**2/(1+2/k), [1, inf]) + 2.0 + >>> nprod(lambda k: (k**3-1)/(k**3+1), [2, inf]) + 0.6666666666666666666666667 + >>> nprod(lambda k: (1-1/k**2), [2, inf]) + 0.5 + + Next, several more infinite products with more complicated + values:: + + >>> nprod(lambda k: exp(1/k**2), [1, inf]); exp(pi**2/6) + 5.180668317897115748416626 + 5.180668317897115748416626 + + >>> nprod(lambda k: (k**2-1)/(k**2+1), [2, inf]); pi*csch(pi) + 0.2720290549821331629502366 + 0.2720290549821331629502366 + + >>> nprod(lambda k: (k**4-1)/(k**4+1), [2, inf]) + 0.8480540493529003921296502 + >>> pi*sinh(pi)/(cosh(sqrt(2)*pi)-cos(sqrt(2)*pi)) + 0.8480540493529003921296502 + + >>> nprod(lambda k: (1+1/k+1/k**2)**2/(1+2/k+3/k**2), [1, inf]) + 1.848936182858244485224927 + >>> 3*sqrt(2)*cosh(pi*sqrt(3)/2)**2*csch(pi*sqrt(2))/pi + 1.848936182858244485224927 + + >>> nprod(lambda k: (1-1/k**4), [2, inf]); sinh(pi)/(4*pi) + 0.9190194775937444301739244 + 0.9190194775937444301739244 + + >>> nprod(lambda k: (1-1/k**6), [2, inf]) + 0.9826842777421925183244759 + >>> (1+cosh(pi*sqrt(3)))/(12*pi**2) + 0.9826842777421925183244759 + + >>> nprod(lambda k: (1+1/k**2), [2, inf]); sinh(pi)/(2*pi) + 1.838038955187488860347849 + 1.838038955187488860347849 + + >>> nprod(lambda n: (1+1/n)**n * exp(1/(2*n)-1), [1, inf]) + 1.447255926890365298959138 + >>> exp(1+euler/2)/sqrt(2*pi) + 1.447255926890365298959138 + + The following two products are equivalent and can be evaluated in + terms of a Jacobi theta function. Pi can be replaced by any value + (as long as convergence is preserved):: + + >>> nprod(lambda k: (1-pi**-k)/(1+pi**-k), [1, inf]) + 0.3838451207481672404778686 + >>> nprod(lambda k: tanh(k*log(pi)/2), [1, inf]) + 0.3838451207481672404778686 + >>> jtheta(4,0,1/pi) + 0.3838451207481672404778686 + + This product does not have a known closed form value:: + + >>> nprod(lambda k: (1-1/2**k), [1, inf]) + 0.2887880950866024212788997 + + A product taken from `-\infty`:: + + >>> nprod(lambda k: 1-k**(-3), [-inf,-2]) + 0.8093965973662901095786805 + >>> cosh(pi*sqrt(3)/2)/(3*pi) + 0.8093965973662901095786805 + + A doubly infinite product:: + + >>> nprod(lambda k: exp(1/(1+k**2)), [-inf, inf]) + 23.41432688231864337420035 + >>> exp(pi/tanh(pi)) + 23.41432688231864337420035 + + A product requiring the use of Euler-Maclaurin summation to compute + an accurate value:: + + >>> nprod(lambda k: (1-1/k**2.5), [2, inf], method='e') + 0.696155111336231052898125 + + **References** + + 1. [Weisstein]_ http://mathworld.wolfram.com/InfiniteProduct.html + + """ + if nsum or ('e' in kwargs.get('method', '')): + orig = ctx.prec + try: + # TODO: we are evaluating log(1+eps) -> eps, which is + # inaccurate. This currently works because nsum greatly + # increases the working precision. But we should be + # more intelligent and handle the precision here. + ctx.prec += 10 + v = ctx.nsum(lambda n: ctx.ln(f(n)), interval, **kwargs) + finally: + ctx.prec = orig + return +ctx.exp(v) + + a, b = ctx._as_points(interval) + if a == ctx.ninf: + if b == ctx.inf: + return f(0) * ctx.nprod(lambda k: f(-k) * f(k), [1, ctx.inf], **kwargs) + return ctx.nprod(f, [-b, ctx.inf], **kwargs) + elif b != ctx.inf: + return ctx.fprod(f(ctx.mpf(k)) for k in xrange(int(a), int(b)+1)) + + a = int(a) + + def update(partial_products, indices): + if partial_products: + pprod = partial_products[-1] + else: + pprod = ctx.one + for k in indices: + pprod = pprod * f(a + ctx.mpf(k)) + partial_products.append(pprod) + + return +ctx.adaptive_extrapolation(update, None, kwargs) + + +@defun +def limit(ctx, f, x, direction=1, exp=False, **kwargs): + r""" + Computes an estimate of the limit + + .. math :: + + \lim_{t \to x} f(t) + + where `x` may be finite or infinite. + + For finite `x`, :func:`~mpmath.limit` evaluates `f(x + d/n)` for + consecutive integer values of `n`, where the approach direction + `d` may be specified using the *direction* keyword argument. + For infinite `x`, :func:`~mpmath.limit` evaluates values of + `f(\mathrm{sign}(x) \cdot n)`. + + If the approach to the limit is not sufficiently fast to give + an accurate estimate directly, :func:`~mpmath.limit` attempts to find + the limit using Richardson extrapolation or the Shanks + transformation. You can select between these methods using + the *method* keyword (see documentation of :func:`~mpmath.nsum` for + more information). + + **Options** + + The following options are available with essentially the + same meaning as for :func:`~mpmath.nsum`: *tol*, *method*, *maxterms*, + *steps*, *verbose*. + + If the option *exp=True* is set, `f` will be + sampled at exponentially spaced points `n = 2^1, 2^2, 2^3, \ldots` + instead of the linearly spaced points `n = 1, 2, 3, \ldots`. + This can sometimes improve the rate of convergence so that + :func:`~mpmath.limit` may return a more accurate answer (and faster). + However, do note that this can only be used if `f` + supports fast and accurate evaluation for arguments that + are extremely close to the limit point (or if infinite, + very large arguments). + + **Examples** + + A basic evaluation of a removable singularity:: + + >>> from mpmath import * + >>> mp.dps = 30; mp.pretty = True + >>> limit(lambda x: (x-sin(x))/x**3, 0) + 0.166666666666666666666666666667 + + Computing the exponential function using its limit definition:: + + >>> limit(lambda n: (1+3/n)**n, inf) + 20.0855369231876677409285296546 + >>> exp(3) + 20.0855369231876677409285296546 + + A limit for `\pi`:: + + >>> f = lambda n: 2**(4*n+1)*fac(n)**4/(2*n+1)/fac(2*n)**2 + >>> limit(f, inf) + 3.14159265358979323846264338328 + + Calculating the coefficient in Stirling's formula:: + + >>> limit(lambda n: fac(n) / (sqrt(n)*(n/e)**n), inf) + 2.50662827463100050241576528481 + >>> sqrt(2*pi) + 2.50662827463100050241576528481 + + Evaluating Euler's constant `\gamma` using the limit representation + + .. math :: + + \gamma = \lim_{n \rightarrow \infty } \left[ \left( + \sum_{k=1}^n \frac{1}{k} \right) - \log(n) \right] + + (which converges notoriously slowly):: + + >>> f = lambda n: sum([mpf(1)/k for k in range(1,int(n)+1)]) - log(n) + >>> limit(f, inf) + 0.577215664901532860606512090082 + >>> +euler + 0.577215664901532860606512090082 + + With default settings, the following limit converges too slowly + to be evaluated accurately. Changing to exponential sampling + however gives a perfect result:: + + >>> f = lambda x: sqrt(x**3+x**2)/(sqrt(x**3)+x) + >>> limit(f, inf) + 0.992831158558330281129249686491 + >>> limit(f, inf, exp=True) + 1.0 + + """ + + if ctx.isinf(x): + direction = ctx.sign(x) + g = lambda k: f(ctx.mpf(k+1)*direction) + else: + direction *= ctx.one + g = lambda k: f(x + direction/(k+1)) + if exp: + h = g + g = lambda k: h(2**k) + + def update(values, indices): + for k in indices: + values.append(g(k+1)) + + # XXX: steps used by nsum don't work well + if not 'steps' in kwargs: + kwargs['steps'] = [10] + + return +ctx.adaptive_extrapolation(update, None, kwargs) diff --git a/lib/python3.12/site-packages/mpmath/calculus/inverselaplace.py b/lib/python3.12/site-packages/mpmath/calculus/inverselaplace.py new file mode 100644 index 0000000000000000000000000000000000000000..d2206b05c1601ee781b09dcbedf3c0fcd89cfa59 --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/calculus/inverselaplace.py @@ -0,0 +1,973 @@ +# contributed to mpmath by Kristopher L. Kuhlman, February 2017 +# contributed to mpmath by Guillermo Navas-Palencia, February 2022 + +class InverseLaplaceTransform(object): + r""" + Inverse Laplace transform methods are implemented using this + class, in order to simplify the code and provide a common + infrastructure. + + Implement a custom inverse Laplace transform algorithm by + subclassing :class:`InverseLaplaceTransform` and implementing the + appropriate methods. The subclass can then be used by + :func:`~mpmath.invertlaplace` by passing it as the *method* + argument. + """ + + def __init__(self, ctx): + self.ctx = ctx + + def calc_laplace_parameter(self, t, **kwargs): + r""" + Determine the vector of Laplace parameter values needed for an + algorithm, this will depend on the choice of algorithm (de + Hoog is default), the algorithm-specific parameters passed (or + default ones), and desired time. + """ + raise NotImplementedError + + def calc_time_domain_solution(self, fp): + r""" + Compute the time domain solution, after computing the + Laplace-space function evaluations at the abscissa required + for the algorithm. Abscissa computed for one algorithm are + typically not useful for another algorithm. + """ + raise NotImplementedError + + +class FixedTalbot(InverseLaplaceTransform): + + def calc_laplace_parameter(self, t, **kwargs): + r"""The "fixed" Talbot method deforms the Bromwich contour towards + `-\infty` in the shape of a parabola. Traditionally the Talbot + algorithm has adjustable parameters, but the "fixed" version + does not. The `r` parameter could be passed in as a parameter, + if you want to override the default given by (Abate & Valko, + 2004). + + The Laplace parameter is sampled along a parabola opening + along the negative imaginary axis, with the base of the + parabola along the real axis at + `p=\frac{r}{t_\mathrm{max}}`. As the number of terms used in + the approximation (degree) grows, the abscissa required for + function evaluation tend towards `-\infty`, requiring high + precision to prevent overflow. If any poles, branch cuts or + other singularities exist such that the deformed Bromwich + contour lies to the left of the singularity, the method will + fail. + + **Optional arguments** + + :class:`~mpmath.calculus.inverselaplace.FixedTalbot.calc_laplace_parameter` + recognizes the following keywords + + *tmax* + maximum time associated with vector of times + (typically just the time requested) + *degree* + integer order of approximation (M = number of terms) + *r* + abscissa for `p_0` (otherwise computed using rule + of thumb `2M/5`) + + The working precision will be increased according to a rule of + thumb. If 'degree' is not specified, the working precision and + degree are chosen to hopefully achieve the dps of the calling + context. If 'degree' is specified, the working precision is + chosen to achieve maximum resulting precision for the + specified degree. + + .. math :: + + p_0=\frac{r}{t} + + .. math :: + + p_i=\frac{i r \pi}{Mt_\mathrm{max}}\left[\cot\left( + \frac{i\pi}{M}\right) + j \right] \qquad 1\le i 0: + self.degree += 1 + + M = self.degree + + # this is adjusting the dps of the calling context + # hopefully the caller doesn't monkey around with it + # between calling this routine and calc_time_domain_solution() + self.dps_orig = self.ctx.dps + self.ctx.dps = self.dps_goal + + self.V = self._coeff() + self.p = self.ctx.matrix(self.ctx.arange(1, M+1))*self.ctx.ln2/self.t + + # NB: p is real (mpf) + + def _coeff(self): + r"""Salzer summation weights (aka, "Stehfest coefficients") + only depend on the approximation order (M) and the precision""" + + M = self.degree + M2 = int(M/2) # checked earlier that M is even + + V = self.ctx.matrix(M, 1) + + # Salzer summation weights + # get very large in magnitude and oscillate in sign, + # if the precision is not high enough, there will be + # catastrophic cancellation + for k in range(1, M+1): + z = self.ctx.matrix(min(k, M2)+1, 1) + for j in range(int((k+1)/2), min(k, M2)+1): + z[j] = (self.ctx.power(j, M2)*self.ctx.fac(2*j)/ + (self.ctx.fac(M2-j)*self.ctx.fac(j)* + self.ctx.fac(j-1)*self.ctx.fac(k-j)* + self.ctx.fac(2*j-k))) + V[k-1] = self.ctx.power(-1, k+M2)*self.ctx.fsum(z) + + return V + + def calc_time_domain_solution(self, fp, t, manual_prec=False): + r"""Compute time-domain Stehfest algorithm solution. + + .. math :: + + f(t,M) = \frac{\log 2}{t} \sum_{k=1}^{M} V_k \bar{f}\left( + p_k \right) + + where + + .. math :: + + V_k = (-1)^{k + N/2} \sum^{\min(k,N/2)}_{i=\lfloor(k+1)/2 \rfloor} + \frac{i^{\frac{N}{2}}(2i)!}{\left(\frac{N}{2}-i \right)! \, i! \, + \left(i-1 \right)! \, \left(k-i\right)! \, \left(2i-k \right)!} + + As the degree increases, the abscissa (`p_k`) only increase + linearly towards `\infty`, but the Stehfest coefficients + (`V_k`) alternate in sign and increase rapidly in sign, + requiring high precision to prevent overflow or loss of + significance when evaluating the sum. + + **References** + + 1. Widder, D. (1941). *The Laplace Transform*. Princeton. + 2. Stehfest, H. (1970). Algorithm 368: numerical inversion of + Laplace transforms. *Communications of the ACM* 13(1):47-49, + http://dx.doi.org/10.1145/361953.361969 + + """ + + # required + self.t = self.ctx.convert(t) + + # assume fp was computed from p matrix returned from + # calc_laplace_parameter(), so is already + # a list or matrix of mpmath 'mpf' types + + result = self.ctx.fdot(self.V, fp)*self.ctx.ln2/self.t + + # setting dps back to value when calc_laplace_parameter was called + if not manual_prec: + self.ctx.dps = self.dps_orig + + # ignore any small imaginary part + return result.real + + +# **************************************** + +class deHoog(InverseLaplaceTransform): + + def calc_laplace_parameter(self, t, **kwargs): + r"""the de Hoog, Knight & Stokes algorithm is an + accelerated form of the Fourier series numerical + inverse Laplace transform algorithms. + + .. math :: + + p_k = \gamma + \frac{jk}{T} \qquad 0 \le k < 2M+1 + + where + + .. math :: + + \gamma = \alpha - \frac{\log \mathrm{tol}}{2T}, + + `j=\sqrt{-1}`, `T = 2t_\mathrm{max}` is a scaled time, + `\alpha=10^{-\mathrm{dps\_goal}}` is the real part of the + rightmost pole or singularity, which is chosen based on the + desired accuracy (assuming the rightmost singularity is 0), + and `\mathrm{tol}=10\alpha` is the desired tolerance, which is + chosen in relation to `\alpha`.` + + When increasing the degree, the abscissa increase towards + `j\infty`, but more slowly than the fixed Talbot + algorithm. The de Hoog et al. algorithm typically does better + with oscillatory functions of time, and less well-behaved + functions. The method tends to be slower than the Talbot and + Stehfest algorithsm, especially so at very high precision + (e.g., `>500` digits precision). + + """ + + # required + # ------------------------------ + self.t = self.ctx.convert(t) + + # optional + # ------------------------------ + self.tmax = kwargs.get('tmax', self.t) + + # empirical relationships used here based on a linear fit of + # requested and delivered dps for exponentially decaying time + # functions for requested dps up to 512. + + if 'degree' in kwargs: + self.degree = kwargs['degree'] + self.dps_goal = int(1.38*self.degree) + else: + self.dps_goal = int(self.ctx.dps*1.36) + self.degree = max(10, self.dps_goal) + + # 2*M+1 terms in approximation + M = self.degree + + # adjust alpha component of abscissa of convergence for higher + # precision + tmp = self.ctx.power(10.0, -self.dps_goal) + self.alpha = self.ctx.convert(kwargs.get('alpha', tmp)) + + # desired tolerance (here simply related to alpha) + self.tol = self.ctx.convert(kwargs.get('tol', self.alpha*10.0)) + self.np = 2*self.degree+1 # number of terms in approximation + + # this is adjusting the dps of the calling context + # hopefully the caller doesn't monkey around with it + # between calling this routine and calc_time_domain_solution() + self.dps_orig = self.ctx.dps + self.ctx.dps = self.dps_goal + + # scaling factor (likely tun-able, but 2 is typical) + self.scale = kwargs.get('scale', 2) + self.T = self.ctx.convert(kwargs.get('T', self.scale*self.tmax)) + + self.p = self.ctx.matrix(2*M+1, 1) + self.gamma = self.alpha - self.ctx.log(self.tol)/(self.scale*self.T) + self.p = (self.gamma + self.ctx.pi* + self.ctx.matrix(self.ctx.arange(self.np))/self.T*1j) + + # NB: p is complex (mpc) + + def calc_time_domain_solution(self, fp, t, manual_prec=False): + r"""Calculate time-domain solution for + de Hoog, Knight & Stokes algorithm. + + The un-accelerated Fourier series approach is: + + .. math :: + + f(t,2M+1) = \frac{e^{\gamma t}}{T} \sum_{k=0}^{2M}{}^{'} + \Re\left[\bar{f}\left( p_k \right) + e^{i\pi t/T} \right], + + where the prime on the summation indicates the first term is halved. + + This simplistic approach requires so many function evaluations + that it is not practical. Non-linear acceleration is + accomplished via Pade-approximation and an analytic expression + for the remainder of the continued fraction. See the original + paper (reference 2 below) a detailed description of the + numerical approach. + + **References** + + 1. Davies, B. (2005). *Integral Transforms and their + Applications*, Third Edition. Springer. + 2. de Hoog, F., J. Knight, A. Stokes (1982). An improved + method for numerical inversion of Laplace transforms. *SIAM + Journal of Scientific and Statistical Computing* 3:357-366, + http://dx.doi.org/10.1137/0903022 + + """ + + M = self.degree + np = self.np + T = self.T + + self.t = self.ctx.convert(t) + + # would it be useful to try re-using + # space between e&q and A&B? + e = self.ctx.zeros(np, M+1) + q = self.ctx.matrix(2*M, M) + d = self.ctx.matrix(np, 1) + A = self.ctx.zeros(np+1, 1) + B = self.ctx.ones(np+1, 1) + + # initialize Q-D table + e[:, 0] = 0.0 + 0j + q[0, 0] = fp[1]/(fp[0]/2) + for i in range(1, 2*M): + q[i, 0] = fp[i+1]/fp[i] + + # rhombus rule for filling triangular Q-D table (e & q) + for r in range(1, M+1): + # start with e, column 1, 0:2*M-2 + mr = 2*(M-r) + 1 + e[0:mr, r] = q[1:mr+1, r-1] - q[0:mr, r-1] + e[1:mr+1, r-1] + if not r == M: + rq = r+1 + mr = 2*(M-rq)+1 + 2 + for i in range(mr): + q[i, rq-1] = q[i+1, rq-2]*e[i+1, rq-1]/e[i, rq-1] + + # build up continued fraction coefficients (d) + d[0] = fp[0]/2 + for r in range(1, M+1): + d[2*r-1] = -q[0, r-1] # even terms + d[2*r] = -e[0, r] # odd terms + + # seed A and B for recurrence + A[0] = 0.0 + 0.0j + A[1] = d[0] + B[0:2] = 1.0 + 0.0j + + # base of the power series + z = self.ctx.expjpi(self.t/T) # i*pi is already in fcn + + # coefficients of Pade approximation (A & B) + # using recurrence for all but last term + for i in range(1, 2*M): + A[i+1] = A[i] + d[i]*A[i-1]*z + B[i+1] = B[i] + d[i]*B[i-1]*z + + # "improved remainder" to continued fraction + brem = (1 + (d[2*M-1] - d[2*M])*z)/2 + # powm1(x,y) computes x^y - 1 more accurately near zero + rem = brem*self.ctx.powm1(1 + d[2*M]*z/brem, + self.ctx.fraction(1, 2)) + + # last term of recurrence using new remainder + A[np] = A[2*M] + rem*A[2*M-1] + B[np] = B[2*M] + rem*B[2*M-1] + + # diagonal Pade approximation + # F=A/B represents accelerated trapezoid rule + result = self.ctx.exp(self.gamma*self.t)/T*(A[np]/B[np]).real + + # setting dps back to value when calc_laplace_parameter was called + if not manual_prec: + self.ctx.dps = self.dps_orig + + return result + + +# **************************************** + +class Cohen(InverseLaplaceTransform): + + def calc_laplace_parameter(self, t, **kwargs): + r"""The Cohen algorithm accelerates the convergence of the nearly + alternating series resulting from the application of the trapezoidal + rule to the Bromwich contour inversion integral. + + .. math :: + + p_k = \frac{\gamma}{2 t} + \frac{\pi i k}{t} \qquad 0 \le k < M + + where + + .. math :: + + \gamma = \frac{2}{3} (d + \log(10) + \log(2 t)), + + `d = \mathrm{dps\_goal}`, which is chosen based on the desired + accuracy using the method developed in [1] to improve numerical + stability. The Cohen algorithm shows robustness similar to the de Hoog + et al. algorithm, but it is faster than the fixed Talbot algorithm. + + **Optional arguments** + + *degree* + integer order of the approximation (M = number of terms) + *alpha* + abscissa for `p_0` (controls the discretization error) + + The working precision will be increased according to a rule of + thumb. If 'degree' is not specified, the working precision and + degree are chosen to hopefully achieve the dps of the calling + context. If 'degree' is specified, the working precision is + chosen to achieve maximum resulting precision for the + specified degree. + + **References** + + 1. P. Glasserman, J. Ruiz-Mata (2006). Computing the credit loss + distribution in the Gaussian copula model: a comparison of methods. + *Journal of Credit Risk* 2(4):33-66, 10.21314/JCR.2006.057 + + """ + self.t = self.ctx.convert(t) + + if 'degree' in kwargs: + self.degree = kwargs['degree'] + self.dps_goal = int(1.5 * self.degree) + else: + self.dps_goal = int(self.ctx.dps * 1.74) + self.degree = max(22, int(1.31 * self.dps_goal)) + + M = self.degree + 1 + + # this is adjusting the dps of the calling context hopefully + # the caller doesn't monkey around with it between calling + # this routine and calc_time_domain_solution() + self.dps_orig = self.ctx.dps + self.ctx.dps = self.dps_goal + + ttwo = 2 * self.t + tmp = self.ctx.dps * self.ctx.log(10) + self.ctx.log(ttwo) + tmp = self.ctx.fraction(2, 3) * tmp + self.alpha = self.ctx.convert(kwargs.get('alpha', tmp)) + + # all but time-dependent part of p + a_t = self.alpha / ttwo + p_t = self.ctx.pi * 1j / self.t + + self.p = self.ctx.matrix(M, 1) + self.p[0] = a_t + + for i in range(1, M): + self.p[i] = a_t + i * p_t + + def calc_time_domain_solution(self, fp, t, manual_prec=False): + r"""Calculate time-domain solution for Cohen algorithm. + + The accelerated nearly alternating series is: + + .. math :: + + f(t, M) = \frac{e^{\gamma / 2}}{t} \left[\frac{1}{2} + \Re\left(\bar{f}\left(\frac{\gamma}{2t}\right) \right) - + \sum_{k=0}^{M-1}\frac{c_{M,k}}{d_M}\Re\left(\bar{f} + \left(\frac{\gamma + 2(k+1) \pi i}{2t}\right)\right)\right], + + where coefficients `\frac{c_{M, k}}{d_M}` are described in [1]. + + 1. H. Cohen, F. Rodriguez Villegas, D. Zagier (2000). Convergence + acceleration of alternating series. *Experiment. Math* 9(1):3-12 + + """ + self.t = self.ctx.convert(t) + + n = self.degree + M = n + 1 + + A = self.ctx.matrix(M, 1) + for i in range(M): + A[i] = fp[i].real + + d = (3 + self.ctx.sqrt(8)) ** n + d = (d + 1 / d) / 2 + b = -self.ctx.one + c = -d + s = 0 + + for k in range(n): + c = b - c + s = s + c * A[k + 1] + b = 2 * (k + n) * (k - n) * b / ((2 * k + 1) * (k + self.ctx.one)) + + result = self.ctx.exp(self.alpha / 2) / self.t * (A[0] / 2 - s / d) + + # setting dps back to value when calc_laplace_parameter was + # called, unless flag is set. + if not manual_prec: + self.ctx.dps = self.dps_orig + + return result + + +# **************************************** + +class LaplaceTransformInversionMethods(object): + def __init__(ctx, *args, **kwargs): + ctx._fixed_talbot = FixedTalbot(ctx) + ctx._stehfest = Stehfest(ctx) + ctx._de_hoog = deHoog(ctx) + ctx._cohen = Cohen(ctx) + + def invertlaplace(ctx, f, t, **kwargs): + r"""Computes the numerical inverse Laplace transform for a + Laplace-space function at a given time. The function being + evaluated is assumed to be a real-valued function of time. + + The user must supply a Laplace-space function `\bar{f}(p)`, + and a desired time at which to estimate the time-domain + solution `f(t)`. + + A few basic examples of Laplace-space functions with known + inverses (see references [1,2]) : + + .. math :: + + \mathcal{L}\left\lbrace f(t) \right\rbrace=\bar{f}(p) + + .. math :: + + \mathcal{L}^{-1}\left\lbrace \bar{f}(p) \right\rbrace = f(t) + + .. math :: + + \bar{f}(p) = \frac{1}{(p+1)^2} + + .. math :: + + f(t) = t e^{-t} + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> tt = [0.001, 0.01, 0.1, 1, 10] + >>> fp = lambda p: 1/(p+1)**2 + >>> ft = lambda t: t*exp(-t) + >>> ft(tt[0]),ft(tt[0])-invertlaplace(fp,tt[0],method='talbot') + (0.000999000499833375, 8.57923043561212e-20) + >>> ft(tt[1]),ft(tt[1])-invertlaplace(fp,tt[1],method='talbot') + (0.00990049833749168, 3.27007646698047e-19) + >>> ft(tt[2]),ft(tt[2])-invertlaplace(fp,tt[2],method='talbot') + (0.090483741803596, -1.75215800052168e-18) + >>> ft(tt[3]),ft(tt[3])-invertlaplace(fp,tt[3],method='talbot') + (0.367879441171442, 1.2428864009344e-17) + >>> ft(tt[4]),ft(tt[4])-invertlaplace(fp,tt[4],method='talbot') + (0.000453999297624849, 4.04513489306658e-20) + + The methods also work for higher precision: + + >>> mp.dps = 100; mp.pretty = True + >>> nstr(ft(tt[0]),15),nstr(ft(tt[0])-invertlaplace(fp,tt[0],method='talbot'),15) + ('0.000999000499833375', '-4.96868310693356e-105') + >>> nstr(ft(tt[1]),15),nstr(ft(tt[1])-invertlaplace(fp,tt[1],method='talbot'),15) + ('0.00990049833749168', '1.23032291513122e-104') + + .. math :: + + \bar{f}(p) = \frac{1}{p^2+1} + + .. math :: + + f(t) = \mathrm{J}_0(t) + + >>> mp.dps = 15; mp.pretty = True + >>> fp = lambda p: 1/sqrt(p*p + 1) + >>> ft = lambda t: besselj(0,t) + >>> ft(tt[0]),ft(tt[0])-invertlaplace(fp,tt[0],method='dehoog') + (0.999999750000016, -6.09717765032273e-18) + >>> ft(tt[1]),ft(tt[1])-invertlaplace(fp,tt[1],method='dehoog') + (0.99997500015625, -5.61756281076169e-17) + + .. math :: + + \bar{f}(p) = \frac{\log p}{p} + + .. math :: + + f(t) = -\gamma -\log t + + >>> mp.dps = 15; mp.pretty = True + >>> fp = lambda p: log(p)/p + >>> ft = lambda t: -euler-log(t) + >>> ft(tt[0]),ft(tt[0])-invertlaplace(fp,tt[0],method='stehfest') + (6.3305396140806, -1.92126634837863e-16) + >>> ft(tt[1]),ft(tt[1])-invertlaplace(fp,tt[1],method='stehfest') + (4.02795452108656, -4.81486093200704e-16) + + **Options** + + :func:`~mpmath.invertlaplace` recognizes the following optional + keywords valid for all methods: + + *method* + Chooses numerical inverse Laplace transform algorithm + (described below). + *degree* + Number of terms used in the approximation + + **Algorithms** + + Mpmath implements four numerical inverse Laplace transform + algorithms, attributed to: Talbot, Stehfest, and de Hoog, + Knight and Stokes. These can be selected by using + *method='talbot'*, *method='stehfest'*, *method='dehoog'* or + *method='cohen'* or by passing the classes *method=FixedTalbot*, + *method=Stehfest*, *method=deHoog*, or *method=Cohen*. The functions + :func:`~mpmath.invlaptalbot`, :func:`~mpmath.invlapstehfest`, + :func:`~mpmath.invlapdehoog`, and :func:`~mpmath.invlapcohen` + are also available as shortcuts. + + All four algorithms implement a heuristic balance between the + requested precision and the precision used internally for the + calculations. This has been tuned for a typical exponentially + decaying function and precision up to few hundred decimal + digits. + + The Laplace transform converts the variable time (i.e., along + a line) into a parameter given by the right half of the + complex `p`-plane. Singularities, poles, and branch cuts in + the complex `p`-plane contain all the information regarding + the time behavior of the corresponding function. Any numerical + method must therefore sample `p`-plane "close enough" to the + singularities to accurately characterize them, while not + getting too close to have catastrophic cancellation, overflow, + or underflow issues. Most significantly, if one or more of the + singularities in the `p`-plane is not on the left side of the + Bromwich contour, its effects will be left out of the computed + solution, and the answer will be completely wrong. + + *Talbot* + + The fixed Talbot method is high accuracy and fast, but the + method can catastrophically fail for certain classes of time-domain + behavior, including a Heaviside step function for positive + time (e.g., `H(t-2)`), or some oscillatory behaviors. The + Talbot method usually has adjustable parameters, but the + "fixed" variety implemented here does not. This method + deforms the Bromwich integral contour in the shape of a + parabola towards `-\infty`, which leads to problems + when the solution has a decaying exponential in it (e.g., a + Heaviside step function is equivalent to multiplying by a + decaying exponential in Laplace space). + + *Stehfest* + + The Stehfest algorithm only uses abscissa along the real axis + of the complex `p`-plane to estimate the time-domain + function. Oscillatory time-domain functions have poles away + from the real axis, so this method does not work well with + oscillatory functions, especially high-frequency ones. This + method also depends on summation of terms in a series that + grows very large, and will have catastrophic cancellation + during summation if the working precision is too low. + + *de Hoog et al.* + + The de Hoog, Knight, and Stokes method is essentially a + Fourier-series quadrature-type approximation to the Bromwich + contour integral, with non-linear series acceleration and an + analytical expression for the remainder term. This method is + typically one of the most robust. This method also involves the + greatest amount of overhead, so it is typically the slowest of the + four methods at high precision. + + *Cohen* + + The Cohen method is a trapezoidal rule approximation to the Bromwich + contour integral, with linear acceleration for alternating + series. This method is as robust as the de Hoog et al method and the + fastest of the four methods at high precision, and is therefore the + default method. + + **Singularities** + + All numerical inverse Laplace transform methods have problems + at large time when the Laplace-space function has poles, + singularities, or branch cuts to the right of the origin in + the complex plane. For simple poles in `\bar{f}(p)` at the + `p`-plane origin, the time function is constant in time (e.g., + `\mathcal{L}\left\lbrace 1 \right\rbrace=1/p` has a pole at + `p=0`). A pole in `\bar{f}(p)` to the left of the origin is a + decreasing function of time (e.g., `\mathcal{L}\left\lbrace + e^{-t/2} \right\rbrace=1/(p+1/2)` has a pole at `p=-1/2`), and + a pole to the right of the origin leads to an increasing + function in time (e.g., `\mathcal{L}\left\lbrace t e^{t/4} + \right\rbrace = 1/(p-1/4)^2` has a pole at `p=1/4`). When + singularities occur off the real `p` axis, the time-domain + function is oscillatory. For example `\mathcal{L}\left\lbrace + \mathrm{J}_0(t) \right\rbrace=1/\sqrt{p^2+1}` has a branch cut + starting at `p=j=\sqrt{-1}` and is a decaying oscillatory + function, This range of behaviors is illustrated in Duffy [3] + Figure 4.10.4, p. 228. + + In general as `p \rightarrow \infty` `t \rightarrow 0` and + vice-versa. All numerical inverse Laplace transform methods + require their abscissa to shift closer to the origin for + larger times. If the abscissa shift left of the rightmost + singularity in the Laplace domain, the answer will be + completely wrong (the effect of singularities to the right of + the Bromwich contour are not included in the results). + + For example, the following exponentially growing function has + a pole at `p=3`: + + .. math :: + + \bar{f}(p)=\frac{1}{p^2-9} + + .. math :: + + f(t)=\frac{1}{3}\sinh 3t + + >>> mp.dps = 15; mp.pretty = True + >>> fp = lambda p: 1/(p*p-9) + >>> ft = lambda t: sinh(3*t)/3 + >>> tt = [0.01,0.1,1.0,10.0] + >>> ft(tt[0]),invertlaplace(fp,tt[0],method='talbot') + (0.0100015000675014, 0.0100015000675014) + >>> ft(tt[1]),invertlaplace(fp,tt[1],method='talbot') + (0.101506764482381, 0.101506764482381) + >>> ft(tt[2]),invertlaplace(fp,tt[2],method='talbot') + (3.33929164246997, 3.33929164246997) + >>> ft(tt[3]),invertlaplace(fp,tt[3],method='talbot') + (1781079096920.74, -1.61331069624091e-14) + + **References** + + 1. [DLMF]_ section 1.14 (http://dlmf.nist.gov/1.14T4) + 2. Cohen, A.M. (2007). Numerical Methods for Laplace Transform + Inversion, Springer. + 3. Duffy, D.G. (1998). Advanced Engineering Mathematics, CRC Press. + + **Numerical Inverse Laplace Transform Reviews** + + 1. Bellman, R., R.E. Kalaba, J.A. Lockett (1966). *Numerical + inversion of the Laplace transform: Applications to Biology, + Economics, Engineering, and Physics*. Elsevier. + 2. Davies, B., B. Martin (1979). Numerical inversion of the + Laplace transform: a survey and comparison of methods. *Journal + of Computational Physics* 33:1-32, + http://dx.doi.org/10.1016/0021-9991(79)90025-1 + 3. Duffy, D.G. (1993). On the numerical inversion of Laplace + transforms: Comparison of three new methods on characteristic + problems from applications. *ACM Transactions on Mathematical + Software* 19(3):333-359, http://dx.doi.org/10.1145/155743.155788 + 4. Kuhlman, K.L., (2013). Review of Inverse Laplace Transform + Algorithms for Laplace-Space Numerical Approaches, *Numerical + Algorithms*, 63(2):339-355. + http://dx.doi.org/10.1007/s11075-012-9625-3 + + """ + + rule = kwargs.get('method', 'cohen') + if type(rule) is str: + lrule = rule.lower() + if lrule == 'talbot': + rule = ctx._fixed_talbot + elif lrule == 'stehfest': + rule = ctx._stehfest + elif lrule == 'dehoog': + rule = ctx._de_hoog + elif rule == 'cohen': + rule = ctx._cohen + else: + raise ValueError("unknown invlap algorithm: %s" % rule) + else: + rule = rule(ctx) + + # determine the vector of Laplace-space parameter + # needed for the requested method and desired time + rule.calc_laplace_parameter(t, **kwargs) + + # compute the Laplace-space function evalutations + # at the required abscissa. + fp = [f(p) for p in rule.p] + + # compute the time-domain solution from the + # Laplace-space function evaluations + return rule.calc_time_domain_solution(fp, t) + + # shortcuts for the above function for specific methods + def invlaptalbot(ctx, *args, **kwargs): + kwargs['method'] = 'talbot' + return ctx.invertlaplace(*args, **kwargs) + + def invlapstehfest(ctx, *args, **kwargs): + kwargs['method'] = 'stehfest' + return ctx.invertlaplace(*args, **kwargs) + + def invlapdehoog(ctx, *args, **kwargs): + kwargs['method'] = 'dehoog' + return ctx.invertlaplace(*args, **kwargs) + + def invlapcohen(ctx, *args, **kwargs): + kwargs['method'] = 'cohen' + return ctx.invertlaplace(*args, **kwargs) + + +# **************************************** + +if __name__ == '__main__': + import doctest + doctest.testmod() diff --git a/lib/python3.12/site-packages/mpmath/calculus/odes.py b/lib/python3.12/site-packages/mpmath/calculus/odes.py new file mode 100644 index 0000000000000000000000000000000000000000..ac6bd3cb056c8841e6cf77ba4d08a0b1abb2f2c9 --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/calculus/odes.py @@ -0,0 +1,288 @@ +from bisect import bisect +from ..libmp.backend import xrange + +class ODEMethods(object): + pass + +def ode_taylor(ctx, derivs, x0, y0, tol_prec, n): + h = tol = ctx.ldexp(1, -tol_prec) + dim = len(y0) + xs = [x0] + ys = [y0] + x = x0 + y = y0 + orig = ctx.prec + try: + ctx.prec = orig*(1+n) + # Use n steps with Euler's method to get + # evaluation points for derivatives + for i in range(n): + fxy = derivs(x, y) + y = [y[i]+h*fxy[i] for i in xrange(len(y))] + x += h + xs.append(x) + ys.append(y) + # Compute derivatives + ser = [[] for d in range(dim)] + for j in range(n+1): + s = [0]*dim + b = (-1) ** (j & 1) + k = 1 + for i in range(j+1): + for d in range(dim): + s[d] += b * ys[i][d] + b = (b * (j-k+1)) // (-k) + k += 1 + scale = h**(-j) / ctx.fac(j) + for d in range(dim): + s[d] = s[d] * scale + ser[d].append(s[d]) + finally: + ctx.prec = orig + # Estimate radius for which we can get full accuracy. + # XXX: do this right for zeros + radius = ctx.one + for ts in ser: + if ts[-1]: + radius = min(radius, ctx.nthroot(tol/abs(ts[-1]), n)) + radius /= 2 # XXX + return ser, x0+radius + +def odefun(ctx, F, x0, y0, tol=None, degree=None, method='taylor', verbose=False): + r""" + Returns a function `y(x) = [y_0(x), y_1(x), \ldots, y_n(x)]` + that is a numerical solution of the `n+1`-dimensional first-order + ordinary differential equation (ODE) system + + .. math :: + + y_0'(x) = F_0(x, [y_0(x), y_1(x), \ldots, y_n(x)]) + + y_1'(x) = F_1(x, [y_0(x), y_1(x), \ldots, y_n(x)]) + + \vdots + + y_n'(x) = F_n(x, [y_0(x), y_1(x), \ldots, y_n(x)]) + + The derivatives are specified by the vector-valued function + *F* that evaluates + `[y_0', \ldots, y_n'] = F(x, [y_0, \ldots, y_n])`. + The initial point `x_0` is specified by the scalar argument *x0*, + and the initial value `y(x_0) = [y_0(x_0), \ldots, y_n(x_0)]` is + specified by the vector argument *y0*. + + For convenience, if the system is one-dimensional, you may optionally + provide just a scalar value for *y0*. In this case, *F* should accept + a scalar *y* argument and return a scalar. The solution function + *y* will return scalar values instead of length-1 vectors. + + Evaluation of the solution function `y(x)` is permitted + for any `x \ge x_0`. + + A high-order ODE can be solved by transforming it into first-order + vector form. This transformation is described in standard texts + on ODEs. Examples will also be given below. + + **Options, speed and accuracy** + + By default, :func:`~mpmath.odefun` uses a high-order Taylor series + method. For reasonably well-behaved problems, the solution will + be fully accurate to within the working precision. Note that + *F* must be possible to evaluate to very high precision + for the generation of Taylor series to work. + + To get a faster but less accurate solution, you can set a large + value for *tol* (which defaults roughly to *eps*). If you just + want to plot the solution or perform a basic simulation, + *tol = 0.01* is likely sufficient. + + The *degree* argument controls the degree of the solver (with + *method='taylor'*, this is the degree of the Taylor series + expansion). A higher degree means that a longer step can be taken + before a new local solution must be generated from *F*, + meaning that fewer steps are required to get from `x_0` to a given + `x_1`. On the other hand, a higher degree also means that each + local solution becomes more expensive (i.e., more evaluations of + *F* are required per step, and at higher precision). + + The optimal setting therefore involves a tradeoff. Generally, + decreasing the *degree* for Taylor series is likely to give faster + solution at low precision, while increasing is likely to be better + at higher precision. + + The function + object returned by :func:`~mpmath.odefun` caches the solutions at all step + points and uses polynomial interpolation between step points. + Therefore, once `y(x_1)` has been evaluated for some `x_1`, + `y(x)` can be evaluated very quickly for any `x_0 \le x \le x_1`. + and continuing the evaluation up to `x_2 > x_1` is also fast. + + **Examples of first-order ODEs** + + We will solve the standard test problem `y'(x) = y(x), y(0) = 1` + which has explicit solution `y(x) = \exp(x)`:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> f = odefun(lambda x, y: y, 0, 1) + >>> for x in [0, 1, 2.5]: + ... print((f(x), exp(x))) + ... + (1.0, 1.0) + (2.71828182845905, 2.71828182845905) + (12.1824939607035, 12.1824939607035) + + The solution with high precision:: + + >>> mp.dps = 50 + >>> f = odefun(lambda x, y: y, 0, 1) + >>> f(1) + 2.7182818284590452353602874713526624977572470937 + >>> exp(1) + 2.7182818284590452353602874713526624977572470937 + + Using the more general vectorized form, the test problem + can be input as (note that *f* returns a 1-element vector):: + + >>> mp.dps = 15 + >>> f = odefun(lambda x, y: [y[0]], 0, [1]) + >>> f(1) + [2.71828182845905] + + :func:`~mpmath.odefun` can solve nonlinear ODEs, which are generally + impossible (and at best difficult) to solve analytically. As + an example of a nonlinear ODE, we will solve `y'(x) = x \sin(y(x))` + for `y(0) = \pi/2`. An exact solution happens to be known + for this problem, and is given by + `y(x) = 2 \tan^{-1}\left(\exp\left(x^2/2\right)\right)`:: + + >>> f = odefun(lambda x, y: x*sin(y), 0, pi/2) + >>> for x in [2, 5, 10]: + ... print((f(x), 2*atan(exp(mpf(x)**2/2)))) + ... + (2.87255666284091, 2.87255666284091) + (3.14158520028345, 3.14158520028345) + (3.14159265358979, 3.14159265358979) + + If `F` is independent of `y`, an ODE can be solved using direct + integration. We can therefore obtain a reference solution with + :func:`~mpmath.quad`:: + + >>> f = lambda x: (1+x**2)/(1+x**3) + >>> g = odefun(lambda x, y: f(x), pi, 0) + >>> g(2*pi) + 0.72128263801696 + >>> quad(f, [pi, 2*pi]) + 0.72128263801696 + + **Examples of second-order ODEs** + + We will solve the harmonic oscillator equation `y''(x) + y(x) = 0`. + To do this, we introduce the helper functions `y_0 = y, y_1 = y_0'` + whereby the original equation can be written as `y_1' + y_0' = 0`. Put + together, we get the first-order, two-dimensional vector ODE + + .. math :: + + \begin{cases} + y_0' = y_1 \\ + y_1' = -y_0 + \end{cases} + + To get a well-defined IVP, we need two initial values. With + `y(0) = y_0(0) = 1` and `-y'(0) = y_1(0) = 0`, the problem will of + course be solved by `y(x) = y_0(x) = \cos(x)` and + `-y'(x) = y_1(x) = \sin(x)`. We check this:: + + >>> f = odefun(lambda x, y: [-y[1], y[0]], 0, [1, 0]) + >>> for x in [0, 1, 2.5, 10]: + ... nprint(f(x), 15) + ... nprint([cos(x), sin(x)], 15) + ... print("---") + ... + [1.0, 0.0] + [1.0, 0.0] + --- + [0.54030230586814, 0.841470984807897] + [0.54030230586814, 0.841470984807897] + --- + [-0.801143615546934, 0.598472144103957] + [-0.801143615546934, 0.598472144103957] + --- + [-0.839071529076452, -0.54402111088937] + [-0.839071529076452, -0.54402111088937] + --- + + Note that we get both the sine and the cosine solutions + simultaneously. + + **TODO** + + * Better automatic choice of degree and step size + * Make determination of Taylor series convergence radius + more robust + * Allow solution for `x < x_0` + * Allow solution for complex `x` + * Test for difficult (ill-conditioned) problems + * Implement Runge-Kutta and other algorithms + + """ + if tol: + tol_prec = int(-ctx.log(tol, 2))+10 + else: + tol_prec = ctx.prec+10 + degree = degree or (3 + int(3*ctx.dps/2.)) + workprec = ctx.prec + 40 + try: + len(y0) + return_vector = True + except TypeError: + F_ = F + F = lambda x, y: [F_(x, y[0])] + y0 = [y0] + return_vector = False + ser, xb = ode_taylor(ctx, F, x0, y0, tol_prec, degree) + series_boundaries = [x0, xb] + series_data = [(ser, x0, xb)] + # We will be working with vectors of Taylor series + def mpolyval(ser, a): + return [ctx.polyval(s[::-1], a) for s in ser] + # Find nearest expansion point; compute if necessary + def get_series(x): + if x < x0: + raise ValueError + n = bisect(series_boundaries, x) + if n < len(series_boundaries): + return series_data[n-1] + while 1: + ser, xa, xb = series_data[-1] + if verbose: + print("Computing Taylor series for [%f, %f]" % (xa, xb)) + y = mpolyval(ser, xb-xa) + xa = xb + ser, xb = ode_taylor(ctx, F, xb, y, tol_prec, degree) + series_boundaries.append(xb) + series_data.append((ser, xa, xb)) + if x <= xb: + return series_data[-1] + # Evaluation function + def interpolant(x): + x = ctx.convert(x) + orig = ctx.prec + try: + ctx.prec = workprec + ser, xa, xb = get_series(x) + y = mpolyval(ser, x-xa) + finally: + ctx.prec = orig + if return_vector: + return [+yk for yk in y] + else: + return +y[0] + return interpolant + +ODEMethods.odefun = odefun + +if __name__ == "__main__": + import doctest + doctest.testmod() diff --git a/lib/python3.12/site-packages/mpmath/calculus/optimization.py b/lib/python3.12/site-packages/mpmath/calculus/optimization.py new file mode 100644 index 0000000000000000000000000000000000000000..69724f78336ee24856366db48917a47108b1b416 --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/calculus/optimization.py @@ -0,0 +1,1102 @@ +from __future__ import print_function + +from copy import copy + +from ..libmp.backend import xrange + +class OptimizationMethods(object): + def __init__(ctx): + pass + +############## +# 1D-SOLVERS # +############## + +class Newton: + """ + 1d-solver generating pairs of approximative root and error. + + Needs starting points x0 close to the root. + + Pro: + + * converges fast + * sometimes more robust than secant with bad second starting point + + Contra: + + * converges slowly for multiple roots + * needs first derivative + * 2 function evaluations per iteration + """ + maxsteps = 20 + + def __init__(self, ctx, f, x0, **kwargs): + self.ctx = ctx + if len(x0) == 1: + self.x0 = x0[0] + else: + raise ValueError('expected 1 starting point, got %i' % len(x0)) + self.f = f + if not 'df' in kwargs: + def df(x): + return self.ctx.diff(f, x) + else: + df = kwargs['df'] + self.df = df + + def __iter__(self): + f = self.f + df = self.df + x0 = self.x0 + while True: + x1 = x0 - f(x0) / df(x0) + error = abs(x1 - x0) + x0 = x1 + yield (x1, error) + +class Secant: + """ + 1d-solver generating pairs of approximative root and error. + + Needs starting points x0 and x1 close to the root. + x1 defaults to x0 + 0.25. + + Pro: + + * converges fast + + Contra: + + * converges slowly for multiple roots + """ + maxsteps = 30 + + def __init__(self, ctx, f, x0, **kwargs): + self.ctx = ctx + if len(x0) == 1: + self.x0 = x0[0] + self.x1 = self.x0 + 0.25 + elif len(x0) == 2: + self.x0 = x0[0] + self.x1 = x0[1] + else: + raise ValueError('expected 1 or 2 starting points, got %i' % len(x0)) + self.f = f + + def __iter__(self): + f = self.f + x0 = self.x0 + x1 = self.x1 + f0 = f(x0) + while True: + f1 = f(x1) + l = x1 - x0 + if not l: + break + s = (f1 - f0) / l + if not s: + break + x0, x1 = x1, x1 - f1/s + f0 = f1 + yield x1, abs(l) + +class MNewton: + """ + 1d-solver generating pairs of approximative root and error. + + Needs starting point x0 close to the root. + Uses modified Newton's method that converges fast regardless of the + multiplicity of the root. + + Pro: + + * converges fast for multiple roots + + Contra: + + * needs first and second derivative of f + * 3 function evaluations per iteration + """ + maxsteps = 20 + + def __init__(self, ctx, f, x0, **kwargs): + self.ctx = ctx + if not len(x0) == 1: + raise ValueError('expected 1 starting point, got %i' % len(x0)) + self.x0 = x0[0] + self.f = f + if not 'df' in kwargs: + def df(x): + return self.ctx.diff(f, x) + else: + df = kwargs['df'] + self.df = df + if not 'd2f' in kwargs: + def d2f(x): + return self.ctx.diff(df, x) + else: + d2f = kwargs['df'] + self.d2f = d2f + + def __iter__(self): + x = self.x0 + f = self.f + df = self.df + d2f = self.d2f + while True: + prevx = x + fx = f(x) + if fx == 0: + break + dfx = df(x) + d2fx = d2f(x) + # x = x - F(x)/F'(x) with F(x) = f(x)/f'(x) + x -= fx / (dfx - fx * d2fx / dfx) + error = abs(x - prevx) + yield x, error + +class Halley: + """ + 1d-solver generating pairs of approximative root and error. + + Needs a starting point x0 close to the root. + Uses Halley's method with cubic convergence rate. + + Pro: + + * converges even faster the Newton's method + * useful when computing with *many* digits + + Contra: + + * needs first and second derivative of f + * 3 function evaluations per iteration + * converges slowly for multiple roots + """ + + maxsteps = 20 + + def __init__(self, ctx, f, x0, **kwargs): + self.ctx = ctx + if not len(x0) == 1: + raise ValueError('expected 1 starting point, got %i' % len(x0)) + self.x0 = x0[0] + self.f = f + if not 'df' in kwargs: + def df(x): + return self.ctx.diff(f, x) + else: + df = kwargs['df'] + self.df = df + if not 'd2f' in kwargs: + def d2f(x): + return self.ctx.diff(df, x) + else: + d2f = kwargs['df'] + self.d2f = d2f + + def __iter__(self): + x = self.x0 + f = self.f + df = self.df + d2f = self.d2f + while True: + prevx = x + fx = f(x) + dfx = df(x) + d2fx = d2f(x) + x -= 2*fx*dfx / (2*dfx**2 - fx*d2fx) + error = abs(x - prevx) + yield x, error + +class Muller: + """ + 1d-solver generating pairs of approximative root and error. + + Needs starting points x0, x1 and x2 close to the root. + x1 defaults to x0 + 0.25; x2 to x1 + 0.25. + Uses Muller's method that converges towards complex roots. + + Pro: + + * converges fast (somewhat faster than secant) + * can find complex roots + + Contra: + + * converges slowly for multiple roots + * may have complex values for real starting points and real roots + + http://en.wikipedia.org/wiki/Muller's_method + """ + maxsteps = 30 + + def __init__(self, ctx, f, x0, **kwargs): + self.ctx = ctx + if len(x0) == 1: + self.x0 = x0[0] + self.x1 = self.x0 + 0.25 + self.x2 = self.x1 + 0.25 + elif len(x0) == 2: + self.x0 = x0[0] + self.x1 = x0[1] + self.x2 = self.x1 + 0.25 + elif len(x0) == 3: + self.x0 = x0[0] + self.x1 = x0[1] + self.x2 = x0[2] + else: + raise ValueError('expected 1, 2 or 3 starting points, got %i' + % len(x0)) + self.f = f + self.verbose = kwargs['verbose'] + + def __iter__(self): + f = self.f + x0 = self.x0 + x1 = self.x1 + x2 = self.x2 + fx0 = f(x0) + fx1 = f(x1) + fx2 = f(x2) + while True: + # TODO: maybe refactoring with function for divided differences + # calculate divided differences + fx2x1 = (fx1 - fx2) / (x1 - x2) + fx2x0 = (fx0 - fx2) / (x0 - x2) + fx1x0 = (fx0 - fx1) / (x0 - x1) + w = fx2x1 + fx2x0 - fx1x0 + fx2x1x0 = (fx1x0 - fx2x1) / (x0 - x2) + if w == 0 and fx2x1x0 == 0: + if self.verbose: + print('canceled with') + print('x0 =', x0, ', x1 =', x1, 'and x2 =', x2) + break + x0 = x1 + fx0 = fx1 + x1 = x2 + fx1 = fx2 + # denominator should be as large as possible => choose sign + r = self.ctx.sqrt(w**2 - 4*fx2*fx2x1x0) + if abs(w - r) > abs(w + r): + r = -r + x2 -= 2*fx2 / (w + r) + fx2 = f(x2) + error = abs(x2 - x1) + yield x2, error + +# TODO: consider raising a ValueError when there's no sign change in a and b +class Bisection: + """ + 1d-solver generating pairs of approximative root and error. + + Uses bisection method to find a root of f in [a, b]. + Might fail for multiple roots (needs sign change). + + Pro: + + * robust and reliable + + Contra: + + * converges slowly + * needs sign change + """ + maxsteps = 100 + + def __init__(self, ctx, f, x0, **kwargs): + self.ctx = ctx + if len(x0) != 2: + raise ValueError('expected interval of 2 points, got %i' % len(x0)) + self.f = f + self.a = x0[0] + self.b = x0[1] + + def __iter__(self): + f = self.f + a = self.a + b = self.b + l = b - a + fb = f(b) + while True: + m = self.ctx.ldexp(a + b, -1) + fm = f(m) + sign = fm * fb + if sign < 0: + a = m + elif sign > 0: + b = m + fb = fm + else: + yield m, self.ctx.zero + l /= 2 + yield (a + b)/2, abs(l) + +def _getm(method): + """ + Return a function to calculate m for Illinois-like methods. + """ + if method == 'illinois': + def getm(fz, fb): + return 0.5 + elif method == 'pegasus': + def getm(fz, fb): + return fb/(fb + fz) + elif method == 'anderson': + def getm(fz, fb): + m = 1 - fz/fb + if m > 0: + return m + else: + return 0.5 + else: + raise ValueError("method '%s' not recognized" % method) + return getm + +class Illinois: + """ + 1d-solver generating pairs of approximative root and error. + + Uses Illinois method or similar to find a root of f in [a, b]. + Might fail for multiple roots (needs sign change). + Combines bisect with secant (improved regula falsi). + + The only difference between the methods is the scaling factor m, which is + used to ensure convergence (you can choose one using the 'method' keyword): + + Illinois method ('illinois'): + m = 0.5 + + Pegasus method ('pegasus'): + m = fb/(fb + fz) + + Anderson-Bjoerk method ('anderson'): + m = 1 - fz/fb if positive else 0.5 + + Pro: + + * converges very fast + + Contra: + + * has problems with multiple roots + * needs sign change + """ + maxsteps = 30 + + def __init__(self, ctx, f, x0, **kwargs): + self.ctx = ctx + if len(x0) != 2: + raise ValueError('expected interval of 2 points, got %i' % len(x0)) + self.a = x0[0] + self.b = x0[1] + self.f = f + self.tol = kwargs['tol'] + self.verbose = kwargs['verbose'] + self.method = kwargs.get('method', 'illinois') + self.getm = _getm(self.method) + if self.verbose: + print('using %s method' % self.method) + + def __iter__(self): + method = self.method + f = self.f + a = self.a + b = self.b + fa = f(a) + fb = f(b) + m = None + while True: + l = b - a + if l == 0: + break + s = (fb - fa) / l + z = a - fa/s + fz = f(z) + if abs(fz) < self.tol: + # TODO: better condition (when f is very flat) + if self.verbose: + print('canceled with z =', z) + yield z, l + break + if fz * fb < 0: # root in [z, b] + a = b + fa = fb + b = z + fb = fz + else: # root in [a, z] + m = self.getm(fz, fb) + b = z + fb = fz + fa = m*fa # scale down to ensure convergence + if self.verbose and m and not method == 'illinois': + print('m:', m) + yield (a + b)/2, abs(l) + +def Pegasus(*args, **kwargs): + """ + 1d-solver generating pairs of approximative root and error. + + Uses Pegasus method to find a root of f in [a, b]. + Wrapper for illinois to use method='pegasus'. + """ + kwargs['method'] = 'pegasus' + return Illinois(*args, **kwargs) + +def Anderson(*args, **kwargs): + """ + 1d-solver generating pairs of approximative root and error. + + Uses Anderson-Bjoerk method to find a root of f in [a, b]. + Wrapper for illinois to use method='pegasus'. + """ + kwargs['method'] = 'anderson' + return Illinois(*args, **kwargs) + +# TODO: check whether it's possible to combine it with Illinois stuff +class Ridder: + """ + 1d-solver generating pairs of approximative root and error. + + Ridders' method to find a root of f in [a, b]. + Is told to perform as well as Brent's method while being simpler. + + Pro: + + * very fast + * simpler than Brent's method + + Contra: + + * two function evaluations per step + * has problems with multiple roots + * needs sign change + + http://en.wikipedia.org/wiki/Ridders'_method + """ + maxsteps = 30 + + def __init__(self, ctx, f, x0, **kwargs): + self.ctx = ctx + self.f = f + if len(x0) != 2: + raise ValueError('expected interval of 2 points, got %i' % len(x0)) + self.x1 = x0[0] + self.x2 = x0[1] + self.verbose = kwargs['verbose'] + self.tol = kwargs['tol'] + + def __iter__(self): + ctx = self.ctx + f = self.f + x1 = self.x1 + fx1 = f(x1) + x2 = self.x2 + fx2 = f(x2) + while True: + x3 = 0.5*(x1 + x2) + fx3 = f(x3) + x4 = x3 + (x3 - x1) * ctx.sign(fx1 - fx2) * fx3 / ctx.sqrt(fx3**2 - fx1*fx2) + fx4 = f(x4) + if abs(fx4) < self.tol: + # TODO: better condition (when f is very flat) + if self.verbose: + print('canceled with f(x4) =', fx4) + yield x4, abs(x1 - x2) + break + if fx4 * fx2 < 0: # root in [x4, x2] + x1 = x4 + fx1 = fx4 + else: # root in [x1, x4] + x2 = x4 + fx2 = fx4 + error = abs(x1 - x2) + yield (x1 + x2)/2, error + +class ANewton: + """ + EXPERIMENTAL 1d-solver generating pairs of approximative root and error. + + Uses Newton's method modified to use Steffensens method when convergence is + slow. (I.e. for multiple roots.) + """ + maxsteps = 20 + + def __init__(self, ctx, f, x0, **kwargs): + self.ctx = ctx + if not len(x0) == 1: + raise ValueError('expected 1 starting point, got %i' % len(x0)) + self.x0 = x0[0] + self.f = f + if not 'df' in kwargs: + def df(x): + return self.ctx.diff(f, x) + else: + df = kwargs['df'] + self.df = df + def phi(x): + return x - f(x) / df(x) + self.phi = phi + self.verbose = kwargs['verbose'] + + def __iter__(self): + x0 = self.x0 + f = self.f + df = self.df + phi = self.phi + error = 0 + counter = 0 + while True: + prevx = x0 + try: + x0 = phi(x0) + except ZeroDivisionError: + if self.verbose: + print('ZeroDivisionError: canceled with x =', x0) + break + preverror = error + error = abs(prevx - x0) + # TODO: decide not to use convergence acceleration + if error and abs(error - preverror) / error < 1: + if self.verbose: + print('converging slowly') + counter += 1 + if counter >= 3: + # accelerate convergence + phi = steffensen(phi) + counter = 0 + if self.verbose: + print('accelerating convergence') + yield x0, error + +# TODO: add Brent + +############################ +# MULTIDIMENSIONAL SOLVERS # +############################ + +def jacobian(ctx, f, x): + """ + Calculate the Jacobian matrix of a function at the point x0. + + This is the first derivative of a vectorial function: + + f : R^m -> R^n with m >= n + """ + x = ctx.matrix(x) + h = ctx.sqrt(ctx.eps) + fx = ctx.matrix(f(*x)) + m = len(fx) + n = len(x) + J = ctx.matrix(m, n) + for j in xrange(n): + xj = x.copy() + xj[j] += h + Jj = (ctx.matrix(f(*xj)) - fx) / h + for i in xrange(m): + J[i,j] = Jj[i] + return J + +# TODO: test with user-specified jacobian matrix +class MDNewton: + """ + Find the root of a vector function numerically using Newton's method. + + f is a vector function representing a nonlinear equation system. + + x0 is the starting point close to the root. + + J is a function returning the Jacobian matrix for a point. + + Supports overdetermined systems. + + Use the 'norm' keyword to specify which norm to use. Defaults to max-norm. + The function to calculate the Jacobian matrix can be given using the + keyword 'J'. Otherwise it will be calculated numerically. + + Please note that this method converges only locally. Especially for high- + dimensional systems it is not trivial to find a good starting point being + close enough to the root. + + It is recommended to use a faster, low-precision solver from SciPy [1] or + OpenOpt [2] to get an initial guess. Afterwards you can use this method for + root-polishing to any precision. + + [1] http://scipy.org + + [2] http://openopt.org/Welcome + """ + maxsteps = 10 + + def __init__(self, ctx, f, x0, **kwargs): + self.ctx = ctx + self.f = f + if isinstance(x0, (tuple, list)): + x0 = ctx.matrix(x0) + assert x0.cols == 1, 'need a vector' + self.x0 = x0 + if 'J' in kwargs: + self.J = kwargs['J'] + else: + def J(*x): + return ctx.jacobian(f, x) + self.J = J + self.norm = kwargs['norm'] + self.verbose = kwargs['verbose'] + + def __iter__(self): + f = self.f + x0 = self.x0 + norm = self.norm + J = self.J + fx = self.ctx.matrix(f(*x0)) + fxnorm = norm(fx) + cancel = False + while not cancel: + # get direction of descent + fxn = -fx + Jx = J(*x0) + s = self.ctx.lu_solve(Jx, fxn) + if self.verbose: + print('Jx:') + print(Jx) + print('s:', s) + # damping step size TODO: better strategy (hard task) + l = self.ctx.one + x1 = x0 + s + while True: + if x1 == x0: + if self.verbose: + print("canceled, won't get more excact") + cancel = True + break + fx = self.ctx.matrix(f(*x1)) + newnorm = norm(fx) + if newnorm < fxnorm: + # new x accepted + fxnorm = newnorm + x0 = x1 + break + l /= 2 + x1 = x0 + l*s + yield (x0, fxnorm) + +############# +# UTILITIES # +############# + +str2solver = {'newton':Newton, 'secant':Secant, 'mnewton':MNewton, + 'halley':Halley, 'muller':Muller, 'bisect':Bisection, + 'illinois':Illinois, 'pegasus':Pegasus, 'anderson':Anderson, + 'ridder':Ridder, 'anewton':ANewton, 'mdnewton':MDNewton} + +def findroot(ctx, f, x0, solver='secant', tol=None, verbose=False, verify=True, **kwargs): + r""" + Find an approximate solution to `f(x) = 0`, using *x0* as starting point or + interval for *x*. + + Multidimensional overdetermined systems are supported. + You can specify them using a function or a list of functions. + + Mathematically speaking, this function returns `x` such that + `|f(x)|^2 \leq \mathrm{tol}` is true within the current working precision. + If the computed value does not meet this criterion, an exception is raised. + This exception can be disabled with *verify=False*. + + For interval arithmetic (``iv.findroot()``), please note that + the returned interval ``x`` is not guaranteed to contain `f(x)=0`! + It is only some `x` for which `|f(x)|^2 \leq \mathrm{tol}` certainly holds + regardless of numerical error. This may be improved in the future. + + **Arguments** + + *f* + one dimensional function + *x0* + starting point, several starting points or interval (depends on solver) + *tol* + the returned solution has an error smaller than this + *verbose* + print additional information for each iteration if true + *verify* + verify the solution and raise a ValueError if `|f(x)|^2 > \mathrm{tol}` + *solver* + a generator for *f* and *x0* returning approximative solution and error + *maxsteps* + after how many steps the solver will cancel + *df* + first derivative of *f* (used by some solvers) + *d2f* + second derivative of *f* (used by some solvers) + *multidimensional* + force multidimensional solving + *J* + Jacobian matrix of *f* (used by multidimensional solvers) + *norm* + used vector norm (used by multidimensional solvers) + + solver has to be callable with ``(f, x0, **kwargs)`` and return an generator + yielding pairs of approximative solution and estimated error (which is + expected to be positive). + You can use the following string aliases: + 'secant', 'mnewton', 'halley', 'muller', 'illinois', 'pegasus', 'anderson', + 'ridder', 'anewton', 'bisect' + + See mpmath.calculus.optimization for their documentation. + + **Examples** + + The function :func:`~mpmath.findroot` locates a root of a given function using the + secant method by default. A simple example use of the secant method is to + compute `\pi` as the root of `\sin x` closest to `x_0 = 3`:: + + >>> from mpmath import * + >>> mp.dps = 30; mp.pretty = True + >>> findroot(sin, 3) + 3.14159265358979323846264338328 + + The secant method can be used to find complex roots of analytic functions, + although it must in that case generally be given a nonreal starting value + (or else it will never leave the real line):: + + >>> mp.dps = 15 + >>> findroot(lambda x: x**3 + 2*x + 1, j) + (0.226698825758202 + 1.46771150871022j) + + A nice application is to compute nontrivial roots of the Riemann zeta + function with many digits (good initial values are needed for convergence):: + + >>> mp.dps = 30 + >>> findroot(zeta, 0.5+14j) + (0.5 + 14.1347251417346937904572519836j) + + The secant method can also be used as an optimization algorithm, by passing + it a derivative of a function. The following example locates the positive + minimum of the gamma function:: + + >>> mp.dps = 20 + >>> findroot(lambda x: diff(gamma, x), 1) + 1.4616321449683623413 + + Finally, a useful application is to compute inverse functions, such as the + Lambert W function which is the inverse of `w e^w`, given the first + term of the solution's asymptotic expansion as the initial value. In basic + cases, this gives identical results to mpmath's built-in ``lambertw`` + function:: + + >>> def lambert(x): + ... return findroot(lambda w: w*exp(w) - x, log(1+x)) + ... + >>> mp.dps = 15 + >>> lambert(1); lambertw(1) + 0.567143290409784 + 0.567143290409784 + >>> lambert(1000); lambert(1000) + 5.2496028524016 + 5.2496028524016 + + Multidimensional functions are also supported:: + + >>> f = [lambda x1, x2: x1**2 + x2, + ... lambda x1, x2: 5*x1**2 - 3*x1 + 2*x2 - 3] + >>> findroot(f, (0, 0)) + [-0.618033988749895] + [-0.381966011250105] + >>> findroot(f, (10, 10)) + [ 1.61803398874989] + [-2.61803398874989] + + You can verify this by solving the system manually. + + Please note that the following (more general) syntax also works:: + + >>> def f(x1, x2): + ... return x1**2 + x2, 5*x1**2 - 3*x1 + 2*x2 - 3 + ... + >>> findroot(f, (0, 0)) + [-0.618033988749895] + [-0.381966011250105] + + + **Multiple roots** + + For multiple roots all methods of the Newtonian family (including secant) + converge slowly. Consider this example:: + + >>> f = lambda x: (x - 1)**99 + >>> findroot(f, 0.9, verify=False) + 0.918073542444929 + + Even for a very close starting point the secant method converges very + slowly. Use ``verbose=True`` to illustrate this. + + It is possible to modify Newton's method to make it converge regardless of + the root's multiplicity:: + + >>> findroot(f, -10, solver='mnewton') + 1.0 + + This variant uses the first and second derivative of the function, which is + not very efficient. + + Alternatively you can use an experimental Newtonian solver that keeps track + of the speed of convergence and accelerates it using Steffensen's method if + necessary:: + + >>> findroot(f, -10, solver='anewton', verbose=True) + x: -9.88888888888888888889 + error: 0.111111111111111111111 + converging slowly + x: -9.77890011223344556678 + error: 0.10998877665544332211 + converging slowly + x: -9.67002233332199662166 + error: 0.108877778911448945119 + converging slowly + accelerating convergence + x: -9.5622443299551077669 + error: 0.107778003366888854764 + converging slowly + x: 0.99999999999999999214 + error: 10.562244329955107759 + x: 1.0 + error: 7.8598304758094664213e-18 + ZeroDivisionError: canceled with x = 1.0 + 1.0 + + **Complex roots** + + For complex roots it's recommended to use Muller's method as it converges + even for real starting points very fast:: + + >>> findroot(lambda x: x**4 + x + 1, (0, 1, 2), solver='muller') + (0.727136084491197 + 0.934099289460529j) + + + **Intersection methods** + + When you need to find a root in a known interval, it's highly recommended to + use an intersection-based solver like ``'anderson'`` or ``'ridder'``. + Usually they converge faster and more reliable. They have however problems + with multiple roots and usually need a sign change to find a root:: + + >>> findroot(lambda x: x**3, (-1, 1), solver='anderson') + 0.0 + + Be careful with symmetric functions:: + + >>> findroot(lambda x: x**2, (-1, 1), solver='anderson') #doctest:+ELLIPSIS + Traceback (most recent call last): + ... + ZeroDivisionError + + It fails even for better starting points, because there is no sign change:: + + >>> findroot(lambda x: x**2, (-1, .5), solver='anderson') + Traceback (most recent call last): + ... + ValueError: Could not find root within given tolerance. (1.0 > 2.16840434497100886801e-19) + Try another starting point or tweak arguments. + + """ + prec = ctx.prec + try: + ctx.prec += 20 + + # initialize arguments + if tol is None: + tol = ctx.eps * 2**10 + + kwargs['verbose'] = kwargs.get('verbose', verbose) + + if 'd1f' in kwargs: + kwargs['df'] = kwargs['d1f'] + + kwargs['tol'] = tol + if isinstance(x0, (list, tuple)): + x0 = [ctx.convert(x) for x in x0] + else: + x0 = [ctx.convert(x0)] + + if isinstance(solver, str): + try: + solver = str2solver[solver] + except KeyError: + raise ValueError('could not recognize solver') + + # accept list of functions + if isinstance(f, (list, tuple)): + f2 = copy(f) + def tmp(*args): + return [fn(*args) for fn in f2] + f = tmp + + # detect multidimensional functions + try: + fx = f(*x0) + multidimensional = isinstance(fx, (list, tuple, ctx.matrix)) + except TypeError: + fx = f(x0[0]) + multidimensional = False + if 'multidimensional' in kwargs: + multidimensional = kwargs['multidimensional'] + if multidimensional: + # only one multidimensional solver available at the moment + solver = MDNewton + if not 'norm' in kwargs: + norm = lambda x: ctx.norm(x, 'inf') + kwargs['norm'] = norm + else: + norm = kwargs['norm'] + else: + norm = abs + + # happily return starting point if it's a root + if norm(fx) == 0: + if multidimensional: + return ctx.matrix(x0) + else: + return x0[0] + + # use solver + iterations = solver(ctx, f, x0, **kwargs) + if 'maxsteps' in kwargs: + maxsteps = kwargs['maxsteps'] + else: + maxsteps = iterations.maxsteps + i = 0 + for x, error in iterations: + if verbose: + print('x: ', x) + print('error:', error) + i += 1 + if error < tol * max(1, norm(x)) or i >= maxsteps: + break + else: + if not i: + raise ValueError('Could not find root using the given solver.\n' + 'Try another starting point or tweak arguments.') + if not isinstance(x, (list, tuple, ctx.matrix)): + xl = [x] + else: + xl = x + if verify and norm(f(*xl))**2 > tol: # TODO: better condition? + raise ValueError('Could not find root within given tolerance. ' + '(%s > %s)\n' + 'Try another starting point or tweak arguments.' + % (norm(f(*xl))**2, tol)) + return x + finally: + ctx.prec = prec + + +def multiplicity(ctx, f, root, tol=None, maxsteps=10, **kwargs): + """ + Return the multiplicity of a given root of f. + + Internally, numerical derivatives are used. This might be inefficient for + higher order derviatives. Due to this, ``multiplicity`` cancels after + evaluating 10 derivatives by default. You can be specify the n-th derivative + using the dnf keyword. + + >>> from mpmath import * + >>> multiplicity(lambda x: sin(x) - 1, pi/2) + 2 + + """ + if tol is None: + tol = ctx.eps ** 0.8 + kwargs['d0f'] = f + for i in xrange(maxsteps): + dfstr = 'd' + str(i) + 'f' + if dfstr in kwargs: + df = kwargs[dfstr] + else: + df = lambda x: ctx.diff(f, x, i) + if not abs(df(root)) < tol: + break + return i + +def steffensen(f): + """ + linear convergent function -> quadratic convergent function + + Steffensen's method for quadratic convergence of a linear converging + sequence. + Don not use it for higher rates of convergence. + It may even work for divergent sequences. + + Definition: + F(x) = (x*f(f(x)) - f(x)**2) / (f(f(x)) - 2*f(x) + x) + + Example + ....... + + You can use Steffensen's method to accelerate a fixpoint iteration of linear + (or less) convergence. + + x* is a fixpoint of the iteration x_{k+1} = phi(x_k) if x* = phi(x*). For + phi(x) = x**2 there are two fixpoints: 0 and 1. + + Let's try Steffensen's method: + + >>> f = lambda x: x**2 + >>> from mpmath.calculus.optimization import steffensen + >>> F = steffensen(f) + >>> for x in [0.5, 0.9, 2.0]: + ... fx = Fx = x + ... for i in xrange(9): + ... try: + ... fx = f(fx) + ... except OverflowError: + ... pass + ... try: + ... Fx = F(Fx) + ... except ZeroDivisionError: + ... pass + ... print('%20g %20g' % (fx, Fx)) + 0.25 -0.5 + 0.0625 0.1 + 0.00390625 -0.0011236 + 1.52588e-05 1.41691e-09 + 2.32831e-10 -2.84465e-27 + 5.42101e-20 2.30189e-80 + 2.93874e-39 -1.2197e-239 + 8.63617e-78 0 + 7.45834e-155 0 + 0.81 1.02676 + 0.6561 1.00134 + 0.430467 1 + 0.185302 1 + 0.0343368 1 + 0.00117902 1 + 1.39008e-06 1 + 1.93233e-12 1 + 3.73392e-24 1 + 4 1.6 + 16 1.2962 + 256 1.10194 + 65536 1.01659 + 4.29497e+09 1.00053 + 1.84467e+19 1 + 3.40282e+38 1 + 1.15792e+77 1 + 1.34078e+154 1 + + Unmodified, the iteration converges only towards 0. Modified it converges + not only much faster, it converges even to the repelling fixpoint 1. + """ + def F(x): + fx = f(x) + ffx = f(fx) + return (x*ffx - fx**2) / (ffx - 2*fx + x) + return F + +OptimizationMethods.jacobian = jacobian +OptimizationMethods.findroot = findroot +OptimizationMethods.multiplicity = multiplicity + +if __name__ == '__main__': + import doctest + doctest.testmod() diff --git a/lib/python3.12/site-packages/mpmath/calculus/polynomials.py b/lib/python3.12/site-packages/mpmath/calculus/polynomials.py new file mode 100644 index 0000000000000000000000000000000000000000..ba75c1e88cbc5d40aa590a786c0af5229f193103 --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/calculus/polynomials.py @@ -0,0 +1,213 @@ +from ..libmp.backend import xrange +from .calculus import defun + +#----------------------------------------------------------------------------# +# Polynomials # +#----------------------------------------------------------------------------# + +# XXX: extra precision +@defun +def polyval(ctx, coeffs, x, derivative=False): + r""" + Given coefficients `[c_n, \ldots, c_2, c_1, c_0]` and a number `x`, + :func:`~mpmath.polyval` evaluates the polynomial + + .. math :: + + P(x) = c_n x^n + \ldots + c_2 x^2 + c_1 x + c_0. + + If *derivative=True* is set, :func:`~mpmath.polyval` simultaneously + evaluates `P(x)` with the derivative, `P'(x)`, and returns the + tuple `(P(x), P'(x))`. + + >>> from mpmath import * + >>> mp.pretty = True + >>> polyval([3, 0, 2], 0.5) + 2.75 + >>> polyval([3, 0, 2], 0.5, derivative=True) + (2.75, 3.0) + + The coefficients and the evaluation point may be any combination + of real or complex numbers. + """ + if not coeffs: + return ctx.zero + p = ctx.convert(coeffs[0]) + q = ctx.zero + for c in coeffs[1:]: + if derivative: + q = p + x*q + p = c + x*p + if derivative: + return p, q + else: + return p + +@defun +def polyroots(ctx, coeffs, maxsteps=50, cleanup=True, extraprec=10, + error=False, roots_init=None): + """ + Computes all roots (real or complex) of a given polynomial. + + The roots are returned as a sorted list, where real roots appear first + followed by complex conjugate roots as adjacent elements. The polynomial + should be given as a list of coefficients, in the format used by + :func:`~mpmath.polyval`. The leading coefficient must be nonzero. + + With *error=True*, :func:`~mpmath.polyroots` returns a tuple *(roots, err)* + where *err* is an estimate of the maximum error among the computed roots. + + **Examples** + + Finding the three real roots of `x^3 - x^2 - 14x + 24`:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> nprint(polyroots([1,-1,-14,24]), 4) + [-4.0, 2.0, 3.0] + + Finding the two complex conjugate roots of `4x^2 + 3x + 2`, with an + error estimate:: + + >>> roots, err = polyroots([4,3,2], error=True) + >>> for r in roots: + ... print(r) + ... + (-0.375 + 0.59947894041409j) + (-0.375 - 0.59947894041409j) + >>> + >>> err + 2.22044604925031e-16 + >>> + >>> polyval([4,3,2], roots[0]) + (2.22044604925031e-16 + 0.0j) + >>> polyval([4,3,2], roots[1]) + (2.22044604925031e-16 + 0.0j) + + The following example computes all the 5th roots of unity; that is, + the roots of `x^5 - 1`:: + + >>> mp.dps = 20 + >>> for r in polyroots([1, 0, 0, 0, 0, -1]): + ... print(r) + ... + 1.0 + (-0.8090169943749474241 + 0.58778525229247312917j) + (-0.8090169943749474241 - 0.58778525229247312917j) + (0.3090169943749474241 + 0.95105651629515357212j) + (0.3090169943749474241 - 0.95105651629515357212j) + + **Precision and conditioning** + + The roots are computed to the current working precision accuracy. If this + accuracy cannot be achieved in ``maxsteps`` steps, then a + ``NoConvergence`` exception is raised. The algorithm internally is using + the current working precision extended by ``extraprec``. If + ``NoConvergence`` was raised, that is caused either by not having enough + extra precision to achieve convergence (in which case increasing + ``extraprec`` should fix the problem) or too low ``maxsteps`` (in which + case increasing ``maxsteps`` should fix the problem), or a combination of + both. + + The user should always do a convergence study with regards to + ``extraprec`` to ensure accurate results. It is possible to get + convergence to a wrong answer with too low ``extraprec``. + + Provided there are no repeated roots, :func:`~mpmath.polyroots` can + typically compute all roots of an arbitrary polynomial to high precision:: + + >>> mp.dps = 60 + >>> for r in polyroots([1, 0, -10, 0, 1]): + ... print(r) + ... + -3.14626436994197234232913506571557044551247712918732870123249 + -0.317837245195782244725757617296174288373133378433432554879127 + 0.317837245195782244725757617296174288373133378433432554879127 + 3.14626436994197234232913506571557044551247712918732870123249 + >>> + >>> sqrt(3) + sqrt(2) + 3.14626436994197234232913506571557044551247712918732870123249 + >>> sqrt(3) - sqrt(2) + 0.317837245195782244725757617296174288373133378433432554879127 + + **Algorithm** + + :func:`~mpmath.polyroots` implements the Durand-Kerner method [1], which + uses complex arithmetic to locate all roots simultaneously. + The Durand-Kerner method can be viewed as approximately performing + simultaneous Newton iteration for all the roots. In particular, + the convergence to simple roots is quadratic, just like Newton's + method. + + Although all roots are internally calculated using complex arithmetic, any + root found to have an imaginary part smaller than the estimated numerical + error is truncated to a real number (small real parts are also chopped). + Real roots are placed first in the returned list, sorted by value. The + remaining complex roots are sorted by their real parts so that conjugate + roots end up next to each other. + + **References** + + 1. http://en.wikipedia.org/wiki/Durand-Kerner_method + + """ + if len(coeffs) <= 1: + if not coeffs or not coeffs[0]: + raise ValueError("Input to polyroots must not be the zero polynomial") + # Constant polynomial with no roots + return [] + + orig = ctx.prec + tol = +ctx.eps + with ctx.extraprec(extraprec): + deg = len(coeffs) - 1 + # Must be monic + lead = ctx.convert(coeffs[0]) + if lead == 1: + coeffs = [ctx.convert(c) for c in coeffs] + else: + coeffs = [c/lead for c in coeffs] + f = lambda x: ctx.polyval(coeffs, x) + if roots_init is None: + roots = [ctx.mpc((0.4+0.9j)**n) for n in xrange(deg)] + else: + roots = [None]*deg; + deg_init = min(deg, len(roots_init)) + roots[:deg_init] = list(roots_init[:deg_init]) + roots[deg_init:] = [ctx.mpc((0.4+0.9j)**n) for n + in xrange(deg_init,deg)] + err = [ctx.one for n in xrange(deg)] + # Durand-Kerner iteration until convergence + for step in xrange(maxsteps): + if abs(max(err)) < tol: + break + for i in xrange(deg): + p = roots[i] + x = f(p) + for j in range(deg): + if i != j: + try: + x /= (p-roots[j]) + except ZeroDivisionError: + continue + roots[i] = p - x + err[i] = abs(x) + if abs(max(err)) >= tol: + raise ctx.NoConvergence("Didn't converge in maxsteps=%d steps." \ + % maxsteps) + # Remove small real or imaginary parts + if cleanup: + for i in xrange(deg): + if abs(roots[i]) < tol: + roots[i] = ctx.zero + elif abs(ctx._im(roots[i])) < tol: + roots[i] = roots[i].real + elif abs(ctx._re(roots[i])) < tol: + roots[i] = roots[i].imag * 1j + roots.sort(key=lambda x: (abs(ctx._im(x)), ctx._re(x))) + if error: + err = max(err) + err = max(err, ctx.ldexp(1, -orig+1)) + return [+r for r in roots], +err + else: + return [+r for r in roots] diff --git a/lib/python3.12/site-packages/mpmath/calculus/quadrature.py b/lib/python3.12/site-packages/mpmath/calculus/quadrature.py new file mode 100644 index 0000000000000000000000000000000000000000..0545b3ad3e6f70a44f4ec31a0c3bcfb4ffde422b --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/calculus/quadrature.py @@ -0,0 +1,1115 @@ +import math + +from ..libmp.backend import xrange + +class QuadratureRule(object): + """ + Quadrature rules are implemented using this class, in order to + simplify the code and provide a common infrastructure + for tasks such as error estimation and node caching. + + You can implement a custom quadrature rule by subclassing + :class:`QuadratureRule` and implementing the appropriate + methods. The subclass can then be used by :func:`~mpmath.quad` by + passing it as the *method* argument. + + :class:`QuadratureRule` instances are supposed to be singletons. + :class:`QuadratureRule` therefore implements instance caching + in :func:`~mpmath.__new__`. + """ + + def __init__(self, ctx): + self.ctx = ctx + self.standard_cache = {} + self.transformed_cache = {} + self.interval_count = {} + + def clear(self): + """ + Delete cached node data. + """ + self.standard_cache = {} + self.transformed_cache = {} + self.interval_count = {} + + def calc_nodes(self, degree, prec, verbose=False): + r""" + Compute nodes for the standard interval `[-1, 1]`. Subclasses + should probably implement only this method, and use + :func:`~mpmath.get_nodes` method to retrieve the nodes. + """ + raise NotImplementedError + + def get_nodes(self, a, b, degree, prec, verbose=False): + """ + Return nodes for given interval, degree and precision. The + nodes are retrieved from a cache if already computed; + otherwise they are computed by calling :func:`~mpmath.calc_nodes` + and are then cached. + + Subclasses should probably not implement this method, + but just implement :func:`~mpmath.calc_nodes` for the actual + node computation. + """ + key = (a, b, degree, prec) + if key in self.transformed_cache: + return self.transformed_cache[key] + orig = self.ctx.prec + try: + self.ctx.prec = prec+20 + # Get nodes on standard interval + if (degree, prec) in self.standard_cache: + nodes = self.standard_cache[degree, prec] + else: + nodes = self.calc_nodes(degree, prec, verbose) + self.standard_cache[degree, prec] = nodes + # Transform to general interval + nodes = self.transform_nodes(nodes, a, b, verbose) + if key in self.interval_count: + self.transformed_cache[key] = nodes + else: + self.interval_count[key] = True + finally: + self.ctx.prec = orig + return nodes + + def transform_nodes(self, nodes, a, b, verbose=False): + r""" + Rescale standardized nodes (for `[-1, 1]`) to a general + interval `[a, b]`. For a finite interval, a simple linear + change of variables is used. Otherwise, the following + transformations are used: + + .. math :: + + \lbrack a, \infty \rbrack : t = \frac{1}{x} + (a-1) + + \lbrack -\infty, b \rbrack : t = (b+1) - \frac{1}{x} + + \lbrack -\infty, \infty \rbrack : t = \frac{x}{\sqrt{1-x^2}} + + """ + ctx = self.ctx + a = ctx.convert(a) + b = ctx.convert(b) + one = ctx.one + if (a, b) == (-one, one): + return nodes + half = ctx.mpf(0.5) + new_nodes = [] + if ctx.isinf(a) or ctx.isinf(b): + if (a, b) == (ctx.ninf, ctx.inf): + p05 = -half + for x, w in nodes: + x2 = x*x + px1 = one-x2 + spx1 = px1**p05 + x = x*spx1 + w *= spx1/px1 + new_nodes.append((x, w)) + elif a == ctx.ninf: + b1 = b+1 + for x, w in nodes: + u = 2/(x+one) + x = b1-u + w *= half*u**2 + new_nodes.append((x, w)) + elif b == ctx.inf: + a1 = a-1 + for x, w in nodes: + u = 2/(x+one) + x = a1+u + w *= half*u**2 + new_nodes.append((x, w)) + elif a == ctx.inf or b == ctx.ninf: + return [(x,-w) for (x,w) in self.transform_nodes(nodes, b, a, verbose)] + else: + raise NotImplementedError + else: + # Simple linear change of variables + C = (b-a)/2 + D = (b+a)/2 + for x, w in nodes: + new_nodes.append((D+C*x, C*w)) + return new_nodes + + def guess_degree(self, prec): + """ + Given a desired precision `p` in bits, estimate the degree `m` + of the quadrature required to accomplish full accuracy for + typical integrals. By default, :func:`~mpmath.quad` will perform up + to `m` iterations. The value of `m` should be a slight + overestimate, so that "slightly bad" integrals can be dealt + with automatically using a few extra iterations. On the + other hand, it should not be too big, so :func:`~mpmath.quad` can + quit within a reasonable amount of time when it is given + an "unsolvable" integral. + + The default formula used by :func:`~mpmath.guess_degree` is tuned + for both :class:`TanhSinh` and :class:`GaussLegendre`. + The output is roughly as follows: + + +---------+---------+ + | `p` | `m` | + +=========+=========+ + | 50 | 6 | + +---------+---------+ + | 100 | 7 | + +---------+---------+ + | 500 | 10 | + +---------+---------+ + | 3000 | 12 | + +---------+---------+ + + This formula is based purely on a limited amount of + experimentation and will sometimes be wrong. + """ + # Expected degree + # XXX: use mag + g = int(4 + max(0, self.ctx.log(prec/30.0, 2))) + # Reasonable "worst case" + g += 2 + return g + + def estimate_error(self, results, prec, epsilon): + r""" + Given results from integrations `[I_1, I_2, \ldots, I_k]` done + with a quadrature of rule of degree `1, 2, \ldots, k`, estimate + the error of `I_k`. + + For `k = 2`, we estimate `|I_{\infty}-I_2|` as `|I_2-I_1|`. + + For `k > 2`, we extrapolate `|I_{\infty}-I_k| \approx |I_{k+1}-I_k|` + from `|I_k-I_{k-1}|` and `|I_k-I_{k-2}|` under the assumption + that each degree increment roughly doubles the accuracy of + the quadrature rule (this is true for both :class:`TanhSinh` + and :class:`GaussLegendre`). The extrapolation formula is given + by Borwein, Bailey & Girgensohn. Although not very conservative, + this method seems to be very robust in practice. + """ + if len(results) == 2: + return abs(results[0]-results[1]) + try: + if results[-1] == results[-2] == results[-3]: + return self.ctx.zero + D1 = self.ctx.log(abs(results[-1]-results[-2]), 10) + D2 = self.ctx.log(abs(results[-1]-results[-3]), 10) + except ValueError: + return epsilon + D3 = -prec + D4 = min(0, max(D1**2/D2, 2*D1, D3)) + return self.ctx.mpf(10) ** int(D4) + + def summation(self, f, points, prec, epsilon, max_degree, verbose=False): + """ + Main integration function. Computes the 1D integral over + the interval specified by *points*. For each subinterval, + performs quadrature of degree from 1 up to *max_degree* + until :func:`~mpmath.estimate_error` signals convergence. + + :func:`~mpmath.summation` transforms each subintegration to + the standard interval and then calls :func:`~mpmath.sum_next`. + """ + ctx = self.ctx + I = total_err = ctx.zero + for i in xrange(len(points)-1): + a, b = points[i], points[i+1] + if a == b: + continue + # XXX: we could use a single variable transformation, + # but this is not good in practice. We get better accuracy + # by having 0 as an endpoint. + if (a, b) == (ctx.ninf, ctx.inf): + _f = f + f = lambda x: _f(-x) + _f(x) + a, b = (ctx.zero, ctx.inf) + results = [] + err = ctx.zero + for degree in xrange(1, max_degree+1): + nodes = self.get_nodes(a, b, degree, prec, verbose) + if verbose: + print("Integrating from %s to %s (degree %s of %s)" % \ + (ctx.nstr(a), ctx.nstr(b), degree, max_degree)) + result = self.sum_next(f, nodes, degree, prec, results, verbose) + results.append(result) + if degree > 1: + err = self.estimate_error(results, prec, epsilon) + if verbose: + print("Estimated error:", ctx.nstr(err), " epsilon:", ctx.nstr(epsilon), " result: ", ctx.nstr(result)) + if err <= epsilon: + break + I += results[-1] + total_err += err + if total_err > epsilon: + if verbose: + print("Failed to reach full accuracy. Estimated error:", ctx.nstr(total_err)) + return I, total_err + + def sum_next(self, f, nodes, degree, prec, previous, verbose=False): + r""" + Evaluates the step sum `\sum w_k f(x_k)` where the *nodes* list + contains the `(w_k, x_k)` pairs. + + :func:`~mpmath.summation` will supply the list *results* of + values computed by :func:`~mpmath.sum_next` at previous degrees, in + case the quadrature rule is able to reuse them. + """ + return self.ctx.fdot((w, f(x)) for (x,w) in nodes) + + +class TanhSinh(QuadratureRule): + r""" + This class implements "tanh-sinh" or "doubly exponential" + quadrature. This quadrature rule is based on the Euler-Maclaurin + integral formula. By performing a change of variables involving + nested exponentials / hyperbolic functions (hence the name), the + derivatives at the endpoints vanish rapidly. Since the error term + in the Euler-Maclaurin formula depends on the derivatives at the + endpoints, a simple step sum becomes extremely accurate. In + practice, this means that doubling the number of evaluation + points roughly doubles the number of accurate digits. + + Comparison to Gauss-Legendre: + * Initial computation of nodes is usually faster + * Handles endpoint singularities better + * Handles infinite integration intervals better + * Is slower for smooth integrands once nodes have been computed + + The implementation of the tanh-sinh algorithm is based on the + description given in Borwein, Bailey & Girgensohn, "Experimentation + in Mathematics - Computational Paths to Discovery", A K Peters, + 2003, pages 312-313. In the present implementation, a few + improvements have been made: + + * A more efficient scheme is used to compute nodes (exploiting + recurrence for the exponential function) + * The nodes are computed successively instead of all at once + + **References** + + * [Bailey]_ + * http://users.cs.dal.ca/~jborwein/tanh-sinh.pdf + + """ + + def sum_next(self, f, nodes, degree, prec, previous, verbose=False): + """ + Step sum for tanh-sinh quadrature of degree `m`. We exploit the + fact that half of the abscissas at degree `m` are precisely the + abscissas from degree `m-1`. Thus reusing the result from + the previous level allows a 2x speedup. + """ + h = self.ctx.mpf(2)**(-degree) + # Abscissas overlap, so reusing saves half of the time + if previous: + S = previous[-1]/(h*2) + else: + S = self.ctx.zero + S += self.ctx.fdot((w,f(x)) for (x,w) in nodes) + return h*S + + def calc_nodes(self, degree, prec, verbose=False): + r""" + The abscissas and weights for tanh-sinh quadrature of degree + `m` are given by + + .. math:: + + x_k = \tanh(\pi/2 \sinh(t_k)) + + w_k = \pi/2 \cosh(t_k) / \cosh(\pi/2 \sinh(t_k))^2 + + where `t_k = t_0 + hk` for a step length `h \sim 2^{-m}`. The + list of nodes is actually infinite, but the weights die off so + rapidly that only a few are needed. + """ + ctx = self.ctx + nodes = [] + + extra = 20 + ctx.prec += extra + tol = ctx.ldexp(1, -prec-10) + pi4 = ctx.pi/4 + + # For simplicity, we work in steps h = 1/2^n, with the first point + # offset so that we can reuse the sum from the previous degree + + # We define degree 1 to include the "degree 0" steps, including + # the point x = 0. (It doesn't work well otherwise; not sure why.) + t0 = ctx.ldexp(1, -degree) + if degree == 1: + #nodes.append((mpf(0), pi4)) + #nodes.append((-mpf(0), pi4)) + nodes.append((ctx.zero, ctx.pi/2)) + h = t0 + else: + h = t0*2 + + # Since h is fixed, we can compute the next exponential + # by simply multiplying by exp(h) + expt0 = ctx.exp(t0) + a = pi4 * expt0 + b = pi4 / expt0 + udelta = ctx.exp(h) + urdelta = 1/udelta + + for k in xrange(0, 20*2**degree+1): + # Reference implementation: + # t = t0 + k*h + # x = tanh(pi/2 * sinh(t)) + # w = pi/2 * cosh(t) / cosh(pi/2 * sinh(t))**2 + + # Fast implementation. Note that c = exp(pi/2 * sinh(t)) + c = ctx.exp(a-b) + d = 1/c + co = (c+d)/2 + si = (c-d)/2 + x = si / co + w = (a+b) / co**2 + diff = abs(x-1) + if diff <= tol: + break + + nodes.append((x, w)) + nodes.append((-x, w)) + + a *= udelta + b *= urdelta + + if verbose and k % 300 == 150: + # Note: the number displayed is rather arbitrary. Should + # figure out how to print something that looks more like a + # percentage + print("Calculating nodes:", ctx.nstr(-ctx.log(diff, 10) / prec)) + + ctx.prec -= extra + return nodes + + +class GaussLegendre(QuadratureRule): + r""" + This class implements Gauss-Legendre quadrature, which is + exceptionally efficient for polynomials and polynomial-like (i.e. + very smooth) integrands. + + The abscissas and weights are given by roots and values of + Legendre polynomials, which are the orthogonal polynomials + on `[-1, 1]` with respect to the unit weight + (see :func:`~mpmath.legendre`). + + In this implementation, we take the "degree" `m` of the quadrature + to denote a Gauss-Legendre rule of degree `3 \cdot 2^m` (following + Borwein, Bailey & Girgensohn). This way we get quadratic, rather + than linear, convergence as the degree is incremented. + + Comparison to tanh-sinh quadrature: + * Is faster for smooth integrands once nodes have been computed + * Initial computation of nodes is usually slower + * Handles endpoint singularities worse + * Handles infinite integration intervals worse + + """ + + def calc_nodes(self, degree, prec, verbose=False): + r""" + Calculates the abscissas and weights for Gauss-Legendre + quadrature of degree of given degree (actually `3 \cdot 2^m`). + """ + ctx = self.ctx + # It is important that the epsilon is set lower than the + # "real" epsilon + epsilon = ctx.ldexp(1, -prec-8) + # Fairly high precision might be required for accurate + # evaluation of the roots + orig = ctx.prec + ctx.prec = int(prec*1.5) + if degree == 1: + x = ctx.sqrt(ctx.mpf(3)/5) + w = ctx.mpf(5)/9 + nodes = [(-x,w),(ctx.zero,ctx.mpf(8)/9),(x,w)] + ctx.prec = orig + return nodes + nodes = [] + n = 3*2**(degree-1) + upto = n//2 + 1 + for j in xrange(1, upto): + # Asymptotic formula for the roots + r = ctx.mpf(math.cos(math.pi*(j-0.25)/(n+0.5))) + # Newton iteration + while 1: + t1, t2 = 1, 0 + # Evaluates the Legendre polynomial using its defining + # recurrence relation + for j1 in xrange(1,n+1): + t3, t2, t1 = t2, t1, ((2*j1-1)*r*t1 - (j1-1)*t2)/j1 + t4 = n*(r*t1-t2)/(r**2-1) + a = t1/t4 + r = r - a + if abs(a) < epsilon: + break + x = r + w = 2/((1-r**2)*t4**2) + if verbose and j % 30 == 15: + print("Computing nodes (%i of %i)" % (j, upto)) + nodes.append((x, w)) + nodes.append((-x, w)) + ctx.prec = orig + return nodes + +class QuadratureMethods(object): + + def __init__(ctx, *args, **kwargs): + ctx._gauss_legendre = GaussLegendre(ctx) + ctx._tanh_sinh = TanhSinh(ctx) + + def quad(ctx, f, *points, **kwargs): + r""" + Computes a single, double or triple integral over a given + 1D interval, 2D rectangle, or 3D cuboid. A basic example:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> quad(sin, [0, pi]) + 2.0 + + A basic 2D integral:: + + >>> f = lambda x, y: cos(x+y/2) + >>> quad(f, [-pi/2, pi/2], [0, pi]) + 4.0 + + **Interval format** + + The integration range for each dimension may be specified + using a list or tuple. Arguments are interpreted as follows: + + ``quad(f, [x1, x2])`` -- calculates + `\int_{x_1}^{x_2} f(x) \, dx` + + ``quad(f, [x1, x2], [y1, y2])`` -- calculates + `\int_{x_1}^{x_2} \int_{y_1}^{y_2} f(x,y) \, dy \, dx` + + ``quad(f, [x1, x2], [y1, y2], [z1, z2])`` -- calculates + `\int_{x_1}^{x_2} \int_{y_1}^{y_2} \int_{z_1}^{z_2} f(x,y,z) + \, dz \, dy \, dx` + + Endpoints may be finite or infinite. An interval descriptor + may also contain more than two points. In this + case, the integration is split into subintervals, between + each pair of consecutive points. This is useful for + dealing with mid-interval discontinuities, or integrating + over large intervals where the function is irregular or + oscillates. + + **Options** + + :func:`~mpmath.quad` recognizes the following keyword arguments: + + *method* + Chooses integration algorithm (described below). + *error* + If set to true, :func:`~mpmath.quad` returns `(v, e)` where `v` is the + integral and `e` is the estimated error. + *maxdegree* + Maximum degree of the quadrature rule to try before + quitting. + *verbose* + Print details about progress. + + **Algorithms** + + Mpmath presently implements two integration algorithms: tanh-sinh + quadrature and Gauss-Legendre quadrature. These can be selected + using *method='tanh-sinh'* or *method='gauss-legendre'* or by + passing the classes *method=TanhSinh*, *method=GaussLegendre*. + The functions :func:`~mpmath.quadts` and :func:`~mpmath.quadgl` are also available + as shortcuts. + + Both algorithms have the property that doubling the number of + evaluation points roughly doubles the accuracy, so both are ideal + for high precision quadrature (hundreds or thousands of digits). + + At high precision, computing the nodes and weights for the + integration can be expensive (more expensive than computing the + function values). To make repeated integrations fast, nodes + are automatically cached. + + The advantages of the tanh-sinh algorithm are that it tends to + handle endpoint singularities well, and that the nodes are cheap + to compute on the first run. For these reasons, it is used by + :func:`~mpmath.quad` as the default algorithm. + + Gauss-Legendre quadrature often requires fewer function + evaluations, and is therefore often faster for repeated use, but + the algorithm does not handle endpoint singularities as well and + the nodes are more expensive to compute. Gauss-Legendre quadrature + can be a better choice if the integrand is smooth and repeated + integrations are required (e.g. for multiple integrals). + + See the documentation for :class:`TanhSinh` and + :class:`GaussLegendre` for additional details. + + **Examples of 1D integrals** + + Intervals may be infinite or half-infinite. The following two + examples evaluate the limits of the inverse tangent function + (`\int 1/(1+x^2) = \tan^{-1} x`), and the Gaussian integral + `\int_{\infty}^{\infty} \exp(-x^2)\,dx = \sqrt{\pi}`:: + + >>> mp.dps = 15 + >>> quad(lambda x: 2/(x**2+1), [0, inf]) + 3.14159265358979 + >>> quad(lambda x: exp(-x**2), [-inf, inf])**2 + 3.14159265358979 + + Integrals can typically be resolved to high precision. + The following computes 50 digits of `\pi` by integrating the + area of the half-circle defined by `x^2 + y^2 \le 1`, + `-1 \le x \le 1`, `y \ge 0`:: + + >>> mp.dps = 50 + >>> 2*quad(lambda x: sqrt(1-x**2), [-1, 1]) + 3.1415926535897932384626433832795028841971693993751 + + One can just as well compute 1000 digits (output truncated):: + + >>> mp.dps = 1000 + >>> 2*quad(lambda x: sqrt(1-x**2), [-1, 1]) #doctest:+ELLIPSIS + 3.141592653589793238462643383279502884...216420199 + + Complex integrals are supported. The following computes + a residue at `z = 0` by integrating counterclockwise along the + diamond-shaped path from `1` to `+i` to `-1` to `-i` to `1`:: + + >>> mp.dps = 15 + >>> chop(quad(lambda z: 1/z, [1,j,-1,-j,1])) + (0.0 + 6.28318530717959j) + + **Examples of 2D and 3D integrals** + + Here are several nice examples of analytically solvable + 2D integrals (taken from MathWorld [1]) that can be evaluated + to high precision fairly rapidly by :func:`~mpmath.quad`:: + + >>> mp.dps = 30 + >>> f = lambda x, y: (x-1)/((1-x*y)*log(x*y)) + >>> quad(f, [0, 1], [0, 1]) + 0.577215664901532860606512090082 + >>> +euler + 0.577215664901532860606512090082 + + >>> f = lambda x, y: 1/sqrt(1+x**2+y**2) + >>> quad(f, [-1, 1], [-1, 1]) + 3.17343648530607134219175646705 + >>> 4*log(2+sqrt(3))-2*pi/3 + 3.17343648530607134219175646705 + + >>> f = lambda x, y: 1/(1-x**2 * y**2) + >>> quad(f, [0, 1], [0, 1]) + 1.23370055013616982735431137498 + >>> pi**2 / 8 + 1.23370055013616982735431137498 + + >>> quad(lambda x, y: 1/(1-x*y), [0, 1], [0, 1]) + 1.64493406684822643647241516665 + >>> pi**2 / 6 + 1.64493406684822643647241516665 + + Multiple integrals may be done over infinite ranges:: + + >>> mp.dps = 15 + >>> print(quad(lambda x,y: exp(-x-y), [0, inf], [1, inf])) + 0.367879441171442 + >>> print(1/e) + 0.367879441171442 + + For nonrectangular areas, one can call :func:`~mpmath.quad` recursively. + For example, we can replicate the earlier example of calculating + `\pi` by integrating over the unit-circle, and actually use double + quadrature to actually measure the area circle:: + + >>> f = lambda x: quad(lambda y: 1, [-sqrt(1-x**2), sqrt(1-x**2)]) + >>> quad(f, [-1, 1]) + 3.14159265358979 + + Here is a simple triple integral:: + + >>> mp.dps = 15 + >>> f = lambda x,y,z: x*y/(1+z) + >>> quad(f, [0,1], [0,1], [1,2], method='gauss-legendre') + 0.101366277027041 + >>> (log(3)-log(2))/4 + 0.101366277027041 + + **Singularities** + + Both tanh-sinh and Gauss-Legendre quadrature are designed to + integrate smooth (infinitely differentiable) functions. Neither + algorithm copes well with mid-interval singularities (such as + mid-interval discontinuities in `f(x)` or `f'(x)`). + The best solution is to split the integral into parts:: + + >>> mp.dps = 15 + >>> quad(lambda x: abs(sin(x)), [0, 2*pi]) # Bad + 3.99900894176779 + >>> quad(lambda x: abs(sin(x)), [0, pi, 2*pi]) # Good + 4.0 + + The tanh-sinh rule often works well for integrands having a + singularity at one or both endpoints:: + + >>> mp.dps = 15 + >>> quad(log, [0, 1], method='tanh-sinh') # Good + -1.0 + >>> quad(log, [0, 1], method='gauss-legendre') # Bad + -0.999932197413801 + + However, the result may still be inaccurate for some functions:: + + >>> quad(lambda x: 1/sqrt(x), [0, 1], method='tanh-sinh') + 1.99999999946942 + + This problem is not due to the quadrature rule per se, but to + numerical amplification of errors in the nodes. The problem can be + circumvented by temporarily increasing the precision:: + + >>> mp.dps = 30 + >>> a = quad(lambda x: 1/sqrt(x), [0, 1], method='tanh-sinh') + >>> mp.dps = 15 + >>> +a + 2.0 + + **Highly variable functions** + + For functions that are smooth (in the sense of being infinitely + differentiable) but contain sharp mid-interval peaks or many + "bumps", :func:`~mpmath.quad` may fail to provide full accuracy. For + example, with default settings, :func:`~mpmath.quad` is able to integrate + `\sin(x)` accurately over an interval of length 100 but not over + length 1000:: + + >>> quad(sin, [0, 100]); 1-cos(100) # Good + 0.137681127712316 + 0.137681127712316 + >>> quad(sin, [0, 1000]); 1-cos(1000) # Bad + -37.8587612408485 + 0.437620923709297 + + One solution is to break the integration into 10 intervals of + length 100:: + + >>> quad(sin, linspace(0, 1000, 10)) # Good + 0.437620923709297 + + Another is to increase the degree of the quadrature:: + + >>> quad(sin, [0, 1000], maxdegree=10) # Also good + 0.437620923709297 + + Whether splitting the interval or increasing the degree is + more efficient differs from case to case. Another example is the + function `1/(1+x^2)`, which has a sharp peak centered around + `x = 0`:: + + >>> f = lambda x: 1/(1+x**2) + >>> quad(f, [-100, 100]) # Bad + 3.64804647105268 + >>> quad(f, [-100, 100], maxdegree=10) # Good + 3.12159332021646 + >>> quad(f, [-100, 0, 100]) # Also good + 3.12159332021646 + + **References** + + 1. http://mathworld.wolfram.com/DoubleIntegral.html + + """ + rule = kwargs.get('method', 'tanh-sinh') + if type(rule) is str: + if rule == 'tanh-sinh': + rule = ctx._tanh_sinh + elif rule == 'gauss-legendre': + rule = ctx._gauss_legendre + else: + raise ValueError("unknown quadrature rule: %s" % rule) + else: + rule = rule(ctx) + verbose = kwargs.get('verbose') + dim = len(points) + orig = prec = ctx.prec + epsilon = ctx.eps/8 + m = kwargs.get('maxdegree') or rule.guess_degree(prec) + points = [ctx._as_points(p) for p in points] + try: + ctx.prec += 20 + if dim == 1: + v, err = rule.summation(f, points[0], prec, epsilon, m, verbose) + elif dim == 2: + v, err = rule.summation(lambda x: \ + rule.summation(lambda y: f(x,y), \ + points[1], prec, epsilon, m)[0], + points[0], prec, epsilon, m, verbose) + elif dim == 3: + v, err = rule.summation(lambda x: \ + rule.summation(lambda y: \ + rule.summation(lambda z: f(x,y,z), \ + points[2], prec, epsilon, m)[0], + points[1], prec, epsilon, m)[0], + points[0], prec, epsilon, m, verbose) + else: + raise NotImplementedError("quadrature must have dim 1, 2 or 3") + finally: + ctx.prec = orig + if kwargs.get("error"): + return +v, err + return +v + + def quadts(ctx, *args, **kwargs): + """ + Performs tanh-sinh quadrature. The call + + quadts(func, *points, ...) + + is simply a shortcut for: + + quad(func, *points, ..., method=TanhSinh) + + For example, a single integral and a double integral: + + quadts(lambda x: exp(cos(x)), [0, 1]) + quadts(lambda x, y: exp(cos(x+y)), [0, 1], [0, 1]) + + See the documentation for quad for information about how points + arguments and keyword arguments are parsed. + + See documentation for TanhSinh for algorithmic information about + tanh-sinh quadrature. + """ + kwargs['method'] = 'tanh-sinh' + return ctx.quad(*args, **kwargs) + + def quadgl(ctx, *args, **kwargs): + """ + Performs Gauss-Legendre quadrature. The call + + quadgl(func, *points, ...) + + is simply a shortcut for: + + quad(func, *points, ..., method=GaussLegendre) + + For example, a single integral and a double integral: + + quadgl(lambda x: exp(cos(x)), [0, 1]) + quadgl(lambda x, y: exp(cos(x+y)), [0, 1], [0, 1]) + + See the documentation for quad for information about how points + arguments and keyword arguments are parsed. + + See documentation for TanhSinh for algorithmic information about + tanh-sinh quadrature. + """ + kwargs['method'] = 'gauss-legendre' + return ctx.quad(*args, **kwargs) + + def quadosc(ctx, f, interval, omega=None, period=None, zeros=None): + r""" + Calculates + + .. math :: + + I = \int_a^b f(x) dx + + where at least one of `a` and `b` is infinite and where + `f(x) = g(x) \cos(\omega x + \phi)` for some slowly + decreasing function `g(x)`. With proper input, :func:`~mpmath.quadosc` + can also handle oscillatory integrals where the oscillation + rate is different from a pure sine or cosine wave. + + In the standard case when `|a| < \infty, b = \infty`, + :func:`~mpmath.quadosc` works by evaluating the infinite series + + .. math :: + + I = \int_a^{x_1} f(x) dx + + \sum_{k=1}^{\infty} \int_{x_k}^{x_{k+1}} f(x) dx + + where `x_k` are consecutive zeros (alternatively + some other periodic reference point) of `f(x)`. + Accordingly, :func:`~mpmath.quadosc` requires information about the + zeros of `f(x)`. For a periodic function, you can specify + the zeros by either providing the angular frequency `\omega` + (*omega*) or the *period* `2 \pi/\omega`. In general, you can + specify the `n`-th zero by providing the *zeros* arguments. + Below is an example of each:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> f = lambda x: sin(3*x)/(x**2+1) + >>> quadosc(f, [0,inf], omega=3) + 0.37833007080198 + >>> quadosc(f, [0,inf], period=2*pi/3) + 0.37833007080198 + >>> quadosc(f, [0,inf], zeros=lambda n: pi*n/3) + 0.37833007080198 + >>> (ei(3)*exp(-3)-exp(3)*ei(-3))/2 # Computed by Mathematica + 0.37833007080198 + + Note that *zeros* was specified to multiply `n` by the + *half-period*, not the full period. In theory, it does not matter + whether each partial integral is done over a half period or a full + period. However, if done over half-periods, the infinite series + passed to :func:`~mpmath.nsum` becomes an *alternating series* and this + typically makes the extrapolation much more efficient. + + Here is an example of an integration over the entire real line, + and a half-infinite integration starting at `-\infty`:: + + >>> quadosc(lambda x: cos(x)/(1+x**2), [-inf, inf], omega=1) + 1.15572734979092 + >>> pi/e + 1.15572734979092 + >>> quadosc(lambda x: cos(x)/x**2, [-inf, -1], period=2*pi) + -0.0844109505595739 + >>> cos(1)+si(1)-pi/2 + -0.0844109505595738 + + Of course, the integrand may contain a complex exponential just as + well as a real sine or cosine:: + + >>> quadosc(lambda x: exp(3*j*x)/(1+x**2), [-inf,inf], omega=3) + (0.156410688228254 + 0.0j) + >>> pi/e**3 + 0.156410688228254 + >>> quadosc(lambda x: exp(3*j*x)/(2+x+x**2), [-inf,inf], omega=3) + (0.00317486988463794 - 0.0447701735209082j) + >>> 2*pi/sqrt(7)/exp(3*(j+sqrt(7))/2) + (0.00317486988463794 - 0.0447701735209082j) + + **Non-periodic functions** + + If `f(x) = g(x) h(x)` for some function `h(x)` that is not + strictly periodic, *omega* or *period* might not work, and it might + be necessary to use *zeros*. + + A notable exception can be made for Bessel functions which, though not + periodic, are "asymptotically periodic" in a sufficiently strong sense + that the sum extrapolation will work out:: + + >>> quadosc(j0, [0, inf], period=2*pi) + 1.0 + >>> quadosc(j1, [0, inf], period=2*pi) + 1.0 + + More properly, one should provide the exact Bessel function zeros:: + + >>> j0zero = lambda n: findroot(j0, pi*(n-0.25)) + >>> quadosc(j0, [0, inf], zeros=j0zero) + 1.0 + + For an example where *zeros* becomes necessary, consider the + complete Fresnel integrals + + .. math :: + + \int_0^{\infty} \cos x^2\,dx = \int_0^{\infty} \sin x^2\,dx + = \sqrt{\frac{\pi}{8}}. + + Although the integrands do not decrease in magnitude as + `x \to \infty`, the integrals are convergent since the oscillation + rate increases (causing consecutive periods to asymptotically + cancel out). These integrals are virtually impossible to calculate + to any kind of accuracy using standard quadrature rules. However, + if one provides the correct asymptotic distribution of zeros + (`x_n \sim \sqrt{n}`), :func:`~mpmath.quadosc` works:: + + >>> mp.dps = 30 + >>> f = lambda x: cos(x**2) + >>> quadosc(f, [0,inf], zeros=lambda n:sqrt(pi*n)) + 0.626657068657750125603941321203 + >>> f = lambda x: sin(x**2) + >>> quadosc(f, [0,inf], zeros=lambda n:sqrt(pi*n)) + 0.626657068657750125603941321203 + >>> sqrt(pi/8) + 0.626657068657750125603941321203 + + (Interestingly, these integrals can still be evaluated if one + places some other constant than `\pi` in the square root sign.) + + In general, if `f(x) \sim g(x) \cos(h(x))`, the zeros follow + the inverse-function distribution `h^{-1}(x)`:: + + >>> mp.dps = 15 + >>> f = lambda x: sin(exp(x)) + >>> quadosc(f, [1,inf], zeros=lambda n: log(n)) + -0.25024394235267 + >>> pi/2-si(e) + -0.250243942352671 + + **Non-alternating functions** + + If the integrand oscillates around a positive value, without + alternating signs, the extrapolation might fail. A simple trick + that sometimes works is to multiply or divide the frequency by 2:: + + >>> f = lambda x: 1/x**2+sin(x)/x**4 + >>> quadosc(f, [1,inf], omega=1) # Bad + 1.28642190869861 + >>> quadosc(f, [1,inf], omega=0.5) # Perfect + 1.28652953559617 + >>> 1+(cos(1)+ci(1)+sin(1))/6 + 1.28652953559617 + + **Fast decay** + + :func:`~mpmath.quadosc` is primarily useful for slowly decaying + integrands. If the integrand decreases exponentially or faster, + :func:`~mpmath.quad` will likely handle it without trouble (and generally be + much faster than :func:`~mpmath.quadosc`):: + + >>> quadosc(lambda x: cos(x)/exp(x), [0, inf], omega=1) + 0.5 + >>> quad(lambda x: cos(x)/exp(x), [0, inf]) + 0.5 + + """ + a, b = ctx._as_points(interval) + a = ctx.convert(a) + b = ctx.convert(b) + if [omega, period, zeros].count(None) != 2: + raise ValueError( \ + "must specify exactly one of omega, period, zeros") + if a == ctx.ninf and b == ctx.inf: + s1 = ctx.quadosc(f, [a, 0], omega=omega, zeros=zeros, period=period) + s2 = ctx.quadosc(f, [0, b], omega=omega, zeros=zeros, period=period) + return s1 + s2 + if a == ctx.ninf: + if zeros: + return ctx.quadosc(lambda x:f(-x), [-b,-a], lambda n: zeros(-n)) + else: + return ctx.quadosc(lambda x:f(-x), [-b,-a], omega=omega, period=period) + if b != ctx.inf: + raise ValueError("quadosc requires an infinite integration interval") + if not zeros: + if omega: + period = 2*ctx.pi/omega + zeros = lambda n: n*period/2 + #for n in range(1,10): + # p = zeros(n) + # if p > a: + # break + #if n >= 9: + # raise ValueError("zeros do not appear to be correctly indexed") + n = 1 + s = ctx.quadgl(f, [a, zeros(n)]) + def term(k): + return ctx.quadgl(f, [zeros(k), zeros(k+1)]) + s += ctx.nsum(term, [n, ctx.inf]) + return s + + def quadsubdiv(ctx, f, interval, tol=None, maxintervals=None, **kwargs): + """ + Computes the integral of *f* over the interval or path specified + by *interval*, using :func:`~mpmath.quad` together with adaptive + subdivision of the interval. + + This function gives an accurate answer for some integrals where + :func:`~mpmath.quad` fails:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> quad(lambda x: abs(sin(x)), [0, 2*pi]) + 3.99900894176779 + >>> quadsubdiv(lambda x: abs(sin(x)), [0, 2*pi]) + 4.0 + >>> quadsubdiv(sin, [0, 1000]) + 0.437620923709297 + >>> quadsubdiv(lambda x: 1/(1+x**2), [-100, 100]) + 3.12159332021646 + >>> quadsubdiv(lambda x: ceil(x), [0, 100]) + 5050.0 + >>> quadsubdiv(lambda x: sin(x+exp(x)), [0,8]) + 0.347400172657248 + + The argument *maxintervals* can be set to limit the permissible + subdivision:: + + >>> quadsubdiv(lambda x: sin(x**2), [0,100], maxintervals=5, error=True) + (-5.40487904307774, 5.011) + >>> quadsubdiv(lambda x: sin(x**2), [0,100], maxintervals=100, error=True) + (0.631417921866934, 1.10101120134116e-17) + + Subdivision does not guarantee a correct answer since, the error + estimate on subintervals may be inaccurate:: + + >>> quadsubdiv(lambda x: sech(10*x-2)**2 + sech(100*x-40)**4 + sech(1000*x-600)**6, [0,1], error=True) + (0.210802735500549, 1.0001111101e-17) + >>> mp.dps = 20 + >>> quadsubdiv(lambda x: sech(10*x-2)**2 + sech(100*x-40)**4 + sech(1000*x-600)**6, [0,1], error=True) + (0.21080273550054927738, 2.200000001e-24) + + The second answer is correct. We can get an accurate result at lower + precision by forcing a finer initial subdivision:: + + >>> mp.dps = 15 + >>> quadsubdiv(lambda x: sech(10*x-2)**2 + sech(100*x-40)**4 + sech(1000*x-600)**6, linspace(0,1,5)) + 0.210802735500549 + + The following integral is too oscillatory for convergence, but we can get a + reasonable estimate:: + + >>> v, err = fp.quadsubdiv(lambda x: fp.sin(1/x), [0,1], error=True) + >>> round(v, 6), round(err, 6) + (0.504067, 1e-06) + >>> sin(1) - ci(1) + 0.504067061906928 + + """ + queue = [] + for i in range(len(interval)-1): + queue.append((interval[i], interval[i+1])) + total = ctx.zero + total_error = ctx.zero + if maxintervals is None: + maxintervals = 10 * ctx.prec + count = 0 + quad_args = kwargs.copy() + quad_args["verbose"] = False + quad_args["error"] = True + if tol is None: + tol = +ctx.eps + orig = ctx.prec + try: + ctx.prec += 5 + while queue: + a, b = queue.pop() + s, err = ctx.quad(f, [a, b], **quad_args) + if kwargs.get("verbose"): + print("subinterval", count, a, b, err) + if err < tol or count > maxintervals: + total += s + total_error += err + else: + count += 1 + if count == maxintervals and kwargs.get("verbose"): + print("warning: number of intervals exceeded maxintervals") + if a == -ctx.inf and b == ctx.inf: + m = 0 + elif a == -ctx.inf: + m = min(b-1, 2*b) + elif b == ctx.inf: + m = max(a+1, 2*a) + else: + m = a + (b - a) / 2 + queue.append((a, m)) + queue.append((m, b)) + finally: + ctx.prec = orig + if kwargs.get("error"): + return +total, +total_error + else: + return +total + +if __name__ == '__main__': + import doctest + doctest.testmod() diff --git a/lib/python3.12/site-packages/mpmath/ctx_base.py b/lib/python3.12/site-packages/mpmath/ctx_base.py new file mode 100644 index 0000000000000000000000000000000000000000..1946f8daf4dbe165b3943be09af361812828aab1 --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/ctx_base.py @@ -0,0 +1,494 @@ +from operator import gt, lt + +from .libmp.backend import xrange + +from .functions.functions import SpecialFunctions +from .functions.rszeta import RSCache +from .calculus.quadrature import QuadratureMethods +from .calculus.inverselaplace import LaplaceTransformInversionMethods +from .calculus.calculus import CalculusMethods +from .calculus.optimization import OptimizationMethods +from .calculus.odes import ODEMethods +from .matrices.matrices import MatrixMethods +from .matrices.calculus import MatrixCalculusMethods +from .matrices.linalg import LinearAlgebraMethods +from .matrices.eigen import Eigen +from .identification import IdentificationMethods +from .visualization import VisualizationMethods + +from . import libmp + +class Context(object): + pass + +class StandardBaseContext(Context, + SpecialFunctions, + RSCache, + QuadratureMethods, + LaplaceTransformInversionMethods, + CalculusMethods, + MatrixMethods, + MatrixCalculusMethods, + LinearAlgebraMethods, + Eigen, + IdentificationMethods, + OptimizationMethods, + ODEMethods, + VisualizationMethods): + + NoConvergence = libmp.NoConvergence + ComplexResult = libmp.ComplexResult + + def __init__(ctx): + ctx._aliases = {} + # Call those that need preinitialization (e.g. for wrappers) + SpecialFunctions.__init__(ctx) + RSCache.__init__(ctx) + QuadratureMethods.__init__(ctx) + LaplaceTransformInversionMethods.__init__(ctx) + CalculusMethods.__init__(ctx) + MatrixMethods.__init__(ctx) + + def _init_aliases(ctx): + for alias, value in ctx._aliases.items(): + try: + setattr(ctx, alias, getattr(ctx, value)) + except AttributeError: + pass + + _fixed_precision = False + + # XXX + verbose = False + + def warn(ctx, msg): + print("Warning:", msg) + + def bad_domain(ctx, msg): + raise ValueError(msg) + + def _re(ctx, x): + if hasattr(x, "real"): + return x.real + return x + + def _im(ctx, x): + if hasattr(x, "imag"): + return x.imag + return ctx.zero + + def _as_points(ctx, x): + return x + + def fneg(ctx, x, **kwargs): + return -ctx.convert(x) + + def fadd(ctx, x, y, **kwargs): + return ctx.convert(x)+ctx.convert(y) + + def fsub(ctx, x, y, **kwargs): + return ctx.convert(x)-ctx.convert(y) + + def fmul(ctx, x, y, **kwargs): + return ctx.convert(x)*ctx.convert(y) + + def fdiv(ctx, x, y, **kwargs): + return ctx.convert(x)/ctx.convert(y) + + def fsum(ctx, args, absolute=False, squared=False): + if absolute: + if squared: + return sum((abs(x)**2 for x in args), ctx.zero) + return sum((abs(x) for x in args), ctx.zero) + if squared: + return sum((x**2 for x in args), ctx.zero) + return sum(args, ctx.zero) + + def fdot(ctx, xs, ys=None, conjugate=False): + if ys is not None: + xs = zip(xs, ys) + if conjugate: + cf = ctx.conj + return sum((x*cf(y) for (x,y) in xs), ctx.zero) + else: + return sum((x*y for (x,y) in xs), ctx.zero) + + def fprod(ctx, args): + prod = ctx.one + for arg in args: + prod *= arg + return prod + + def nprint(ctx, x, n=6, **kwargs): + """ + Equivalent to ``print(nstr(x, n))``. + """ + print(ctx.nstr(x, n, **kwargs)) + + def chop(ctx, x, tol=None): + """ + Chops off small real or imaginary parts, or converts + numbers close to zero to exact zeros. The input can be a + single number or an iterable:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = False + >>> chop(5+1e-10j, tol=1e-9) + mpf('5.0') + >>> nprint(chop([1.0, 1e-20, 3+1e-18j, -4, 2])) + [1.0, 0.0, 3.0, -4.0, 2.0] + + The tolerance defaults to ``100*eps``. + """ + if tol is None: + tol = 100*ctx.eps + try: + x = ctx.convert(x) + absx = abs(x) + if abs(x) < tol: + return ctx.zero + if ctx._is_complex_type(x): + #part_tol = min(tol, absx*tol) + part_tol = max(tol, absx*tol) + if abs(x.imag) < part_tol: + return x.real + if abs(x.real) < part_tol: + return ctx.mpc(0, x.imag) + except TypeError: + if isinstance(x, ctx.matrix): + return x.apply(lambda a: ctx.chop(a, tol)) + if hasattr(x, "__iter__"): + return [ctx.chop(a, tol) for a in x] + return x + + def almosteq(ctx, s, t, rel_eps=None, abs_eps=None): + r""" + Determine whether the difference between `s` and `t` is smaller + than a given epsilon, either relatively or absolutely. + + Both a maximum relative difference and a maximum difference + ('epsilons') may be specified. The absolute difference is + defined as `|s-t|` and the relative difference is defined + as `|s-t|/\max(|s|, |t|)`. + + If only one epsilon is given, both are set to the same value. + If none is given, both epsilons are set to `2^{-p+m}` where + `p` is the current working precision and `m` is a small + integer. The default setting typically allows :func:`~mpmath.almosteq` + to be used to check for mathematical equality + in the presence of small rounding errors. + + **Examples** + + >>> from mpmath import * + >>> mp.dps = 15 + >>> almosteq(3.141592653589793, 3.141592653589790) + True + >>> almosteq(3.141592653589793, 3.141592653589700) + False + >>> almosteq(3.141592653589793, 3.141592653589700, 1e-10) + True + >>> almosteq(1e-20, 2e-20) + True + >>> almosteq(1e-20, 2e-20, rel_eps=0, abs_eps=0) + False + + """ + t = ctx.convert(t) + if abs_eps is None and rel_eps is None: + rel_eps = abs_eps = ctx.ldexp(1, -ctx.prec+4) + if abs_eps is None: + abs_eps = rel_eps + elif rel_eps is None: + rel_eps = abs_eps + diff = abs(s-t) + if diff <= abs_eps: + return True + abss = abs(s) + abst = abs(t) + if abss < abst: + err = diff/abst + else: + err = diff/abss + return err <= rel_eps + + def arange(ctx, *args): + r""" + This is a generalized version of Python's :func:`~mpmath.range` function + that accepts fractional endpoints and step sizes and + returns a list of ``mpf`` instances. Like :func:`~mpmath.range`, + :func:`~mpmath.arange` can be called with 1, 2 or 3 arguments: + + ``arange(b)`` + `[0, 1, 2, \ldots, x]` + ``arange(a, b)`` + `[a, a+1, a+2, \ldots, x]` + ``arange(a, b, h)`` + `[a, a+h, a+h, \ldots, x]` + + where `b-1 \le x < b` (in the third case, `b-h \le x < b`). + + Like Python's :func:`~mpmath.range`, the endpoint is not included. To + produce ranges where the endpoint is included, :func:`~mpmath.linspace` + is more convenient. + + **Examples** + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = False + >>> arange(4) + [mpf('0.0'), mpf('1.0'), mpf('2.0'), mpf('3.0')] + >>> arange(1, 2, 0.25) + [mpf('1.0'), mpf('1.25'), mpf('1.5'), mpf('1.75')] + >>> arange(1, -1, -0.75) + [mpf('1.0'), mpf('0.25'), mpf('-0.5')] + + """ + if not len(args) <= 3: + raise TypeError('arange expected at most 3 arguments, got %i' + % len(args)) + if not len(args) >= 1: + raise TypeError('arange expected at least 1 argument, got %i' + % len(args)) + # set default + a = 0 + dt = 1 + # interpret arguments + if len(args) == 1: + b = args[0] + elif len(args) >= 2: + a = args[0] + b = args[1] + if len(args) == 3: + dt = args[2] + a, b, dt = ctx.mpf(a), ctx.mpf(b), ctx.mpf(dt) + assert a + dt != a, 'dt is too small and would cause an infinite loop' + # adapt code for sign of dt + if a > b: + if dt > 0: + return [] + op = gt + else: + if dt < 0: + return [] + op = lt + # create list + result = [] + i = 0 + t = a + while 1: + t = a + dt*i + i += 1 + if op(t, b): + result.append(t) + else: + break + return result + + def linspace(ctx, *args, **kwargs): + """ + ``linspace(a, b, n)`` returns a list of `n` evenly spaced + samples from `a` to `b`. The syntax ``linspace(mpi(a,b), n)`` + is also valid. + + This function is often more convenient than :func:`~mpmath.arange` + for partitioning an interval into subintervals, since + the endpoint is included:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = False + >>> linspace(1, 4, 4) + [mpf('1.0'), mpf('2.0'), mpf('3.0'), mpf('4.0')] + + You may also provide the keyword argument ``endpoint=False``:: + + >>> linspace(1, 4, 4, endpoint=False) + [mpf('1.0'), mpf('1.75'), mpf('2.5'), mpf('3.25')] + + """ + if len(args) == 3: + a = ctx.mpf(args[0]) + b = ctx.mpf(args[1]) + n = int(args[2]) + elif len(args) == 2: + assert hasattr(args[0], '_mpi_') + a = args[0].a + b = args[0].b + n = int(args[1]) + else: + raise TypeError('linspace expected 2 or 3 arguments, got %i' \ + % len(args)) + if n < 1: + raise ValueError('n must be greater than 0') + if not 'endpoint' in kwargs or kwargs['endpoint']: + if n == 1: + return [ctx.mpf(a)] + step = (b - a) / ctx.mpf(n - 1) + y = [i*step + a for i in xrange(n)] + y[-1] = b + else: + step = (b - a) / ctx.mpf(n) + y = [i*step + a for i in xrange(n)] + return y + + def cos_sin(ctx, z, **kwargs): + return ctx.cos(z, **kwargs), ctx.sin(z, **kwargs) + + def cospi_sinpi(ctx, z, **kwargs): + return ctx.cospi(z, **kwargs), ctx.sinpi(z, **kwargs) + + def _default_hyper_maxprec(ctx, p): + return int(1000 * p**0.25 + 4*p) + + _gcd = staticmethod(libmp.gcd) + list_primes = staticmethod(libmp.list_primes) + isprime = staticmethod(libmp.isprime) + bernfrac = staticmethod(libmp.bernfrac) + moebius = staticmethod(libmp.moebius) + _ifac = staticmethod(libmp.ifac) + _eulernum = staticmethod(libmp.eulernum) + _stirling1 = staticmethod(libmp.stirling1) + _stirling2 = staticmethod(libmp.stirling2) + + def sum_accurately(ctx, terms, check_step=1): + prec = ctx.prec + try: + extraprec = 10 + while 1: + ctx.prec = prec + extraprec + 5 + max_mag = ctx.ninf + s = ctx.zero + k = 0 + for term in terms(): + s += term + if (not k % check_step) and term: + term_mag = ctx.mag(term) + max_mag = max(max_mag, term_mag) + sum_mag = ctx.mag(s) + if sum_mag - term_mag > ctx.prec: + break + k += 1 + cancellation = max_mag - sum_mag + if cancellation != cancellation: + break + if cancellation < extraprec or ctx._fixed_precision: + break + extraprec += min(ctx.prec, cancellation) + return s + finally: + ctx.prec = prec + + def mul_accurately(ctx, factors, check_step=1): + prec = ctx.prec + try: + extraprec = 10 + while 1: + ctx.prec = prec + extraprec + 5 + max_mag = ctx.ninf + one = ctx.one + s = one + k = 0 + for factor in factors(): + s *= factor + term = factor - one + if (not k % check_step): + term_mag = ctx.mag(term) + max_mag = max(max_mag, term_mag) + sum_mag = ctx.mag(s-one) + #if sum_mag - term_mag > ctx.prec: + # break + if -term_mag > ctx.prec: + break + k += 1 + cancellation = max_mag - sum_mag + if cancellation != cancellation: + break + if cancellation < extraprec or ctx._fixed_precision: + break + extraprec += min(ctx.prec, cancellation) + return s + finally: + ctx.prec = prec + + def power(ctx, x, y): + r"""Converts `x` and `y` to mpmath numbers and evaluates + `x^y = \exp(y \log(x))`:: + + >>> from mpmath import * + >>> mp.dps = 30; mp.pretty = True + >>> power(2, 0.5) + 1.41421356237309504880168872421 + + This shows the leading few digits of a large Mersenne prime + (performing the exact calculation ``2**43112609-1`` and + displaying the result in Python would be very slow):: + + >>> power(2, 43112609)-1 + 3.16470269330255923143453723949e+12978188 + """ + return ctx.convert(x) ** ctx.convert(y) + + def _zeta_int(ctx, n): + return ctx.zeta(n) + + def maxcalls(ctx, f, N): + """ + Return a wrapped copy of *f* that raises ``NoConvergence`` when *f* + has been called more than *N* times:: + + >>> from mpmath import * + >>> mp.dps = 15 + >>> f = maxcalls(sin, 10) + >>> print(sum(f(n) for n in range(10))) + 1.95520948210738 + >>> f(10) # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + NoConvergence: maxcalls: function evaluated 10 times + + """ + counter = [0] + def f_maxcalls_wrapped(*args, **kwargs): + counter[0] += 1 + if counter[0] > N: + raise ctx.NoConvergence("maxcalls: function evaluated %i times" % N) + return f(*args, **kwargs) + return f_maxcalls_wrapped + + def memoize(ctx, f): + """ + Return a wrapped copy of *f* that caches computed values, i.e. + a memoized copy of *f*. Values are only reused if the cached precision + is equal to or higher than the working precision:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> f = memoize(maxcalls(sin, 1)) + >>> f(2) + 0.909297426825682 + >>> f(2) + 0.909297426825682 + >>> mp.dps = 25 + >>> f(2) # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + NoConvergence: maxcalls: function evaluated 1 times + + """ + f_cache = {} + def f_cached(*args, **kwargs): + if kwargs: + key = args, tuple(kwargs.items()) + else: + key = args + prec = ctx.prec + if key in f_cache: + cprec, cvalue = f_cache[key] + if cprec >= prec: + return +cvalue + value = f(*args, **kwargs) + f_cache[key] = (prec, value) + return value + f_cached.__name__ = f.__name__ + f_cached.__doc__ = f.__doc__ + return f_cached diff --git a/lib/python3.12/site-packages/mpmath/ctx_fp.py b/lib/python3.12/site-packages/mpmath/ctx_fp.py new file mode 100644 index 0000000000000000000000000000000000000000..aa72ea5b03fde4da66b0d8fbf8ffa4012e3f6178 --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/ctx_fp.py @@ -0,0 +1,253 @@ +from .ctx_base import StandardBaseContext + +import math +import cmath +from . import math2 + +from . import function_docs + +from .libmp import mpf_bernoulli, to_float, int_types +from . import libmp + +class FPContext(StandardBaseContext): + """ + Context for fast low-precision arithmetic (53-bit precision, giving at most + about 15-digit accuracy), using Python's builtin float and complex. + """ + + def __init__(ctx): + StandardBaseContext.__init__(ctx) + + # Override SpecialFunctions implementation + ctx.loggamma = math2.loggamma + ctx._bernoulli_cache = {} + ctx.pretty = False + + ctx._init_aliases() + + _mpq = lambda cls, x: float(x[0])/x[1] + + NoConvergence = libmp.NoConvergence + + def _get_prec(ctx): return 53 + def _set_prec(ctx, p): return + def _get_dps(ctx): return 15 + def _set_dps(ctx, p): return + + _fixed_precision = True + + prec = property(_get_prec, _set_prec) + dps = property(_get_dps, _set_dps) + + zero = 0.0 + one = 1.0 + eps = math2.EPS + inf = math2.INF + ninf = math2.NINF + nan = math2.NAN + j = 1j + + # Called by SpecialFunctions.__init__() + @classmethod + def _wrap_specfun(cls, name, f, wrap): + if wrap: + def f_wrapped(ctx, *args, **kwargs): + convert = ctx.convert + args = [convert(a) for a in args] + return f(ctx, *args, **kwargs) + else: + f_wrapped = f + f_wrapped.__doc__ = function_docs.__dict__.get(name, f.__doc__) + setattr(cls, name, f_wrapped) + + def bernoulli(ctx, n): + cache = ctx._bernoulli_cache + if n in cache: + return cache[n] + cache[n] = to_float(mpf_bernoulli(n, 53, 'n'), strict=True) + return cache[n] + + pi = math2.pi + e = math2.e + euler = math2.euler + sqrt2 = 1.4142135623730950488 + sqrt5 = 2.2360679774997896964 + phi = 1.6180339887498948482 + ln2 = 0.69314718055994530942 + ln10 = 2.302585092994045684 + euler = 0.57721566490153286061 + catalan = 0.91596559417721901505 + khinchin = 2.6854520010653064453 + apery = 1.2020569031595942854 + glaisher = 1.2824271291006226369 + + absmin = absmax = abs + + def is_special(ctx, x): + return x - x != 0.0 + + def isnan(ctx, x): + return x != x + + def isinf(ctx, x): + return abs(x) == math2.INF + + def isnormal(ctx, x): + if x: + return x - x == 0.0 + return False + + def isnpint(ctx, x): + if type(x) is complex: + if x.imag: + return False + x = x.real + return x <= 0.0 and round(x) == x + + mpf = float + mpc = complex + + def convert(ctx, x): + try: + return float(x) + except: + return complex(x) + + power = staticmethod(math2.pow) + sqrt = staticmethod(math2.sqrt) + exp = staticmethod(math2.exp) + ln = log = staticmethod(math2.log) + cos = staticmethod(math2.cos) + sin = staticmethod(math2.sin) + tan = staticmethod(math2.tan) + cos_sin = staticmethod(math2.cos_sin) + acos = staticmethod(math2.acos) + asin = staticmethod(math2.asin) + atan = staticmethod(math2.atan) + cosh = staticmethod(math2.cosh) + sinh = staticmethod(math2.sinh) + tanh = staticmethod(math2.tanh) + gamma = staticmethod(math2.gamma) + rgamma = staticmethod(math2.rgamma) + fac = factorial = staticmethod(math2.factorial) + floor = staticmethod(math2.floor) + ceil = staticmethod(math2.ceil) + cospi = staticmethod(math2.cospi) + sinpi = staticmethod(math2.sinpi) + cbrt = staticmethod(math2.cbrt) + _nthroot = staticmethod(math2.nthroot) + _ei = staticmethod(math2.ei) + _e1 = staticmethod(math2.e1) + _zeta = _zeta_int = staticmethod(math2.zeta) + + # XXX: math2 + def arg(ctx, z): + z = complex(z) + return math.atan2(z.imag, z.real) + + def expj(ctx, x): + return ctx.exp(ctx.j*x) + + def expjpi(ctx, x): + return ctx.exp(ctx.j*ctx.pi*x) + + ldexp = math.ldexp + frexp = math.frexp + + def mag(ctx, z): + if z: + return ctx.frexp(abs(z))[1] + return ctx.ninf + + def isint(ctx, z): + if hasattr(z, "imag"): # float/int don't have .real/.imag in py2.5 + if z.imag: + return False + z = z.real + try: + return z == int(z) + except: + return False + + def nint_distance(ctx, z): + if hasattr(z, "imag"): # float/int don't have .real/.imag in py2.5 + n = round(z.real) + else: + n = round(z) + if n == z: + return n, ctx.ninf + return n, ctx.mag(abs(z-n)) + + def _convert_param(ctx, z): + if type(z) is tuple: + p, q = z + return ctx.mpf(p) / q, 'R' + if hasattr(z, "imag"): # float/int don't have .real/.imag in py2.5 + intz = int(z.real) + else: + intz = int(z) + if z == intz: + return intz, 'Z' + return z, 'R' + + def _is_real_type(ctx, z): + return isinstance(z, float) or isinstance(z, int_types) + + def _is_complex_type(ctx, z): + return isinstance(z, complex) + + def hypsum(ctx, p, q, types, coeffs, z, maxterms=6000, **kwargs): + coeffs = list(coeffs) + num = range(p) + den = range(p,p+q) + tol = ctx.eps + s = t = 1.0 + k = 0 + while 1: + for i in num: t *= (coeffs[i]+k) + for i in den: t /= (coeffs[i]+k) + k += 1; t /= k; t *= z; s += t + if abs(t) < tol: + return s + if k > maxterms: + raise ctx.NoConvergence + + def atan2(ctx, x, y): + return math.atan2(x, y) + + def psi(ctx, m, z): + m = int(m) + if m == 0: + return ctx.digamma(z) + return (-1)**(m+1) * ctx.fac(m) * ctx.zeta(m+1, z) + + digamma = staticmethod(math2.digamma) + + def harmonic(ctx, x): + x = ctx.convert(x) + if x == 0 or x == 1: + return x + return ctx.digamma(x+1) + ctx.euler + + nstr = str + + def to_fixed(ctx, x, prec): + return int(math.ldexp(x, prec)) + + def rand(ctx): + import random + return random.random() + + _erf = staticmethod(math2.erf) + _erfc = staticmethod(math2.erfc) + + def sum_accurately(ctx, terms, check_step=1): + s = ctx.zero + k = 0 + for term in terms(): + s += term + if (not k % check_step) and term: + if abs(term) <= 1e-18*abs(s): + break + k += 1 + return s diff --git a/lib/python3.12/site-packages/mpmath/ctx_iv.py b/lib/python3.12/site-packages/mpmath/ctx_iv.py new file mode 100644 index 0000000000000000000000000000000000000000..c038e00a5677e318d222b63c22d225e3045e1c2b --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/ctx_iv.py @@ -0,0 +1,551 @@ +import operator + +from . import libmp + +from .libmp.backend import basestring + +from .libmp import ( + int_types, MPZ_ONE, + prec_to_dps, dps_to_prec, repr_dps, + round_floor, round_ceiling, + fzero, finf, fninf, fnan, + mpf_le, mpf_neg, + from_int, from_float, from_str, from_rational, + mpi_mid, mpi_delta, mpi_str, + mpi_abs, mpi_pos, mpi_neg, mpi_add, mpi_sub, + mpi_mul, mpi_div, mpi_pow_int, mpi_pow, + mpi_from_str, + mpci_pos, mpci_neg, mpci_add, mpci_sub, mpci_mul, mpci_div, mpci_pow, + mpci_abs, mpci_pow, mpci_exp, mpci_log, + ComplexResult, + mpf_hash, mpc_hash) +from .matrices.matrices import _matrix + +mpi_zero = (fzero, fzero) + +from .ctx_base import StandardBaseContext + +new = object.__new__ + +def convert_mpf_(x, prec, rounding): + if hasattr(x, "_mpf_"): return x._mpf_ + if isinstance(x, int_types): return from_int(x, prec, rounding) + if isinstance(x, float): return from_float(x, prec, rounding) + if isinstance(x, basestring): return from_str(x, prec, rounding) + raise NotImplementedError + + +class ivmpf(object): + """ + Interval arithmetic class. Precision is controlled by iv.prec. + """ + + def __new__(cls, x=0): + return cls.ctx.convert(x) + + def cast(self, cls, f_convert): + a, b = self._mpi_ + if a == b: + return cls(f_convert(a)) + raise ValueError + + def __int__(self): + return self.cast(int, libmp.to_int) + + def __float__(self): + return self.cast(float, libmp.to_float) + + def __complex__(self): + return self.cast(complex, libmp.to_float) + + def __hash__(self): + a, b = self._mpi_ + if a == b: + return mpf_hash(a) + else: + return hash(self._mpi_) + + @property + def real(self): return self + + @property + def imag(self): return self.ctx.zero + + def conjugate(self): return self + + @property + def a(self): + a, b = self._mpi_ + return self.ctx.make_mpf((a, a)) + + @property + def b(self): + a, b = self._mpi_ + return self.ctx.make_mpf((b, b)) + + @property + def mid(self): + ctx = self.ctx + v = mpi_mid(self._mpi_, ctx.prec) + return ctx.make_mpf((v, v)) + + @property + def delta(self): + ctx = self.ctx + v = mpi_delta(self._mpi_, ctx.prec) + return ctx.make_mpf((v,v)) + + @property + def _mpci_(self): + return self._mpi_, mpi_zero + + def _compare(*args): + raise TypeError("no ordering relation is defined for intervals") + + __gt__ = _compare + __le__ = _compare + __gt__ = _compare + __ge__ = _compare + + def __contains__(self, t): + t = self.ctx.mpf(t) + return (self.a <= t.a) and (t.b <= self.b) + + def __str__(self): + return mpi_str(self._mpi_, self.ctx.prec) + + def __repr__(self): + if self.ctx.pretty: + return str(self) + a, b = self._mpi_ + n = repr_dps(self.ctx.prec) + a = libmp.to_str(a, n) + b = libmp.to_str(b, n) + return "mpi(%r, %r)" % (a, b) + + def _compare(s, t, cmpfun): + if not hasattr(t, "_mpi_"): + try: + t = s.ctx.convert(t) + except: + return NotImplemented + return cmpfun(s._mpi_, t._mpi_) + + def __eq__(s, t): return s._compare(t, libmp.mpi_eq) + def __ne__(s, t): return s._compare(t, libmp.mpi_ne) + def __lt__(s, t): return s._compare(t, libmp.mpi_lt) + def __le__(s, t): return s._compare(t, libmp.mpi_le) + def __gt__(s, t): return s._compare(t, libmp.mpi_gt) + def __ge__(s, t): return s._compare(t, libmp.mpi_ge) + + def __abs__(self): + return self.ctx.make_mpf(mpi_abs(self._mpi_, self.ctx.prec)) + def __pos__(self): + return self.ctx.make_mpf(mpi_pos(self._mpi_, self.ctx.prec)) + def __neg__(self): + return self.ctx.make_mpf(mpi_neg(self._mpi_, self.ctx.prec)) + + def ae(s, t, rel_eps=None, abs_eps=None): + return s.ctx.almosteq(s, t, rel_eps, abs_eps) + +class ivmpc(object): + + def __new__(cls, re=0, im=0): + re = cls.ctx.convert(re) + im = cls.ctx.convert(im) + y = new(cls) + y._mpci_ = re._mpi_, im._mpi_ + return y + + def __hash__(self): + (a, b), (c,d) = self._mpci_ + if a == b and c == d: + return mpc_hash((a, c)) + else: + return hash(self._mpci_) + + def __repr__(s): + if s.ctx.pretty: + return str(s) + return "iv.mpc(%s, %s)" % (repr(s.real), repr(s.imag)) + + def __str__(s): + return "(%s + %s*j)" % (str(s.real), str(s.imag)) + + @property + def a(self): + (a, b), (c,d) = self._mpci_ + return self.ctx.make_mpf((a, a)) + + @property + def b(self): + (a, b), (c,d) = self._mpci_ + return self.ctx.make_mpf((b, b)) + + @property + def c(self): + (a, b), (c,d) = self._mpci_ + return self.ctx.make_mpf((c, c)) + + @property + def d(self): + (a, b), (c,d) = self._mpci_ + return self.ctx.make_mpf((d, d)) + + @property + def real(s): + return s.ctx.make_mpf(s._mpci_[0]) + + @property + def imag(s): + return s.ctx.make_mpf(s._mpci_[1]) + + def conjugate(s): + a, b = s._mpci_ + return s.ctx.make_mpc((a, mpf_neg(b))) + + def overlap(s, t): + t = s.ctx.convert(t) + real_overlap = (s.a <= t.a <= s.b) or (s.a <= t.b <= s.b) or (t.a <= s.a <= t.b) or (t.a <= s.b <= t.b) + imag_overlap = (s.c <= t.c <= s.d) or (s.c <= t.d <= s.d) or (t.c <= s.c <= t.d) or (t.c <= s.d <= t.d) + return real_overlap and imag_overlap + + def __contains__(s, t): + t = s.ctx.convert(t) + return t.real in s.real and t.imag in s.imag + + def _compare(s, t, ne=False): + if not isinstance(t, s.ctx._types): + try: + t = s.ctx.convert(t) + except: + return NotImplemented + if hasattr(t, '_mpi_'): + tval = t._mpi_, mpi_zero + elif hasattr(t, '_mpci_'): + tval = t._mpci_ + if ne: + return s._mpci_ != tval + return s._mpci_ == tval + + def __eq__(s, t): return s._compare(t) + def __ne__(s, t): return s._compare(t, True) + + def __lt__(s, t): raise TypeError("complex intervals cannot be ordered") + __le__ = __gt__ = __ge__ = __lt__ + + def __neg__(s): return s.ctx.make_mpc(mpci_neg(s._mpci_, s.ctx.prec)) + def __pos__(s): return s.ctx.make_mpc(mpci_pos(s._mpci_, s.ctx.prec)) + def __abs__(s): return s.ctx.make_mpf(mpci_abs(s._mpci_, s.ctx.prec)) + + def ae(s, t, rel_eps=None, abs_eps=None): + return s.ctx.almosteq(s, t, rel_eps, abs_eps) + +def _binary_op(f_real, f_complex): + def g_complex(ctx, sval, tval): + return ctx.make_mpc(f_complex(sval, tval, ctx.prec)) + def g_real(ctx, sval, tval): + try: + return ctx.make_mpf(f_real(sval, tval, ctx.prec)) + except ComplexResult: + sval = (sval, mpi_zero) + tval = (tval, mpi_zero) + return g_complex(ctx, sval, tval) + def lop_real(s, t): + if isinstance(t, _matrix): return NotImplemented + ctx = s.ctx + if not isinstance(t, ctx._types): t = ctx.convert(t) + if hasattr(t, "_mpi_"): return g_real(ctx, s._mpi_, t._mpi_) + if hasattr(t, "_mpci_"): return g_complex(ctx, (s._mpi_, mpi_zero), t._mpci_) + return NotImplemented + def rop_real(s, t): + ctx = s.ctx + if not isinstance(t, ctx._types): t = ctx.convert(t) + if hasattr(t, "_mpi_"): return g_real(ctx, t._mpi_, s._mpi_) + if hasattr(t, "_mpci_"): return g_complex(ctx, t._mpci_, (s._mpi_, mpi_zero)) + return NotImplemented + def lop_complex(s, t): + if isinstance(t, _matrix): return NotImplemented + ctx = s.ctx + if not isinstance(t, s.ctx._types): + try: + t = s.ctx.convert(t) + except (ValueError, TypeError): + return NotImplemented + return g_complex(ctx, s._mpci_, t._mpci_) + def rop_complex(s, t): + ctx = s.ctx + if not isinstance(t, s.ctx._types): + t = s.ctx.convert(t) + return g_complex(ctx, t._mpci_, s._mpci_) + return lop_real, rop_real, lop_complex, rop_complex + +ivmpf.__add__, ivmpf.__radd__, ivmpc.__add__, ivmpc.__radd__ = _binary_op(mpi_add, mpci_add) +ivmpf.__sub__, ivmpf.__rsub__, ivmpc.__sub__, ivmpc.__rsub__ = _binary_op(mpi_sub, mpci_sub) +ivmpf.__mul__, ivmpf.__rmul__, ivmpc.__mul__, ivmpc.__rmul__ = _binary_op(mpi_mul, mpci_mul) +ivmpf.__div__, ivmpf.__rdiv__, ivmpc.__div__, ivmpc.__rdiv__ = _binary_op(mpi_div, mpci_div) +ivmpf.__pow__, ivmpf.__rpow__, ivmpc.__pow__, ivmpc.__rpow__ = _binary_op(mpi_pow, mpci_pow) + +ivmpf.__truediv__ = ivmpf.__div__; ivmpf.__rtruediv__ = ivmpf.__rdiv__ +ivmpc.__truediv__ = ivmpc.__div__; ivmpc.__rtruediv__ = ivmpc.__rdiv__ + +class ivmpf_constant(ivmpf): + def __new__(cls, f): + self = new(cls) + self._f = f + return self + def _get_mpi_(self): + prec = self.ctx._prec[0] + a = self._f(prec, round_floor) + b = self._f(prec, round_ceiling) + return a, b + _mpi_ = property(_get_mpi_) + +class MPIntervalContext(StandardBaseContext): + + def __init__(ctx): + ctx.mpf = type('ivmpf', (ivmpf,), {}) + ctx.mpc = type('ivmpc', (ivmpc,), {}) + ctx._types = (ctx.mpf, ctx.mpc) + ctx._constant = type('ivmpf_constant', (ivmpf_constant,), {}) + ctx._prec = [53] + ctx._set_prec(53) + ctx._constant._ctxdata = ctx.mpf._ctxdata = ctx.mpc._ctxdata = [ctx.mpf, new, ctx._prec] + ctx._constant.ctx = ctx.mpf.ctx = ctx.mpc.ctx = ctx + ctx.pretty = False + StandardBaseContext.__init__(ctx) + ctx._init_builtins() + + def _mpi(ctx, a, b=None): + if b is None: + return ctx.mpf(a) + return ctx.mpf((a,b)) + + def _init_builtins(ctx): + ctx.one = ctx.mpf(1) + ctx.zero = ctx.mpf(0) + ctx.inf = ctx.mpf('inf') + ctx.ninf = -ctx.inf + ctx.nan = ctx.mpf('nan') + ctx.j = ctx.mpc(0,1) + ctx.exp = ctx._wrap_mpi_function(libmp.mpi_exp, libmp.mpci_exp) + ctx.sqrt = ctx._wrap_mpi_function(libmp.mpi_sqrt) + ctx.ln = ctx._wrap_mpi_function(libmp.mpi_log, libmp.mpci_log) + ctx.cos = ctx._wrap_mpi_function(libmp.mpi_cos, libmp.mpci_cos) + ctx.sin = ctx._wrap_mpi_function(libmp.mpi_sin, libmp.mpci_sin) + ctx.tan = ctx._wrap_mpi_function(libmp.mpi_tan) + ctx.gamma = ctx._wrap_mpi_function(libmp.mpi_gamma, libmp.mpci_gamma) + ctx.loggamma = ctx._wrap_mpi_function(libmp.mpi_loggamma, libmp.mpci_loggamma) + ctx.rgamma = ctx._wrap_mpi_function(libmp.mpi_rgamma, libmp.mpci_rgamma) + ctx.factorial = ctx._wrap_mpi_function(libmp.mpi_factorial, libmp.mpci_factorial) + ctx.fac = ctx.factorial + + ctx.eps = ctx._constant(lambda prec, rnd: (0, MPZ_ONE, 1-prec, 1)) + ctx.pi = ctx._constant(libmp.mpf_pi) + ctx.e = ctx._constant(libmp.mpf_e) + ctx.ln2 = ctx._constant(libmp.mpf_ln2) + ctx.ln10 = ctx._constant(libmp.mpf_ln10) + ctx.phi = ctx._constant(libmp.mpf_phi) + ctx.euler = ctx._constant(libmp.mpf_euler) + ctx.catalan = ctx._constant(libmp.mpf_catalan) + ctx.glaisher = ctx._constant(libmp.mpf_glaisher) + ctx.khinchin = ctx._constant(libmp.mpf_khinchin) + ctx.twinprime = ctx._constant(libmp.mpf_twinprime) + + def _wrap_mpi_function(ctx, f_real, f_complex=None): + def g(x, **kwargs): + if kwargs: + prec = kwargs.get('prec', ctx._prec[0]) + else: + prec = ctx._prec[0] + x = ctx.convert(x) + if hasattr(x, "_mpi_"): + return ctx.make_mpf(f_real(x._mpi_, prec)) + if hasattr(x, "_mpci_"): + return ctx.make_mpc(f_complex(x._mpci_, prec)) + raise ValueError + return g + + @classmethod + def _wrap_specfun(cls, name, f, wrap): + if wrap: + def f_wrapped(ctx, *args, **kwargs): + convert = ctx.convert + args = [convert(a) for a in args] + prec = ctx.prec + try: + ctx.prec += 10 + retval = f(ctx, *args, **kwargs) + finally: + ctx.prec = prec + return +retval + else: + f_wrapped = f + setattr(cls, name, f_wrapped) + + def _set_prec(ctx, n): + ctx._prec[0] = max(1, int(n)) + ctx._dps = prec_to_dps(n) + + def _set_dps(ctx, n): + ctx._prec[0] = dps_to_prec(n) + ctx._dps = max(1, int(n)) + + prec = property(lambda ctx: ctx._prec[0], _set_prec) + dps = property(lambda ctx: ctx._dps, _set_dps) + + def make_mpf(ctx, v): + a = new(ctx.mpf) + a._mpi_ = v + return a + + def make_mpc(ctx, v): + a = new(ctx.mpc) + a._mpci_ = v + return a + + def _mpq(ctx, pq): + p, q = pq + a = libmp.from_rational(p, q, ctx.prec, round_floor) + b = libmp.from_rational(p, q, ctx.prec, round_ceiling) + return ctx.make_mpf((a, b)) + + def convert(ctx, x): + if isinstance(x, (ctx.mpf, ctx.mpc)): + return x + if isinstance(x, ctx._constant): + return +x + if isinstance(x, complex) or hasattr(x, "_mpc_"): + re = ctx.convert(x.real) + im = ctx.convert(x.imag) + return ctx.mpc(re,im) + if isinstance(x, basestring): + v = mpi_from_str(x, ctx.prec) + return ctx.make_mpf(v) + if hasattr(x, "_mpi_"): + a, b = x._mpi_ + else: + try: + a, b = x + except (TypeError, ValueError): + a = b = x + if hasattr(a, "_mpi_"): + a = a._mpi_[0] + else: + a = convert_mpf_(a, ctx.prec, round_floor) + if hasattr(b, "_mpi_"): + b = b._mpi_[1] + else: + b = convert_mpf_(b, ctx.prec, round_ceiling) + if a == fnan or b == fnan: + a = fninf + b = finf + assert mpf_le(a, b), "endpoints must be properly ordered" + return ctx.make_mpf((a, b)) + + def nstr(ctx, x, n=5, **kwargs): + x = ctx.convert(x) + if hasattr(x, "_mpi_"): + return libmp.mpi_to_str(x._mpi_, n, **kwargs) + if hasattr(x, "_mpci_"): + re = libmp.mpi_to_str(x._mpci_[0], n, **kwargs) + im = libmp.mpi_to_str(x._mpci_[1], n, **kwargs) + return "(%s + %s*j)" % (re, im) + + def mag(ctx, x): + x = ctx.convert(x) + if isinstance(x, ctx.mpc): + return max(ctx.mag(x.real), ctx.mag(x.imag)) + 1 + a, b = libmp.mpi_abs(x._mpi_) + sign, man, exp, bc = b + if man: + return exp+bc + if b == fzero: + return ctx.ninf + if b == fnan: + return ctx.nan + return ctx.inf + + def isnan(ctx, x): + return False + + def isinf(ctx, x): + return x == ctx.inf + + def isint(ctx, x): + x = ctx.convert(x) + a, b = x._mpi_ + if a == b: + sign, man, exp, bc = a + if man: + return exp >= 0 + return a == fzero + return None + + def ldexp(ctx, x, n): + a, b = ctx.convert(x)._mpi_ + a = libmp.mpf_shift(a, n) + b = libmp.mpf_shift(b, n) + return ctx.make_mpf((a,b)) + + def absmin(ctx, x): + return abs(ctx.convert(x)).a + + def absmax(ctx, x): + return abs(ctx.convert(x)).b + + def atan2(ctx, y, x): + y = ctx.convert(y)._mpi_ + x = ctx.convert(x)._mpi_ + return ctx.make_mpf(libmp.mpi_atan2(y,x,ctx.prec)) + + def _convert_param(ctx, x): + if isinstance(x, libmp.int_types): + return x, 'Z' + if isinstance(x, tuple): + p, q = x + return (ctx.mpf(p) / ctx.mpf(q), 'R') + x = ctx.convert(x) + if isinstance(x, ctx.mpf): + return x, 'R' + if isinstance(x, ctx.mpc): + return x, 'C' + raise ValueError + + def _is_real_type(ctx, z): + return isinstance(z, ctx.mpf) or isinstance(z, int_types) + + def _is_complex_type(ctx, z): + return isinstance(z, ctx.mpc) + + def hypsum(ctx, p, q, types, coeffs, z, maxterms=6000, **kwargs): + coeffs = list(coeffs) + num = range(p) + den = range(p,p+q) + #tol = ctx.eps + s = t = ctx.one + k = 0 + while 1: + for i in num: t *= (coeffs[i]+k) + for i in den: t /= (coeffs[i]+k) + k += 1; t /= k; t *= z; s += t + if t == 0: + return s + #if abs(t) < tol: + # return s + if k > maxterms: + raise ctx.NoConvergence + + +# Register with "numbers" ABC +# We do not subclass, hence we do not use the @abstractmethod checks. While +# this is less invasive it may turn out that we do not actually support +# parts of the expected interfaces. See +# http://docs.python.org/2/library/numbers.html for list of abstract +# methods. +try: + import numbers + numbers.Complex.register(ivmpc) + numbers.Real.register(ivmpf) +except ImportError: + pass diff --git a/lib/python3.12/site-packages/mpmath/ctx_mp.py b/lib/python3.12/site-packages/mpmath/ctx_mp.py new file mode 100644 index 0000000000000000000000000000000000000000..93594dd44474a415c74e4b0beb83bd7012666c9d --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/ctx_mp.py @@ -0,0 +1,1339 @@ +""" +This module defines the mpf, mpc classes, and standard functions for +operating with them. +""" +__docformat__ = 'plaintext' + +import functools + +import re + +from .ctx_base import StandardBaseContext + +from .libmp.backend import basestring, BACKEND + +from . import libmp + +from .libmp import (MPZ, MPZ_ZERO, MPZ_ONE, int_types, repr_dps, + round_floor, round_ceiling, dps_to_prec, round_nearest, prec_to_dps, + ComplexResult, to_pickable, from_pickable, normalize, + from_int, from_float, from_str, to_int, to_float, to_str, + from_rational, from_man_exp, + fone, fzero, finf, fninf, fnan, + mpf_abs, mpf_pos, mpf_neg, mpf_add, mpf_sub, mpf_mul, mpf_mul_int, + mpf_div, mpf_rdiv_int, mpf_pow_int, mpf_mod, + mpf_eq, mpf_cmp, mpf_lt, mpf_gt, mpf_le, mpf_ge, + mpf_hash, mpf_rand, + mpf_sum, + bitcount, to_fixed, + mpc_to_str, + mpc_to_complex, mpc_hash, mpc_pos, mpc_is_nonzero, mpc_neg, mpc_conjugate, + mpc_abs, mpc_add, mpc_add_mpf, mpc_sub, mpc_sub_mpf, mpc_mul, mpc_mul_mpf, + mpc_mul_int, mpc_div, mpc_div_mpf, mpc_pow, mpc_pow_mpf, mpc_pow_int, + mpc_mpf_div, + mpf_pow, + mpf_pi, mpf_degree, mpf_e, mpf_phi, mpf_ln2, mpf_ln10, + mpf_euler, mpf_catalan, mpf_apery, mpf_khinchin, + mpf_glaisher, mpf_twinprime, mpf_mertens, + int_types) + +from . import function_docs +from . import rational + +new = object.__new__ + +get_complex = re.compile(r'^\(?(?P[\+\-]?\d*(\.\d*)?(e[\+\-]?\d+)?)??' + r'(?P[\+\-]?\d*(\.\d*)?(e[\+\-]?\d+)?j)?\)?$') + +if BACKEND == 'sage': + from sage.libs.mpmath.ext_main import Context as BaseMPContext + # pickle hack + import sage.libs.mpmath.ext_main as _mpf_module +else: + from .ctx_mp_python import PythonMPContext as BaseMPContext + from . import ctx_mp_python as _mpf_module + +from .ctx_mp_python import _mpf, _mpc, mpnumeric + +class MPContext(BaseMPContext, StandardBaseContext): + """ + Context for multiprecision arithmetic with a global precision. + """ + + def __init__(ctx): + BaseMPContext.__init__(ctx) + ctx.trap_complex = False + ctx.pretty = False + ctx.types = [ctx.mpf, ctx.mpc, ctx.constant] + ctx._mpq = rational.mpq + ctx.default() + StandardBaseContext.__init__(ctx) + + ctx.mpq = rational.mpq + ctx.init_builtins() + + ctx.hyp_summators = {} + + ctx._init_aliases() + + # XXX: automate + try: + ctx.bernoulli.im_func.func_doc = function_docs.bernoulli + ctx.primepi.im_func.func_doc = function_docs.primepi + ctx.psi.im_func.func_doc = function_docs.psi + ctx.atan2.im_func.func_doc = function_docs.atan2 + except AttributeError: + # python 3 + ctx.bernoulli.__func__.func_doc = function_docs.bernoulli + ctx.primepi.__func__.func_doc = function_docs.primepi + ctx.psi.__func__.func_doc = function_docs.psi + ctx.atan2.__func__.func_doc = function_docs.atan2 + + ctx.digamma.func_doc = function_docs.digamma + ctx.cospi.func_doc = function_docs.cospi + ctx.sinpi.func_doc = function_docs.sinpi + + def init_builtins(ctx): + + mpf = ctx.mpf + mpc = ctx.mpc + + # Exact constants + ctx.one = ctx.make_mpf(fone) + ctx.zero = ctx.make_mpf(fzero) + ctx.j = ctx.make_mpc((fzero,fone)) + ctx.inf = ctx.make_mpf(finf) + ctx.ninf = ctx.make_mpf(fninf) + ctx.nan = ctx.make_mpf(fnan) + + eps = ctx.constant(lambda prec, rnd: (0, MPZ_ONE, 1-prec, 1), + "epsilon of working precision", "eps") + ctx.eps = eps + + # Approximate constants + ctx.pi = ctx.constant(mpf_pi, "pi", "pi") + ctx.ln2 = ctx.constant(mpf_ln2, "ln(2)", "ln2") + ctx.ln10 = ctx.constant(mpf_ln10, "ln(10)", "ln10") + ctx.phi = ctx.constant(mpf_phi, "Golden ratio phi", "phi") + ctx.e = ctx.constant(mpf_e, "e = exp(1)", "e") + ctx.euler = ctx.constant(mpf_euler, "Euler's constant", "euler") + ctx.catalan = ctx.constant(mpf_catalan, "Catalan's constant", "catalan") + ctx.khinchin = ctx.constant(mpf_khinchin, "Khinchin's constant", "khinchin") + ctx.glaisher = ctx.constant(mpf_glaisher, "Glaisher's constant", "glaisher") + ctx.apery = ctx.constant(mpf_apery, "Apery's constant", "apery") + ctx.degree = ctx.constant(mpf_degree, "1 deg = pi / 180", "degree") + ctx.twinprime = ctx.constant(mpf_twinprime, "Twin prime constant", "twinprime") + ctx.mertens = ctx.constant(mpf_mertens, "Mertens' constant", "mertens") + + # Standard functions + ctx.sqrt = ctx._wrap_libmp_function(libmp.mpf_sqrt, libmp.mpc_sqrt) + ctx.cbrt = ctx._wrap_libmp_function(libmp.mpf_cbrt, libmp.mpc_cbrt) + ctx.ln = ctx._wrap_libmp_function(libmp.mpf_log, libmp.mpc_log) + ctx.atan = ctx._wrap_libmp_function(libmp.mpf_atan, libmp.mpc_atan) + ctx.exp = ctx._wrap_libmp_function(libmp.mpf_exp, libmp.mpc_exp) + ctx.expj = ctx._wrap_libmp_function(libmp.mpf_expj, libmp.mpc_expj) + ctx.expjpi = ctx._wrap_libmp_function(libmp.mpf_expjpi, libmp.mpc_expjpi) + ctx.sin = ctx._wrap_libmp_function(libmp.mpf_sin, libmp.mpc_sin) + ctx.cos = ctx._wrap_libmp_function(libmp.mpf_cos, libmp.mpc_cos) + ctx.tan = ctx._wrap_libmp_function(libmp.mpf_tan, libmp.mpc_tan) + ctx.sinh = ctx._wrap_libmp_function(libmp.mpf_sinh, libmp.mpc_sinh) + ctx.cosh = ctx._wrap_libmp_function(libmp.mpf_cosh, libmp.mpc_cosh) + ctx.tanh = ctx._wrap_libmp_function(libmp.mpf_tanh, libmp.mpc_tanh) + ctx.asin = ctx._wrap_libmp_function(libmp.mpf_asin, libmp.mpc_asin) + ctx.acos = ctx._wrap_libmp_function(libmp.mpf_acos, libmp.mpc_acos) + ctx.atan = ctx._wrap_libmp_function(libmp.mpf_atan, libmp.mpc_atan) + ctx.asinh = ctx._wrap_libmp_function(libmp.mpf_asinh, libmp.mpc_asinh) + ctx.acosh = ctx._wrap_libmp_function(libmp.mpf_acosh, libmp.mpc_acosh) + ctx.atanh = ctx._wrap_libmp_function(libmp.mpf_atanh, libmp.mpc_atanh) + ctx.sinpi = ctx._wrap_libmp_function(libmp.mpf_sin_pi, libmp.mpc_sin_pi) + ctx.cospi = ctx._wrap_libmp_function(libmp.mpf_cos_pi, libmp.mpc_cos_pi) + ctx.floor = ctx._wrap_libmp_function(libmp.mpf_floor, libmp.mpc_floor) + ctx.ceil = ctx._wrap_libmp_function(libmp.mpf_ceil, libmp.mpc_ceil) + ctx.nint = ctx._wrap_libmp_function(libmp.mpf_nint, libmp.mpc_nint) + ctx.frac = ctx._wrap_libmp_function(libmp.mpf_frac, libmp.mpc_frac) + ctx.fib = ctx.fibonacci = ctx._wrap_libmp_function(libmp.mpf_fibonacci, libmp.mpc_fibonacci) + + ctx.gamma = ctx._wrap_libmp_function(libmp.mpf_gamma, libmp.mpc_gamma) + ctx.rgamma = ctx._wrap_libmp_function(libmp.mpf_rgamma, libmp.mpc_rgamma) + ctx.loggamma = ctx._wrap_libmp_function(libmp.mpf_loggamma, libmp.mpc_loggamma) + ctx.fac = ctx.factorial = ctx._wrap_libmp_function(libmp.mpf_factorial, libmp.mpc_factorial) + + ctx.digamma = ctx._wrap_libmp_function(libmp.mpf_psi0, libmp.mpc_psi0) + ctx.harmonic = ctx._wrap_libmp_function(libmp.mpf_harmonic, libmp.mpc_harmonic) + ctx.ei = ctx._wrap_libmp_function(libmp.mpf_ei, libmp.mpc_ei) + ctx.e1 = ctx._wrap_libmp_function(libmp.mpf_e1, libmp.mpc_e1) + ctx._ci = ctx._wrap_libmp_function(libmp.mpf_ci, libmp.mpc_ci) + ctx._si = ctx._wrap_libmp_function(libmp.mpf_si, libmp.mpc_si) + ctx.ellipk = ctx._wrap_libmp_function(libmp.mpf_ellipk, libmp.mpc_ellipk) + ctx._ellipe = ctx._wrap_libmp_function(libmp.mpf_ellipe, libmp.mpc_ellipe) + ctx.agm1 = ctx._wrap_libmp_function(libmp.mpf_agm1, libmp.mpc_agm1) + ctx._erf = ctx._wrap_libmp_function(libmp.mpf_erf, None) + ctx._erfc = ctx._wrap_libmp_function(libmp.mpf_erfc, None) + ctx._zeta = ctx._wrap_libmp_function(libmp.mpf_zeta, libmp.mpc_zeta) + ctx._altzeta = ctx._wrap_libmp_function(libmp.mpf_altzeta, libmp.mpc_altzeta) + + # Faster versions + ctx.sqrt = getattr(ctx, "_sage_sqrt", ctx.sqrt) + ctx.exp = getattr(ctx, "_sage_exp", ctx.exp) + ctx.ln = getattr(ctx, "_sage_ln", ctx.ln) + ctx.cos = getattr(ctx, "_sage_cos", ctx.cos) + ctx.sin = getattr(ctx, "_sage_sin", ctx.sin) + + def to_fixed(ctx, x, prec): + return x.to_fixed(prec) + + def hypot(ctx, x, y): + r""" + Computes the Euclidean norm of the vector `(x, y)`, equal + to `\sqrt{x^2 + y^2}`. Both `x` and `y` must be real.""" + x = ctx.convert(x) + y = ctx.convert(y) + return ctx.make_mpf(libmp.mpf_hypot(x._mpf_, y._mpf_, *ctx._prec_rounding)) + + def _gamma_upper_int(ctx, n, z): + n = int(ctx._re(n)) + if n == 0: + return ctx.e1(z) + if not hasattr(z, '_mpf_'): + raise NotImplementedError + prec, rounding = ctx._prec_rounding + real, imag = libmp.mpf_expint(n, z._mpf_, prec, rounding, gamma=True) + if imag is None: + return ctx.make_mpf(real) + else: + return ctx.make_mpc((real, imag)) + + def _expint_int(ctx, n, z): + n = int(n) + if n == 1: + return ctx.e1(z) + if not hasattr(z, '_mpf_'): + raise NotImplementedError + prec, rounding = ctx._prec_rounding + real, imag = libmp.mpf_expint(n, z._mpf_, prec, rounding) + if imag is None: + return ctx.make_mpf(real) + else: + return ctx.make_mpc((real, imag)) + + def _nthroot(ctx, x, n): + if hasattr(x, '_mpf_'): + try: + return ctx.make_mpf(libmp.mpf_nthroot(x._mpf_, n, *ctx._prec_rounding)) + except ComplexResult: + if ctx.trap_complex: + raise + x = (x._mpf_, libmp.fzero) + else: + x = x._mpc_ + return ctx.make_mpc(libmp.mpc_nthroot(x, n, *ctx._prec_rounding)) + + def _besselj(ctx, n, z): + prec, rounding = ctx._prec_rounding + if hasattr(z, '_mpf_'): + return ctx.make_mpf(libmp.mpf_besseljn(n, z._mpf_, prec, rounding)) + elif hasattr(z, '_mpc_'): + return ctx.make_mpc(libmp.mpc_besseljn(n, z._mpc_, prec, rounding)) + + def _agm(ctx, a, b=1): + prec, rounding = ctx._prec_rounding + if hasattr(a, '_mpf_') and hasattr(b, '_mpf_'): + try: + v = libmp.mpf_agm(a._mpf_, b._mpf_, prec, rounding) + return ctx.make_mpf(v) + except ComplexResult: + pass + if hasattr(a, '_mpf_'): a = (a._mpf_, libmp.fzero) + else: a = a._mpc_ + if hasattr(b, '_mpf_'): b = (b._mpf_, libmp.fzero) + else: b = b._mpc_ + return ctx.make_mpc(libmp.mpc_agm(a, b, prec, rounding)) + + def bernoulli(ctx, n): + return ctx.make_mpf(libmp.mpf_bernoulli(int(n), *ctx._prec_rounding)) + + def _zeta_int(ctx, n): + return ctx.make_mpf(libmp.mpf_zeta_int(int(n), *ctx._prec_rounding)) + + def atan2(ctx, y, x): + x = ctx.convert(x) + y = ctx.convert(y) + return ctx.make_mpf(libmp.mpf_atan2(y._mpf_, x._mpf_, *ctx._prec_rounding)) + + def psi(ctx, m, z): + z = ctx.convert(z) + m = int(m) + if ctx._is_real_type(z): + return ctx.make_mpf(libmp.mpf_psi(m, z._mpf_, *ctx._prec_rounding)) + else: + return ctx.make_mpc(libmp.mpc_psi(m, z._mpc_, *ctx._prec_rounding)) + + def cos_sin(ctx, x, **kwargs): + if type(x) not in ctx.types: + x = ctx.convert(x) + prec, rounding = ctx._parse_prec(kwargs) + if hasattr(x, '_mpf_'): + c, s = libmp.mpf_cos_sin(x._mpf_, prec, rounding) + return ctx.make_mpf(c), ctx.make_mpf(s) + elif hasattr(x, '_mpc_'): + c, s = libmp.mpc_cos_sin(x._mpc_, prec, rounding) + return ctx.make_mpc(c), ctx.make_mpc(s) + else: + return ctx.cos(x, **kwargs), ctx.sin(x, **kwargs) + + def cospi_sinpi(ctx, x, **kwargs): + if type(x) not in ctx.types: + x = ctx.convert(x) + prec, rounding = ctx._parse_prec(kwargs) + if hasattr(x, '_mpf_'): + c, s = libmp.mpf_cos_sin_pi(x._mpf_, prec, rounding) + return ctx.make_mpf(c), ctx.make_mpf(s) + elif hasattr(x, '_mpc_'): + c, s = libmp.mpc_cos_sin_pi(x._mpc_, prec, rounding) + return ctx.make_mpc(c), ctx.make_mpc(s) + else: + return ctx.cos(x, **kwargs), ctx.sin(x, **kwargs) + + def clone(ctx): + """ + Create a copy of the context, with the same working precision. + """ + a = ctx.__class__() + a.prec = ctx.prec + return a + + # Several helper methods + # TODO: add more of these, make consistent, write docstrings, ... + + def _is_real_type(ctx, x): + if hasattr(x, '_mpc_') or type(x) is complex: + return False + return True + + def _is_complex_type(ctx, x): + if hasattr(x, '_mpc_') or type(x) is complex: + return True + return False + + def isnan(ctx, x): + """ + Return *True* if *x* is a NaN (not-a-number), or for a complex + number, whether either the real or complex part is NaN; + otherwise return *False*:: + + >>> from mpmath import * + >>> isnan(3.14) + False + >>> isnan(nan) + True + >>> isnan(mpc(3.14,2.72)) + False + >>> isnan(mpc(3.14,nan)) + True + + """ + if hasattr(x, "_mpf_"): + return x._mpf_ == fnan + if hasattr(x, "_mpc_"): + return fnan in x._mpc_ + if isinstance(x, int_types) or isinstance(x, rational.mpq): + return False + x = ctx.convert(x) + if hasattr(x, '_mpf_') or hasattr(x, '_mpc_'): + return ctx.isnan(x) + raise TypeError("isnan() needs a number as input") + + def isfinite(ctx, x): + """ + Return *True* if *x* is a finite number, i.e. neither + an infinity or a NaN. + + >>> from mpmath import * + >>> isfinite(inf) + False + >>> isfinite(-inf) + False + >>> isfinite(3) + True + >>> isfinite(nan) + False + >>> isfinite(3+4j) + True + >>> isfinite(mpc(3,inf)) + False + >>> isfinite(mpc(nan,3)) + False + + """ + if ctx.isinf(x) or ctx.isnan(x): + return False + return True + + def isnpint(ctx, x): + """ + Determine if *x* is a nonpositive integer. + """ + if not x: + return True + if hasattr(x, '_mpf_'): + sign, man, exp, bc = x._mpf_ + return sign and exp >= 0 + if hasattr(x, '_mpc_'): + return not x.imag and ctx.isnpint(x.real) + if type(x) in int_types: + return x <= 0 + if isinstance(x, ctx.mpq): + p, q = x._mpq_ + if not p: + return True + return q == 1 and p <= 0 + return ctx.isnpint(ctx.convert(x)) + + def __str__(ctx): + lines = ["Mpmath settings:", + (" mp.prec = %s" % ctx.prec).ljust(30) + "[default: 53]", + (" mp.dps = %s" % ctx.dps).ljust(30) + "[default: 15]", + (" mp.trap_complex = %s" % ctx.trap_complex).ljust(30) + "[default: False]", + ] + return "\n".join(lines) + + @property + def _repr_digits(ctx): + return repr_dps(ctx._prec) + + @property + def _str_digits(ctx): + return ctx._dps + + def extraprec(ctx, n, normalize_output=False): + """ + The block + + with extraprec(n): + + + increases the precision n bits, executes , and then + restores the precision. + + extraprec(n)(f) returns a decorated version of the function f + that increases the working precision by n bits before execution, + and restores the parent precision afterwards. With + normalize_output=True, it rounds the return value to the parent + precision. + """ + return PrecisionManager(ctx, lambda p: p + n, None, normalize_output) + + def extradps(ctx, n, normalize_output=False): + """ + This function is analogous to extraprec (see documentation) + but changes the decimal precision instead of the number of bits. + """ + return PrecisionManager(ctx, None, lambda d: d + n, normalize_output) + + def workprec(ctx, n, normalize_output=False): + """ + The block + + with workprec(n): + + + sets the precision to n bits, executes , and then restores + the precision. + + workprec(n)(f) returns a decorated version of the function f + that sets the precision to n bits before execution, + and restores the precision afterwards. With normalize_output=True, + it rounds the return value to the parent precision. + """ + return PrecisionManager(ctx, lambda p: n, None, normalize_output) + + def workdps(ctx, n, normalize_output=False): + """ + This function is analogous to workprec (see documentation) + but changes the decimal precision instead of the number of bits. + """ + return PrecisionManager(ctx, None, lambda d: n, normalize_output) + + def autoprec(ctx, f, maxprec=None, catch=(), verbose=False): + r""" + Return a wrapped copy of *f* that repeatedly evaluates *f* + with increasing precision until the result converges to the + full precision used at the point of the call. + + This heuristically protects against rounding errors, at the cost of + roughly a 2x slowdown compared to manually setting the optimal + precision. This method can, however, easily be fooled if the results + from *f* depend "discontinuously" on the precision, for instance + if catastrophic cancellation can occur. Therefore, :func:`~mpmath.autoprec` + should be used judiciously. + + **Examples** + + Many functions are sensitive to perturbations of the input arguments. + If the arguments are decimal numbers, they may have to be converted + to binary at a much higher precision. If the amount of required + extra precision is unknown, :func:`~mpmath.autoprec` is convenient:: + + >>> from mpmath import * + >>> mp.dps = 15 + >>> mp.pretty = True + >>> besselj(5, 125 * 10**28) # Exact input + -8.03284785591801e-17 + >>> besselj(5, '1.25e30') # Bad + 7.12954868316652e-16 + >>> autoprec(besselj)(5, '1.25e30') # Good + -8.03284785591801e-17 + + The following fails to converge because `\sin(\pi) = 0` whereas all + finite-precision approximations of `\pi` give nonzero values:: + + >>> autoprec(sin)(pi) # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + NoConvergence: autoprec: prec increased to 2910 without convergence + + As the following example shows, :func:`~mpmath.autoprec` can protect against + cancellation, but is fooled by too severe cancellation:: + + >>> x = 1e-10 + >>> exp(x)-1; expm1(x); autoprec(lambda t: exp(t)-1)(x) + 1.00000008274037e-10 + 1.00000000005e-10 + 1.00000000005e-10 + >>> x = 1e-50 + >>> exp(x)-1; expm1(x); autoprec(lambda t: exp(t)-1)(x) + 0.0 + 1.0e-50 + 0.0 + + With *catch*, an exception or list of exceptions to intercept + may be specified. The raised exception is interpreted + as signaling insufficient precision. This permits, for example, + evaluating a function where a too low precision results in a + division by zero:: + + >>> f = lambda x: 1/(exp(x)-1) + >>> f(1e-30) + Traceback (most recent call last): + ... + ZeroDivisionError + >>> autoprec(f, catch=ZeroDivisionError)(1e-30) + 1.0e+30 + + + """ + def f_autoprec_wrapped(*args, **kwargs): + prec = ctx.prec + if maxprec is None: + maxprec2 = ctx._default_hyper_maxprec(prec) + else: + maxprec2 = maxprec + try: + ctx.prec = prec + 10 + try: + v1 = f(*args, **kwargs) + except catch: + v1 = ctx.nan + prec2 = prec + 20 + while 1: + ctx.prec = prec2 + try: + v2 = f(*args, **kwargs) + except catch: + v2 = ctx.nan + if v1 == v2: + break + err = ctx.mag(v2-v1) - ctx.mag(v2) + if err < (-prec): + break + if verbose: + print("autoprec: target=%s, prec=%s, accuracy=%s" \ + % (prec, prec2, -err)) + v1 = v2 + if prec2 >= maxprec2: + raise ctx.NoConvergence(\ + "autoprec: prec increased to %i without convergence"\ + % prec2) + prec2 += int(prec2*2) + prec2 = min(prec2, maxprec2) + finally: + ctx.prec = prec + return +v2 + return f_autoprec_wrapped + + def nstr(ctx, x, n=6, **kwargs): + """ + Convert an ``mpf`` or ``mpc`` to a decimal string literal with *n* + significant digits. The small default value for *n* is chosen to + make this function useful for printing collections of numbers + (lists, matrices, etc). + + If *x* is a list or tuple, :func:`~mpmath.nstr` is applied recursively + to each element. For unrecognized classes, :func:`~mpmath.nstr` + simply returns ``str(x)``. + + The companion function :func:`~mpmath.nprint` prints the result + instead of returning it. + + The keyword arguments *strip_zeros*, *min_fixed*, *max_fixed* + and *show_zero_exponent* are forwarded to :func:`~mpmath.libmp.to_str`. + + The number will be printed in fixed-point format if the position + of the leading digit is strictly between min_fixed + (default = min(-dps/3,-5)) and max_fixed (default = dps). + + To force fixed-point format always, set min_fixed = -inf, + max_fixed = +inf. To force floating-point format, set + min_fixed >= max_fixed. + + >>> from mpmath import * + >>> nstr([+pi, ldexp(1,-500)]) + '[3.14159, 3.05494e-151]' + >>> nprint([+pi, ldexp(1,-500)]) + [3.14159, 3.05494e-151] + >>> nstr(mpf("5e-10"), 5) + '5.0e-10' + >>> nstr(mpf("5e-10"), 5, strip_zeros=False) + '5.0000e-10' + >>> nstr(mpf("5e-10"), 5, strip_zeros=False, min_fixed=-11) + '0.00000000050000' + >>> nstr(mpf(0), 5, show_zero_exponent=True) + '0.0e+0' + + """ + if isinstance(x, list): + return "[%s]" % (", ".join(ctx.nstr(c, n, **kwargs) for c in x)) + if isinstance(x, tuple): + return "(%s)" % (", ".join(ctx.nstr(c, n, **kwargs) for c in x)) + if hasattr(x, '_mpf_'): + return to_str(x._mpf_, n, **kwargs) + if hasattr(x, '_mpc_'): + return "(" + mpc_to_str(x._mpc_, n, **kwargs) + ")" + if isinstance(x, basestring): + return repr(x) + if isinstance(x, ctx.matrix): + return x.__nstr__(n, **kwargs) + return str(x) + + def _convert_fallback(ctx, x, strings): + if strings and isinstance(x, basestring): + if 'j' in x.lower(): + x = x.lower().replace(' ', '') + match = get_complex.match(x) + re = match.group('re') + if not re: + re = 0 + im = match.group('im').rstrip('j') + return ctx.mpc(ctx.convert(re), ctx.convert(im)) + if hasattr(x, "_mpi_"): + a, b = x._mpi_ + if a == b: + return ctx.make_mpf(a) + else: + raise ValueError("can only create mpf from zero-width interval") + raise TypeError("cannot create mpf from " + repr(x)) + + def mpmathify(ctx, *args, **kwargs): + return ctx.convert(*args, **kwargs) + + def _parse_prec(ctx, kwargs): + if kwargs: + if kwargs.get('exact'): + return 0, 'f' + prec, rounding = ctx._prec_rounding + if 'rounding' in kwargs: + rounding = kwargs['rounding'] + if 'prec' in kwargs: + prec = kwargs['prec'] + if prec == ctx.inf: + return 0, 'f' + else: + prec = int(prec) + elif 'dps' in kwargs: + dps = kwargs['dps'] + if dps == ctx.inf: + return 0, 'f' + prec = dps_to_prec(dps) + return prec, rounding + return ctx._prec_rounding + + _exact_overflow_msg = "the exact result does not fit in memory" + + _hypsum_msg = """hypsum() failed to converge to the requested %i bits of accuracy +using a working precision of %i bits. Try with a higher maxprec, +maxterms, or set zeroprec.""" + + def hypsum(ctx, p, q, flags, coeffs, z, accurate_small=True, **kwargs): + if hasattr(z, "_mpf_"): + key = p, q, flags, 'R' + v = z._mpf_ + elif hasattr(z, "_mpc_"): + key = p, q, flags, 'C' + v = z._mpc_ + if key not in ctx.hyp_summators: + ctx.hyp_summators[key] = libmp.make_hyp_summator(key)[1] + summator = ctx.hyp_summators[key] + prec = ctx.prec + maxprec = kwargs.get('maxprec', ctx._default_hyper_maxprec(prec)) + extraprec = 50 + epsshift = 25 + # Jumps in magnitude occur when parameters are close to negative + # integers. We must ensure that these terms are included in + # the sum and added accurately + magnitude_check = {} + max_total_jump = 0 + for i, c in enumerate(coeffs): + if flags[i] == 'Z': + if i >= p and c <= 0: + ok = False + for ii, cc in enumerate(coeffs[:p]): + # Note: c <= cc or c < cc, depending on convention + if flags[ii] == 'Z' and cc <= 0 and c <= cc: + ok = True + if not ok: + raise ZeroDivisionError("pole in hypergeometric series") + continue + n, d = ctx.nint_distance(c) + n = -int(n) + d = -d + if i >= p and n >= 0 and d > 4: + if n in magnitude_check: + magnitude_check[n] += d + else: + magnitude_check[n] = d + extraprec = max(extraprec, d - prec + 60) + max_total_jump += abs(d) + while 1: + if extraprec > maxprec: + raise ValueError(ctx._hypsum_msg % (prec, prec+extraprec)) + wp = prec + extraprec + if magnitude_check: + mag_dict = dict((n,None) for n in magnitude_check) + else: + mag_dict = {} + zv, have_complex, magnitude = summator(coeffs, v, prec, wp, \ + epsshift, mag_dict, **kwargs) + cancel = -magnitude + jumps_resolved = True + if extraprec < max_total_jump: + for n in mag_dict.values(): + if (n is None) or (n < prec): + jumps_resolved = False + break + accurate = (cancel < extraprec-25-5 or not accurate_small) + if jumps_resolved: + if accurate: + break + # zero? + zeroprec = kwargs.get('zeroprec') + if zeroprec is not None: + if cancel > zeroprec: + if have_complex: + return ctx.mpc(0) + else: + return ctx.zero + + # Some near-singularities were not included, so increase + # precision and repeat until they are + extraprec *= 2 + # Possible workaround for bad roundoff in fixed-point arithmetic + epsshift += 5 + extraprec += 5 + + if type(zv) is tuple: + if have_complex: + return ctx.make_mpc(zv) + else: + return ctx.make_mpf(zv) + else: + return zv + + def ldexp(ctx, x, n): + r""" + Computes `x 2^n` efficiently. No rounding is performed. + The argument `x` must be a real floating-point number (or + possible to convert into one) and `n` must be a Python ``int``. + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = False + >>> ldexp(1, 10) + mpf('1024.0') + >>> ldexp(1, -3) + mpf('0.125') + + """ + x = ctx.convert(x) + return ctx.make_mpf(libmp.mpf_shift(x._mpf_, n)) + + def frexp(ctx, x): + r""" + Given a real number `x`, returns `(y, n)` with `y \in [0.5, 1)`, + `n` a Python integer, and such that `x = y 2^n`. No rounding is + performed. + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = False + >>> frexp(7.5) + (mpf('0.9375'), 3) + + """ + x = ctx.convert(x) + y, n = libmp.mpf_frexp(x._mpf_) + return ctx.make_mpf(y), n + + def fneg(ctx, x, **kwargs): + """ + Negates the number *x*, giving a floating-point result, optionally + using a custom precision and rounding mode. + + See the documentation of :func:`~mpmath.fadd` for a detailed description + of how to specify precision and rounding. + + **Examples** + + An mpmath number is returned:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = False + >>> fneg(2.5) + mpf('-2.5') + >>> fneg(-5+2j) + mpc(real='5.0', imag='-2.0') + + Precise control over rounding is possible:: + + >>> x = fadd(2, 1e-100, exact=True) + >>> fneg(x) + mpf('-2.0') + >>> fneg(x, rounding='f') + mpf('-2.0000000000000004') + + Negating with and without roundoff:: + + >>> n = 200000000000000000000001 + >>> print(int(-mpf(n))) + -200000000000000016777216 + >>> print(int(fneg(n))) + -200000000000000016777216 + >>> print(int(fneg(n, prec=log(n,2)+1))) + -200000000000000000000001 + >>> print(int(fneg(n, dps=log(n,10)+1))) + -200000000000000000000001 + >>> print(int(fneg(n, prec=inf))) + -200000000000000000000001 + >>> print(int(fneg(n, dps=inf))) + -200000000000000000000001 + >>> print(int(fneg(n, exact=True))) + -200000000000000000000001 + + """ + prec, rounding = ctx._parse_prec(kwargs) + x = ctx.convert(x) + if hasattr(x, '_mpf_'): + return ctx.make_mpf(mpf_neg(x._mpf_, prec, rounding)) + if hasattr(x, '_mpc_'): + return ctx.make_mpc(mpc_neg(x._mpc_, prec, rounding)) + raise ValueError("Arguments need to be mpf or mpc compatible numbers") + + def fadd(ctx, x, y, **kwargs): + """ + Adds the numbers *x* and *y*, giving a floating-point result, + optionally using a custom precision and rounding mode. + + The default precision is the working precision of the context. + You can specify a custom precision in bits by passing the *prec* keyword + argument, or by providing an equivalent decimal precision with the *dps* + keyword argument. If the precision is set to ``+inf``, or if the flag + *exact=True* is passed, an exact addition with no rounding is performed. + + When the precision is finite, the optional *rounding* keyword argument + specifies the direction of rounding. Valid options are ``'n'`` for + nearest (default), ``'f'`` for floor, ``'c'`` for ceiling, ``'d'`` + for down, ``'u'`` for up. + + **Examples** + + Using :func:`~mpmath.fadd` with precision and rounding control:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = False + >>> fadd(2, 1e-20) + mpf('2.0') + >>> fadd(2, 1e-20, rounding='u') + mpf('2.0000000000000004') + >>> nprint(fadd(2, 1e-20, prec=100), 25) + 2.00000000000000000001 + >>> nprint(fadd(2, 1e-20, dps=15), 25) + 2.0 + >>> nprint(fadd(2, 1e-20, dps=25), 25) + 2.00000000000000000001 + >>> nprint(fadd(2, 1e-20, exact=True), 25) + 2.00000000000000000001 + + Exact addition avoids cancellation errors, enforcing familiar laws + of numbers such as `x+y-x = y`, which don't hold in floating-point + arithmetic with finite precision:: + + >>> x, y = mpf(2), mpf('1e-1000') + >>> print(x + y - x) + 0.0 + >>> print(fadd(x, y, prec=inf) - x) + 1.0e-1000 + >>> print(fadd(x, y, exact=True) - x) + 1.0e-1000 + + Exact addition can be inefficient and may be impossible to perform + with large magnitude differences:: + + >>> fadd(1, '1e-100000000000000000000', prec=inf) + Traceback (most recent call last): + ... + OverflowError: the exact result does not fit in memory + + """ + prec, rounding = ctx._parse_prec(kwargs) + x = ctx.convert(x) + y = ctx.convert(y) + try: + if hasattr(x, '_mpf_'): + if hasattr(y, '_mpf_'): + return ctx.make_mpf(mpf_add(x._mpf_, y._mpf_, prec, rounding)) + if hasattr(y, '_mpc_'): + return ctx.make_mpc(mpc_add_mpf(y._mpc_, x._mpf_, prec, rounding)) + if hasattr(x, '_mpc_'): + if hasattr(y, '_mpf_'): + return ctx.make_mpc(mpc_add_mpf(x._mpc_, y._mpf_, prec, rounding)) + if hasattr(y, '_mpc_'): + return ctx.make_mpc(mpc_add(x._mpc_, y._mpc_, prec, rounding)) + except (ValueError, OverflowError): + raise OverflowError(ctx._exact_overflow_msg) + raise ValueError("Arguments need to be mpf or mpc compatible numbers") + + def fsub(ctx, x, y, **kwargs): + """ + Subtracts the numbers *x* and *y*, giving a floating-point result, + optionally using a custom precision and rounding mode. + + See the documentation of :func:`~mpmath.fadd` for a detailed description + of how to specify precision and rounding. + + **Examples** + + Using :func:`~mpmath.fsub` with precision and rounding control:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = False + >>> fsub(2, 1e-20) + mpf('2.0') + >>> fsub(2, 1e-20, rounding='d') + mpf('1.9999999999999998') + >>> nprint(fsub(2, 1e-20, prec=100), 25) + 1.99999999999999999999 + >>> nprint(fsub(2, 1e-20, dps=15), 25) + 2.0 + >>> nprint(fsub(2, 1e-20, dps=25), 25) + 1.99999999999999999999 + >>> nprint(fsub(2, 1e-20, exact=True), 25) + 1.99999999999999999999 + + Exact subtraction avoids cancellation errors, enforcing familiar laws + of numbers such as `x-y+y = x`, which don't hold in floating-point + arithmetic with finite precision:: + + >>> x, y = mpf(2), mpf('1e1000') + >>> print(x - y + y) + 0.0 + >>> print(fsub(x, y, prec=inf) + y) + 2.0 + >>> print(fsub(x, y, exact=True) + y) + 2.0 + + Exact addition can be inefficient and may be impossible to perform + with large magnitude differences:: + + >>> fsub(1, '1e-100000000000000000000', prec=inf) + Traceback (most recent call last): + ... + OverflowError: the exact result does not fit in memory + + """ + prec, rounding = ctx._parse_prec(kwargs) + x = ctx.convert(x) + y = ctx.convert(y) + try: + if hasattr(x, '_mpf_'): + if hasattr(y, '_mpf_'): + return ctx.make_mpf(mpf_sub(x._mpf_, y._mpf_, prec, rounding)) + if hasattr(y, '_mpc_'): + return ctx.make_mpc(mpc_sub((x._mpf_, fzero), y._mpc_, prec, rounding)) + if hasattr(x, '_mpc_'): + if hasattr(y, '_mpf_'): + return ctx.make_mpc(mpc_sub_mpf(x._mpc_, y._mpf_, prec, rounding)) + if hasattr(y, '_mpc_'): + return ctx.make_mpc(mpc_sub(x._mpc_, y._mpc_, prec, rounding)) + except (ValueError, OverflowError): + raise OverflowError(ctx._exact_overflow_msg) + raise ValueError("Arguments need to be mpf or mpc compatible numbers") + + def fmul(ctx, x, y, **kwargs): + """ + Multiplies the numbers *x* and *y*, giving a floating-point result, + optionally using a custom precision and rounding mode. + + See the documentation of :func:`~mpmath.fadd` for a detailed description + of how to specify precision and rounding. + + **Examples** + + The result is an mpmath number:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = False + >>> fmul(2, 5.0) + mpf('10.0') + >>> fmul(0.5j, 0.5) + mpc(real='0.0', imag='0.25') + + Avoiding roundoff:: + + >>> x, y = 10**10+1, 10**15+1 + >>> print(x*y) + 10000000001000010000000001 + >>> print(mpf(x) * mpf(y)) + 1.0000000001e+25 + >>> print(int(mpf(x) * mpf(y))) + 10000000001000011026399232 + >>> print(int(fmul(x, y))) + 10000000001000011026399232 + >>> print(int(fmul(x, y, dps=25))) + 10000000001000010000000001 + >>> print(int(fmul(x, y, exact=True))) + 10000000001000010000000001 + + Exact multiplication with complex numbers can be inefficient and may + be impossible to perform with large magnitude differences between + real and imaginary parts:: + + >>> x = 1+2j + >>> y = mpc(2, '1e-100000000000000000000') + >>> fmul(x, y) + mpc(real='2.0', imag='4.0') + >>> fmul(x, y, rounding='u') + mpc(real='2.0', imag='4.0000000000000009') + >>> fmul(x, y, exact=True) + Traceback (most recent call last): + ... + OverflowError: the exact result does not fit in memory + + """ + prec, rounding = ctx._parse_prec(kwargs) + x = ctx.convert(x) + y = ctx.convert(y) + try: + if hasattr(x, '_mpf_'): + if hasattr(y, '_mpf_'): + return ctx.make_mpf(mpf_mul(x._mpf_, y._mpf_, prec, rounding)) + if hasattr(y, '_mpc_'): + return ctx.make_mpc(mpc_mul_mpf(y._mpc_, x._mpf_, prec, rounding)) + if hasattr(x, '_mpc_'): + if hasattr(y, '_mpf_'): + return ctx.make_mpc(mpc_mul_mpf(x._mpc_, y._mpf_, prec, rounding)) + if hasattr(y, '_mpc_'): + return ctx.make_mpc(mpc_mul(x._mpc_, y._mpc_, prec, rounding)) + except (ValueError, OverflowError): + raise OverflowError(ctx._exact_overflow_msg) + raise ValueError("Arguments need to be mpf or mpc compatible numbers") + + def fdiv(ctx, x, y, **kwargs): + """ + Divides the numbers *x* and *y*, giving a floating-point result, + optionally using a custom precision and rounding mode. + + See the documentation of :func:`~mpmath.fadd` for a detailed description + of how to specify precision and rounding. + + **Examples** + + The result is an mpmath number:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = False + >>> fdiv(3, 2) + mpf('1.5') + >>> fdiv(2, 3) + mpf('0.66666666666666663') + >>> fdiv(2+4j, 0.5) + mpc(real='4.0', imag='8.0') + + The rounding direction and precision can be controlled:: + + >>> fdiv(2, 3, dps=3) # Should be accurate to at least 3 digits + mpf('0.6666259765625') + >>> fdiv(2, 3, rounding='d') + mpf('0.66666666666666663') + >>> fdiv(2, 3, prec=60) + mpf('0.66666666666666667') + >>> fdiv(2, 3, rounding='u') + mpf('0.66666666666666674') + + Checking the error of a division by performing it at higher precision:: + + >>> fdiv(2, 3) - fdiv(2, 3, prec=100) + mpf('-3.7007434154172148e-17') + + Unlike :func:`~mpmath.fadd`, :func:`~mpmath.fmul`, etc., exact division is not + allowed since the quotient of two floating-point numbers generally + does not have an exact floating-point representation. (In the + future this might be changed to allow the case where the division + is actually exact.) + + >>> fdiv(2, 3, exact=True) + Traceback (most recent call last): + ... + ValueError: division is not an exact operation + + """ + prec, rounding = ctx._parse_prec(kwargs) + if not prec: + raise ValueError("division is not an exact operation") + x = ctx.convert(x) + y = ctx.convert(y) + if hasattr(x, '_mpf_'): + if hasattr(y, '_mpf_'): + return ctx.make_mpf(mpf_div(x._mpf_, y._mpf_, prec, rounding)) + if hasattr(y, '_mpc_'): + return ctx.make_mpc(mpc_div((x._mpf_, fzero), y._mpc_, prec, rounding)) + if hasattr(x, '_mpc_'): + if hasattr(y, '_mpf_'): + return ctx.make_mpc(mpc_div_mpf(x._mpc_, y._mpf_, prec, rounding)) + if hasattr(y, '_mpc_'): + return ctx.make_mpc(mpc_div(x._mpc_, y._mpc_, prec, rounding)) + raise ValueError("Arguments need to be mpf or mpc compatible numbers") + + def nint_distance(ctx, x): + r""" + Return `(n,d)` where `n` is the nearest integer to `x` and `d` is + an estimate of `\log_2(|x-n|)`. If `d < 0`, `-d` gives the precision + (measured in bits) lost to cancellation when computing `x-n`. + + >>> from mpmath import * + >>> n, d = nint_distance(5) + >>> print(n); print(d) + 5 + -inf + >>> n, d = nint_distance(mpf(5)) + >>> print(n); print(d) + 5 + -inf + >>> n, d = nint_distance(mpf(5.00000001)) + >>> print(n); print(d) + 5 + -26 + >>> n, d = nint_distance(mpf(4.99999999)) + >>> print(n); print(d) + 5 + -26 + >>> n, d = nint_distance(mpc(5,10)) + >>> print(n); print(d) + 5 + 4 + >>> n, d = nint_distance(mpc(5,0.000001)) + >>> print(n); print(d) + 5 + -19 + + """ + typx = type(x) + if typx in int_types: + return int(x), ctx.ninf + elif typx is rational.mpq: + p, q = x._mpq_ + n, r = divmod(p, q) + if 2*r >= q: + n += 1 + elif not r: + return n, ctx.ninf + # log(p/q-n) = log((p-nq)/q) = log(p-nq) - log(q) + d = bitcount(abs(p-n*q)) - bitcount(q) + return n, d + if hasattr(x, "_mpf_"): + re = x._mpf_ + im_dist = ctx.ninf + elif hasattr(x, "_mpc_"): + re, im = x._mpc_ + isign, iman, iexp, ibc = im + if iman: + im_dist = iexp + ibc + elif im == fzero: + im_dist = ctx.ninf + else: + raise ValueError("requires a finite number") + else: + x = ctx.convert(x) + if hasattr(x, "_mpf_") or hasattr(x, "_mpc_"): + return ctx.nint_distance(x) + else: + raise TypeError("requires an mpf/mpc") + sign, man, exp, bc = re + mag = exp+bc + # |x| < 0.5 + if mag < 0: + n = 0 + re_dist = mag + elif man: + # exact integer + if exp >= 0: + n = man << exp + re_dist = ctx.ninf + # exact half-integer + elif exp == -1: + n = (man>>1)+1 + re_dist = 0 + else: + d = (-exp-1) + t = man >> d + if t & 1: + t += 1 + man = (t<>1 # int(t)>>1 + re_dist = exp+bitcount(man) + if sign: + n = -n + elif re == fzero: + re_dist = ctx.ninf + n = 0 + else: + raise ValueError("requires a finite number") + return n, max(re_dist, im_dist) + + def fprod(ctx, factors): + r""" + Calculates a product containing a finite number of factors (for + infinite products, see :func:`~mpmath.nprod`). The factors will be + converted to mpmath numbers. + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = False + >>> fprod([1, 2, 0.5, 7]) + mpf('7.0') + + """ + orig = ctx.prec + try: + v = ctx.one + for p in factors: + v *= p + finally: + ctx.prec = orig + return +v + + def rand(ctx): + """ + Returns an ``mpf`` with value chosen randomly from `[0, 1)`. + The number of randomly generated bits in the mantissa is equal + to the working precision. + """ + return ctx.make_mpf(mpf_rand(ctx._prec)) + + def fraction(ctx, p, q): + """ + Given Python integers `(p, q)`, returns a lazy ``mpf`` representing + the fraction `p/q`. The value is updated with the precision. + + >>> from mpmath import * + >>> mp.dps = 15 + >>> a = fraction(1,100) + >>> b = mpf(1)/100 + >>> print(a); print(b) + 0.01 + 0.01 + >>> mp.dps = 30 + >>> print(a); print(b) # a will be accurate + 0.01 + 0.0100000000000000002081668171172 + >>> mp.dps = 15 + """ + return ctx.constant(lambda prec, rnd: from_rational(p, q, prec, rnd), + '%s/%s' % (p, q)) + + def absmin(ctx, x): + return abs(ctx.convert(x)) + + def absmax(ctx, x): + return abs(ctx.convert(x)) + + def _as_points(ctx, x): + # XXX: remove this? + if hasattr(x, '_mpi_'): + a, b = x._mpi_ + return [ctx.make_mpf(a), ctx.make_mpf(b)] + return x + + ''' + def _zetasum(ctx, s, a, b): + """ + Computes sum of k^(-s) for k = a, a+1, ..., b with a, b both small + integers. + """ + a = int(a) + b = int(b) + s = ctx.convert(s) + prec, rounding = ctx._prec_rounding + if hasattr(s, '_mpf_'): + v = ctx.make_mpf(libmp.mpf_zetasum(s._mpf_, a, b, prec)) + elif hasattr(s, '_mpc_'): + v = ctx.make_mpc(libmp.mpc_zetasum(s._mpc_, a, b, prec)) + return v + ''' + + def _zetasum_fast(ctx, s, a, n, derivatives=[0], reflect=False): + if not (ctx.isint(a) and hasattr(s, "_mpc_")): + raise NotImplementedError + a = int(a) + prec = ctx._prec + xs, ys = libmp.mpc_zetasum(s._mpc_, a, n, derivatives, reflect, prec) + xs = [ctx.make_mpc(x) for x in xs] + ys = [ctx.make_mpc(y) for y in ys] + return xs, ys + +class PrecisionManager: + def __init__(self, ctx, precfun, dpsfun, normalize_output=False): + self.ctx = ctx + self.precfun = precfun + self.dpsfun = dpsfun + self.normalize_output = normalize_output + def __call__(self, f): + @functools.wraps(f) + def g(*args, **kwargs): + orig = self.ctx.prec + try: + if self.precfun: + self.ctx.prec = self.precfun(self.ctx.prec) + else: + self.ctx.dps = self.dpsfun(self.ctx.dps) + if self.normalize_output: + v = f(*args, **kwargs) + if type(v) is tuple: + return tuple([+a for a in v]) + return +v + else: + return f(*args, **kwargs) + finally: + self.ctx.prec = orig + return g + def __enter__(self): + self.origp = self.ctx.prec + if self.precfun: + self.ctx.prec = self.precfun(self.ctx.prec) + else: + self.ctx.dps = self.dpsfun(self.ctx.dps) + def __exit__(self, exc_type, exc_val, exc_tb): + self.ctx.prec = self.origp + return False + + +if __name__ == '__main__': + import doctest + doctest.testmod() diff --git a/lib/python3.12/site-packages/mpmath/ctx_mp_python.py b/lib/python3.12/site-packages/mpmath/ctx_mp_python.py new file mode 100644 index 0000000000000000000000000000000000000000..cfbd72fb8300bf840069c38529b7b41418d26eeb --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/ctx_mp_python.py @@ -0,0 +1,1149 @@ +#from ctx_base import StandardBaseContext + +from .libmp.backend import basestring, exec_ + +from .libmp import (MPZ, MPZ_ZERO, MPZ_ONE, int_types, repr_dps, + round_floor, round_ceiling, dps_to_prec, round_nearest, prec_to_dps, + ComplexResult, to_pickable, from_pickable, normalize, + from_int, from_float, from_npfloat, from_Decimal, from_str, to_int, to_float, to_str, + from_rational, from_man_exp, + fone, fzero, finf, fninf, fnan, + mpf_abs, mpf_pos, mpf_neg, mpf_add, mpf_sub, mpf_mul, mpf_mul_int, + mpf_div, mpf_rdiv_int, mpf_pow_int, mpf_mod, + mpf_eq, mpf_cmp, mpf_lt, mpf_gt, mpf_le, mpf_ge, + mpf_hash, mpf_rand, + mpf_sum, + bitcount, to_fixed, + mpc_to_str, + mpc_to_complex, mpc_hash, mpc_pos, mpc_is_nonzero, mpc_neg, mpc_conjugate, + mpc_abs, mpc_add, mpc_add_mpf, mpc_sub, mpc_sub_mpf, mpc_mul, mpc_mul_mpf, + mpc_mul_int, mpc_div, mpc_div_mpf, mpc_pow, mpc_pow_mpf, mpc_pow_int, + mpc_mpf_div, + mpf_pow, + mpf_pi, mpf_degree, mpf_e, mpf_phi, mpf_ln2, mpf_ln10, + mpf_euler, mpf_catalan, mpf_apery, mpf_khinchin, + mpf_glaisher, mpf_twinprime, mpf_mertens, + int_types) + +from . import rational +from . import function_docs + +new = object.__new__ + +class mpnumeric(object): + """Base class for mpf and mpc.""" + __slots__ = [] + def __new__(cls, val): + raise NotImplementedError + +class _mpf(mpnumeric): + """ + An mpf instance holds a real-valued floating-point number. mpf:s + work analogously to Python floats, but support arbitrary-precision + arithmetic. + """ + __slots__ = ['_mpf_'] + + def __new__(cls, val=fzero, **kwargs): + """A new mpf can be created from a Python float, an int, a + or a decimal string representing a number in floating-point + format.""" + prec, rounding = cls.context._prec_rounding + if kwargs: + prec = kwargs.get('prec', prec) + if 'dps' in kwargs: + prec = dps_to_prec(kwargs['dps']) + rounding = kwargs.get('rounding', rounding) + if type(val) is cls: + sign, man, exp, bc = val._mpf_ + if (not man) and exp: + return val + v = new(cls) + v._mpf_ = normalize(sign, man, exp, bc, prec, rounding) + return v + elif type(val) is tuple: + if len(val) == 2: + v = new(cls) + v._mpf_ = from_man_exp(val[0], val[1], prec, rounding) + return v + if len(val) == 4: + if val not in (finf, fninf, fnan): + sign, man, exp, bc = val + val = normalize(sign, MPZ(man), exp, bc, prec, rounding) + v = new(cls) + v._mpf_ = val + return v + raise ValueError + else: + v = new(cls) + v._mpf_ = mpf_pos(cls.mpf_convert_arg(val, prec, rounding), prec, rounding) + return v + + @classmethod + def mpf_convert_arg(cls, x, prec, rounding): + if isinstance(x, int_types): return from_int(x) + if isinstance(x, float): return from_float(x) + if isinstance(x, basestring): return from_str(x, prec, rounding) + if isinstance(x, cls.context.constant): return x.func(prec, rounding) + if hasattr(x, '_mpf_'): return x._mpf_ + if hasattr(x, '_mpmath_'): + t = cls.context.convert(x._mpmath_(prec, rounding)) + if hasattr(t, '_mpf_'): + return t._mpf_ + if hasattr(x, '_mpi_'): + a, b = x._mpi_ + if a == b: + return a + raise ValueError("can only create mpf from zero-width interval") + raise TypeError("cannot create mpf from " + repr(x)) + + @classmethod + def mpf_convert_rhs(cls, x): + if isinstance(x, int_types): return from_int(x) + if isinstance(x, float): return from_float(x) + if isinstance(x, complex_types): return cls.context.mpc(x) + if isinstance(x, rational.mpq): + p, q = x._mpq_ + return from_rational(p, q, cls.context.prec) + if hasattr(x, '_mpf_'): return x._mpf_ + if hasattr(x, '_mpmath_'): + t = cls.context.convert(x._mpmath_(*cls.context._prec_rounding)) + if hasattr(t, '_mpf_'): + return t._mpf_ + return t + return NotImplemented + + @classmethod + def mpf_convert_lhs(cls, x): + x = cls.mpf_convert_rhs(x) + if type(x) is tuple: + return cls.context.make_mpf(x) + return x + + man_exp = property(lambda self: self._mpf_[1:3]) + man = property(lambda self: self._mpf_[1]) + exp = property(lambda self: self._mpf_[2]) + bc = property(lambda self: self._mpf_[3]) + + real = property(lambda self: self) + imag = property(lambda self: self.context.zero) + + conjugate = lambda self: self + + def __getstate__(self): return to_pickable(self._mpf_) + def __setstate__(self, val): self._mpf_ = from_pickable(val) + + def __repr__(s): + if s.context.pretty: + return str(s) + return "mpf('%s')" % to_str(s._mpf_, s.context._repr_digits) + + def __str__(s): return to_str(s._mpf_, s.context._str_digits) + def __hash__(s): return mpf_hash(s._mpf_) + def __int__(s): return int(to_int(s._mpf_)) + def __long__(s): return long(to_int(s._mpf_)) + def __float__(s): return to_float(s._mpf_, rnd=s.context._prec_rounding[1]) + def __complex__(s): return complex(float(s)) + def __nonzero__(s): return s._mpf_ != fzero + + __bool__ = __nonzero__ + + def __abs__(s): + cls, new, (prec, rounding) = s._ctxdata + v = new(cls) + v._mpf_ = mpf_abs(s._mpf_, prec, rounding) + return v + + def __pos__(s): + cls, new, (prec, rounding) = s._ctxdata + v = new(cls) + v._mpf_ = mpf_pos(s._mpf_, prec, rounding) + return v + + def __neg__(s): + cls, new, (prec, rounding) = s._ctxdata + v = new(cls) + v._mpf_ = mpf_neg(s._mpf_, prec, rounding) + return v + + def _cmp(s, t, func): + if hasattr(t, '_mpf_'): + t = t._mpf_ + else: + t = s.mpf_convert_rhs(t) + if t is NotImplemented: + return t + return func(s._mpf_, t) + + def __cmp__(s, t): return s._cmp(t, mpf_cmp) + def __lt__(s, t): return s._cmp(t, mpf_lt) + def __gt__(s, t): return s._cmp(t, mpf_gt) + def __le__(s, t): return s._cmp(t, mpf_le) + def __ge__(s, t): return s._cmp(t, mpf_ge) + + def __ne__(s, t): + v = s.__eq__(t) + if v is NotImplemented: + return v + return not v + + def __rsub__(s, t): + cls, new, (prec, rounding) = s._ctxdata + if type(t) in int_types: + v = new(cls) + v._mpf_ = mpf_sub(from_int(t), s._mpf_, prec, rounding) + return v + t = s.mpf_convert_lhs(t) + if t is NotImplemented: + return t + return t - s + + def __rdiv__(s, t): + cls, new, (prec, rounding) = s._ctxdata + if isinstance(t, int_types): + v = new(cls) + v._mpf_ = mpf_rdiv_int(t, s._mpf_, prec, rounding) + return v + t = s.mpf_convert_lhs(t) + if t is NotImplemented: + return t + return t / s + + def __rpow__(s, t): + t = s.mpf_convert_lhs(t) + if t is NotImplemented: + return t + return t ** s + + def __rmod__(s, t): + t = s.mpf_convert_lhs(t) + if t is NotImplemented: + return t + return t % s + + def sqrt(s): + return s.context.sqrt(s) + + def ae(s, t, rel_eps=None, abs_eps=None): + return s.context.almosteq(s, t, rel_eps, abs_eps) + + def to_fixed(self, prec): + return to_fixed(self._mpf_, prec) + + def __round__(self, *args): + return round(float(self), *args) + +mpf_binary_op = """ +def %NAME%(self, other): + mpf, new, (prec, rounding) = self._ctxdata + sval = self._mpf_ + if hasattr(other, '_mpf_'): + tval = other._mpf_ + %WITH_MPF% + ttype = type(other) + if ttype in int_types: + %WITH_INT% + elif ttype is float: + tval = from_float(other) + %WITH_MPF% + elif hasattr(other, '_mpc_'): + tval = other._mpc_ + mpc = type(other) + %WITH_MPC% + elif ttype is complex: + tval = from_float(other.real), from_float(other.imag) + mpc = self.context.mpc + %WITH_MPC% + if isinstance(other, mpnumeric): + return NotImplemented + try: + other = mpf.context.convert(other, strings=False) + except TypeError: + return NotImplemented + return self.%NAME%(other) +""" + +return_mpf = "; obj = new(mpf); obj._mpf_ = val; return obj" +return_mpc = "; obj = new(mpc); obj._mpc_ = val; return obj" + +mpf_pow_same = """ + try: + val = mpf_pow(sval, tval, prec, rounding) %s + except ComplexResult: + if mpf.context.trap_complex: + raise + mpc = mpf.context.mpc + val = mpc_pow((sval, fzero), (tval, fzero), prec, rounding) %s +""" % (return_mpf, return_mpc) + +def binary_op(name, with_mpf='', with_int='', with_mpc=''): + code = mpf_binary_op + code = code.replace("%WITH_INT%", with_int) + code = code.replace("%WITH_MPC%", with_mpc) + code = code.replace("%WITH_MPF%", with_mpf) + code = code.replace("%NAME%", name) + np = {} + exec_(code, globals(), np) + return np[name] + +_mpf.__eq__ = binary_op('__eq__', + 'return mpf_eq(sval, tval)', + 'return mpf_eq(sval, from_int(other))', + 'return (tval[1] == fzero) and mpf_eq(tval[0], sval)') + +_mpf.__add__ = binary_op('__add__', + 'val = mpf_add(sval, tval, prec, rounding)' + return_mpf, + 'val = mpf_add(sval, from_int(other), prec, rounding)' + return_mpf, + 'val = mpc_add_mpf(tval, sval, prec, rounding)' + return_mpc) + +_mpf.__sub__ = binary_op('__sub__', + 'val = mpf_sub(sval, tval, prec, rounding)' + return_mpf, + 'val = mpf_sub(sval, from_int(other), prec, rounding)' + return_mpf, + 'val = mpc_sub((sval, fzero), tval, prec, rounding)' + return_mpc) + +_mpf.__mul__ = binary_op('__mul__', + 'val = mpf_mul(sval, tval, prec, rounding)' + return_mpf, + 'val = mpf_mul_int(sval, other, prec, rounding)' + return_mpf, + 'val = mpc_mul_mpf(tval, sval, prec, rounding)' + return_mpc) + +_mpf.__div__ = binary_op('__div__', + 'val = mpf_div(sval, tval, prec, rounding)' + return_mpf, + 'val = mpf_div(sval, from_int(other), prec, rounding)' + return_mpf, + 'val = mpc_mpf_div(sval, tval, prec, rounding)' + return_mpc) + +_mpf.__mod__ = binary_op('__mod__', + 'val = mpf_mod(sval, tval, prec, rounding)' + return_mpf, + 'val = mpf_mod(sval, from_int(other), prec, rounding)' + return_mpf, + 'raise NotImplementedError("complex modulo")') + +_mpf.__pow__ = binary_op('__pow__', + mpf_pow_same, + 'val = mpf_pow_int(sval, other, prec, rounding)' + return_mpf, + 'val = mpc_pow((sval, fzero), tval, prec, rounding)' + return_mpc) + +_mpf.__radd__ = _mpf.__add__ +_mpf.__rmul__ = _mpf.__mul__ +_mpf.__truediv__ = _mpf.__div__ +_mpf.__rtruediv__ = _mpf.__rdiv__ + + +class _constant(_mpf): + """Represents a mathematical constant with dynamic precision. + When printed or used in an arithmetic operation, a constant + is converted to a regular mpf at the working precision. A + regular mpf can also be obtained using the operation +x.""" + + def __new__(cls, func, name, docname=''): + a = object.__new__(cls) + a.name = name + a.func = func + a.__doc__ = getattr(function_docs, docname, '') + return a + + def __call__(self, prec=None, dps=None, rounding=None): + prec2, rounding2 = self.context._prec_rounding + if not prec: prec = prec2 + if not rounding: rounding = rounding2 + if dps: prec = dps_to_prec(dps) + return self.context.make_mpf(self.func(prec, rounding)) + + @property + def _mpf_(self): + prec, rounding = self.context._prec_rounding + return self.func(prec, rounding) + + def __repr__(self): + return "<%s: %s~>" % (self.name, self.context.nstr(self(dps=15))) + + +class _mpc(mpnumeric): + """ + An mpc represents a complex number using a pair of mpf:s (one + for the real part and another for the imaginary part.) The mpc + class behaves fairly similarly to Python's complex type. + """ + + __slots__ = ['_mpc_'] + + def __new__(cls, real=0, imag=0): + s = object.__new__(cls) + if isinstance(real, complex_types): + real, imag = real.real, real.imag + elif hasattr(real, '_mpc_'): + s._mpc_ = real._mpc_ + return s + real = cls.context.mpf(real) + imag = cls.context.mpf(imag) + s._mpc_ = (real._mpf_, imag._mpf_) + return s + + real = property(lambda self: self.context.make_mpf(self._mpc_[0])) + imag = property(lambda self: self.context.make_mpf(self._mpc_[1])) + + def __getstate__(self): + return to_pickable(self._mpc_[0]), to_pickable(self._mpc_[1]) + + def __setstate__(self, val): + self._mpc_ = from_pickable(val[0]), from_pickable(val[1]) + + def __repr__(s): + if s.context.pretty: + return str(s) + r = repr(s.real)[4:-1] + i = repr(s.imag)[4:-1] + return "%s(real=%s, imag=%s)" % (type(s).__name__, r, i) + + def __str__(s): + return "(%s)" % mpc_to_str(s._mpc_, s.context._str_digits) + + def __complex__(s): + return mpc_to_complex(s._mpc_, rnd=s.context._prec_rounding[1]) + + def __pos__(s): + cls, new, (prec, rounding) = s._ctxdata + v = new(cls) + v._mpc_ = mpc_pos(s._mpc_, prec, rounding) + return v + + def __abs__(s): + prec, rounding = s.context._prec_rounding + v = new(s.context.mpf) + v._mpf_ = mpc_abs(s._mpc_, prec, rounding) + return v + + def __neg__(s): + cls, new, (prec, rounding) = s._ctxdata + v = new(cls) + v._mpc_ = mpc_neg(s._mpc_, prec, rounding) + return v + + def conjugate(s): + cls, new, (prec, rounding) = s._ctxdata + v = new(cls) + v._mpc_ = mpc_conjugate(s._mpc_, prec, rounding) + return v + + def __nonzero__(s): + return mpc_is_nonzero(s._mpc_) + + __bool__ = __nonzero__ + + def __hash__(s): + return mpc_hash(s._mpc_) + + @classmethod + def mpc_convert_lhs(cls, x): + try: + y = cls.context.convert(x) + return y + except TypeError: + return NotImplemented + + def __eq__(s, t): + if not hasattr(t, '_mpc_'): + if isinstance(t, str): + return False + t = s.mpc_convert_lhs(t) + if t is NotImplemented: + return t + return s.real == t.real and s.imag == t.imag + + def __ne__(s, t): + b = s.__eq__(t) + if b is NotImplemented: + return b + return not b + + def _compare(*args): + raise TypeError("no ordering relation is defined for complex numbers") + + __gt__ = _compare + __le__ = _compare + __gt__ = _compare + __ge__ = _compare + + def __add__(s, t): + cls, new, (prec, rounding) = s._ctxdata + if not hasattr(t, '_mpc_'): + t = s.mpc_convert_lhs(t) + if t is NotImplemented: + return t + if hasattr(t, '_mpf_'): + v = new(cls) + v._mpc_ = mpc_add_mpf(s._mpc_, t._mpf_, prec, rounding) + return v + v = new(cls) + v._mpc_ = mpc_add(s._mpc_, t._mpc_, prec, rounding) + return v + + def __sub__(s, t): + cls, new, (prec, rounding) = s._ctxdata + if not hasattr(t, '_mpc_'): + t = s.mpc_convert_lhs(t) + if t is NotImplemented: + return t + if hasattr(t, '_mpf_'): + v = new(cls) + v._mpc_ = mpc_sub_mpf(s._mpc_, t._mpf_, prec, rounding) + return v + v = new(cls) + v._mpc_ = mpc_sub(s._mpc_, t._mpc_, prec, rounding) + return v + + def __mul__(s, t): + cls, new, (prec, rounding) = s._ctxdata + if not hasattr(t, '_mpc_'): + if isinstance(t, int_types): + v = new(cls) + v._mpc_ = mpc_mul_int(s._mpc_, t, prec, rounding) + return v + t = s.mpc_convert_lhs(t) + if t is NotImplemented: + return t + if hasattr(t, '_mpf_'): + v = new(cls) + v._mpc_ = mpc_mul_mpf(s._mpc_, t._mpf_, prec, rounding) + return v + t = s.mpc_convert_lhs(t) + v = new(cls) + v._mpc_ = mpc_mul(s._mpc_, t._mpc_, prec, rounding) + return v + + def __div__(s, t): + cls, new, (prec, rounding) = s._ctxdata + if not hasattr(t, '_mpc_'): + t = s.mpc_convert_lhs(t) + if t is NotImplemented: + return t + if hasattr(t, '_mpf_'): + v = new(cls) + v._mpc_ = mpc_div_mpf(s._mpc_, t._mpf_, prec, rounding) + return v + v = new(cls) + v._mpc_ = mpc_div(s._mpc_, t._mpc_, prec, rounding) + return v + + def __pow__(s, t): + cls, new, (prec, rounding) = s._ctxdata + if isinstance(t, int_types): + v = new(cls) + v._mpc_ = mpc_pow_int(s._mpc_, t, prec, rounding) + return v + t = s.mpc_convert_lhs(t) + if t is NotImplemented: + return t + v = new(cls) + if hasattr(t, '_mpf_'): + v._mpc_ = mpc_pow_mpf(s._mpc_, t._mpf_, prec, rounding) + else: + v._mpc_ = mpc_pow(s._mpc_, t._mpc_, prec, rounding) + return v + + __radd__ = __add__ + + def __rsub__(s, t): + t = s.mpc_convert_lhs(t) + if t is NotImplemented: + return t + return t - s + + def __rmul__(s, t): + cls, new, (prec, rounding) = s._ctxdata + if isinstance(t, int_types): + v = new(cls) + v._mpc_ = mpc_mul_int(s._mpc_, t, prec, rounding) + return v + t = s.mpc_convert_lhs(t) + if t is NotImplemented: + return t + return t * s + + def __rdiv__(s, t): + t = s.mpc_convert_lhs(t) + if t is NotImplemented: + return t + return t / s + + def __rpow__(s, t): + t = s.mpc_convert_lhs(t) + if t is NotImplemented: + return t + return t ** s + + __truediv__ = __div__ + __rtruediv__ = __rdiv__ + + def ae(s, t, rel_eps=None, abs_eps=None): + return s.context.almosteq(s, t, rel_eps, abs_eps) + + +complex_types = (complex, _mpc) + + +class PythonMPContext(object): + + def __init__(ctx): + ctx._prec_rounding = [53, round_nearest] + ctx.mpf = type('mpf', (_mpf,), {}) + ctx.mpc = type('mpc', (_mpc,), {}) + ctx.mpf._ctxdata = [ctx.mpf, new, ctx._prec_rounding] + ctx.mpc._ctxdata = [ctx.mpc, new, ctx._prec_rounding] + ctx.mpf.context = ctx + ctx.mpc.context = ctx + ctx.constant = type('constant', (_constant,), {}) + ctx.constant._ctxdata = [ctx.mpf, new, ctx._prec_rounding] + ctx.constant.context = ctx + + def make_mpf(ctx, v): + a = new(ctx.mpf) + a._mpf_ = v + return a + + def make_mpc(ctx, v): + a = new(ctx.mpc) + a._mpc_ = v + return a + + def default(ctx): + ctx._prec = ctx._prec_rounding[0] = 53 + ctx._dps = 15 + ctx.trap_complex = False + + def _set_prec(ctx, n): + ctx._prec = ctx._prec_rounding[0] = max(1, int(n)) + ctx._dps = prec_to_dps(n) + + def _set_dps(ctx, n): + ctx._prec = ctx._prec_rounding[0] = dps_to_prec(n) + ctx._dps = max(1, int(n)) + + prec = property(lambda ctx: ctx._prec, _set_prec) + dps = property(lambda ctx: ctx._dps, _set_dps) + + def convert(ctx, x, strings=True): + """ + Converts *x* to an ``mpf`` or ``mpc``. If *x* is of type ``mpf``, + ``mpc``, ``int``, ``float``, ``complex``, the conversion + will be performed losslessly. + + If *x* is a string, the result will be rounded to the present + working precision. Strings representing fractions or complex + numbers are permitted. + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = False + >>> mpmathify(3.5) + mpf('3.5') + >>> mpmathify('2.1') + mpf('2.1000000000000001') + >>> mpmathify('3/4') + mpf('0.75') + >>> mpmathify('2+3j') + mpc(real='2.0', imag='3.0') + + """ + if type(x) in ctx.types: return x + if isinstance(x, int_types): return ctx.make_mpf(from_int(x)) + if isinstance(x, float): return ctx.make_mpf(from_float(x)) + if isinstance(x, complex): + return ctx.make_mpc((from_float(x.real), from_float(x.imag))) + if type(x).__module__ == 'numpy': return ctx.npconvert(x) + if isinstance(x, numbers.Rational): # e.g. Fraction + try: x = rational.mpq(int(x.numerator), int(x.denominator)) + except: pass + prec, rounding = ctx._prec_rounding + if isinstance(x, rational.mpq): + p, q = x._mpq_ + return ctx.make_mpf(from_rational(p, q, prec)) + if strings and isinstance(x, basestring): + try: + _mpf_ = from_str(x, prec, rounding) + return ctx.make_mpf(_mpf_) + except ValueError: + pass + if hasattr(x, '_mpf_'): return ctx.make_mpf(x._mpf_) + if hasattr(x, '_mpc_'): return ctx.make_mpc(x._mpc_) + if hasattr(x, '_mpmath_'): + return ctx.convert(x._mpmath_(prec, rounding)) + if type(x).__module__ == 'decimal': + try: return ctx.make_mpf(from_Decimal(x, prec, rounding)) + except: pass + return ctx._convert_fallback(x, strings) + + def npconvert(ctx, x): + """ + Converts *x* to an ``mpf`` or ``mpc``. *x* should be a numpy + scalar. + """ + import numpy as np + if isinstance(x, np.integer): return ctx.make_mpf(from_int(int(x))) + if isinstance(x, np.floating): return ctx.make_mpf(from_npfloat(x)) + if isinstance(x, np.complexfloating): + return ctx.make_mpc((from_npfloat(x.real), from_npfloat(x.imag))) + raise TypeError("cannot create mpf from " + repr(x)) + + def isnan(ctx, x): + """ + Return *True* if *x* is a NaN (not-a-number), or for a complex + number, whether either the real or complex part is NaN; + otherwise return *False*:: + + >>> from mpmath import * + >>> isnan(3.14) + False + >>> isnan(nan) + True + >>> isnan(mpc(3.14,2.72)) + False + >>> isnan(mpc(3.14,nan)) + True + + """ + if hasattr(x, "_mpf_"): + return x._mpf_ == fnan + if hasattr(x, "_mpc_"): + return fnan in x._mpc_ + if isinstance(x, int_types) or isinstance(x, rational.mpq): + return False + x = ctx.convert(x) + if hasattr(x, '_mpf_') or hasattr(x, '_mpc_'): + return ctx.isnan(x) + raise TypeError("isnan() needs a number as input") + + def isinf(ctx, x): + """ + Return *True* if the absolute value of *x* is infinite; + otherwise return *False*:: + + >>> from mpmath import * + >>> isinf(inf) + True + >>> isinf(-inf) + True + >>> isinf(3) + False + >>> isinf(3+4j) + False + >>> isinf(mpc(3,inf)) + True + >>> isinf(mpc(inf,3)) + True + + """ + if hasattr(x, "_mpf_"): + return x._mpf_ in (finf, fninf) + if hasattr(x, "_mpc_"): + re, im = x._mpc_ + return re in (finf, fninf) or im in (finf, fninf) + if isinstance(x, int_types) or isinstance(x, rational.mpq): + return False + x = ctx.convert(x) + if hasattr(x, '_mpf_') or hasattr(x, '_mpc_'): + return ctx.isinf(x) + raise TypeError("isinf() needs a number as input") + + def isnormal(ctx, x): + """ + Determine whether *x* is "normal" in the sense of floating-point + representation; that is, return *False* if *x* is zero, an + infinity or NaN; otherwise return *True*. By extension, a + complex number *x* is considered "normal" if its magnitude is + normal:: + + >>> from mpmath import * + >>> isnormal(3) + True + >>> isnormal(0) + False + >>> isnormal(inf); isnormal(-inf); isnormal(nan) + False + False + False + >>> isnormal(0+0j) + False + >>> isnormal(0+3j) + True + >>> isnormal(mpc(2,nan)) + False + """ + if hasattr(x, "_mpf_"): + return bool(x._mpf_[1]) + if hasattr(x, "_mpc_"): + re, im = x._mpc_ + re_normal = bool(re[1]) + im_normal = bool(im[1]) + if re == fzero: return im_normal + if im == fzero: return re_normal + return re_normal and im_normal + if isinstance(x, int_types) or isinstance(x, rational.mpq): + return bool(x) + x = ctx.convert(x) + if hasattr(x, '_mpf_') or hasattr(x, '_mpc_'): + return ctx.isnormal(x) + raise TypeError("isnormal() needs a number as input") + + def isint(ctx, x, gaussian=False): + """ + Return *True* if *x* is integer-valued; otherwise return + *False*:: + + >>> from mpmath import * + >>> isint(3) + True + >>> isint(mpf(3)) + True + >>> isint(3.2) + False + >>> isint(inf) + False + + Optionally, Gaussian integers can be checked for:: + + >>> isint(3+0j) + True + >>> isint(3+2j) + False + >>> isint(3+2j, gaussian=True) + True + + """ + if isinstance(x, int_types): + return True + if hasattr(x, "_mpf_"): + sign, man, exp, bc = xval = x._mpf_ + return bool((man and exp >= 0) or xval == fzero) + if hasattr(x, "_mpc_"): + re, im = x._mpc_ + rsign, rman, rexp, rbc = re + isign, iman, iexp, ibc = im + re_isint = (rman and rexp >= 0) or re == fzero + if gaussian: + im_isint = (iman and iexp >= 0) or im == fzero + return re_isint and im_isint + return re_isint and im == fzero + if isinstance(x, rational.mpq): + p, q = x._mpq_ + return p % q == 0 + x = ctx.convert(x) + if hasattr(x, '_mpf_') or hasattr(x, '_mpc_'): + return ctx.isint(x, gaussian) + raise TypeError("isint() needs a number as input") + + def fsum(ctx, terms, absolute=False, squared=False): + """ + Calculates a sum containing a finite number of terms (for infinite + series, see :func:`~mpmath.nsum`). The terms will be converted to + mpmath numbers. For len(terms) > 2, this function is generally + faster and produces more accurate results than the builtin + Python function :func:`sum`. + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = False + >>> fsum([1, 2, 0.5, 7]) + mpf('10.5') + + With squared=True each term is squared, and with absolute=True + the absolute value of each term is used. + """ + prec, rnd = ctx._prec_rounding + real = [] + imag = [] + for term in terms: + reval = imval = 0 + if hasattr(term, "_mpf_"): + reval = term._mpf_ + elif hasattr(term, "_mpc_"): + reval, imval = term._mpc_ + else: + term = ctx.convert(term) + if hasattr(term, "_mpf_"): + reval = term._mpf_ + elif hasattr(term, "_mpc_"): + reval, imval = term._mpc_ + else: + raise NotImplementedError + if imval: + if squared: + if absolute: + real.append(mpf_mul(reval,reval)) + real.append(mpf_mul(imval,imval)) + else: + reval, imval = mpc_pow_int((reval,imval),2,prec+10) + real.append(reval) + imag.append(imval) + elif absolute: + real.append(mpc_abs((reval,imval), prec)) + else: + real.append(reval) + imag.append(imval) + else: + if squared: + reval = mpf_mul(reval, reval) + elif absolute: + reval = mpf_abs(reval) + real.append(reval) + s = mpf_sum(real, prec, rnd, absolute) + if imag: + s = ctx.make_mpc((s, mpf_sum(imag, prec, rnd))) + else: + s = ctx.make_mpf(s) + return s + + def fdot(ctx, A, B=None, conjugate=False): + r""" + Computes the dot product of the iterables `A` and `B`, + + .. math :: + + \sum_{k=0} A_k B_k. + + Alternatively, :func:`~mpmath.fdot` accepts a single iterable of pairs. + In other words, ``fdot(A,B)`` and ``fdot(zip(A,B))`` are equivalent. + The elements are automatically converted to mpmath numbers. + + With ``conjugate=True``, the elements in the second vector + will be conjugated: + + .. math :: + + \sum_{k=0} A_k \overline{B_k} + + **Examples** + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = False + >>> A = [2, 1.5, 3] + >>> B = [1, -1, 2] + >>> fdot(A, B) + mpf('6.5') + >>> list(zip(A, B)) + [(2, 1), (1.5, -1), (3, 2)] + >>> fdot(_) + mpf('6.5') + >>> A = [2, 1.5, 3j] + >>> B = [1+j, 3, -1-j] + >>> fdot(A, B) + mpc(real='9.5', imag='-1.0') + >>> fdot(A, B, conjugate=True) + mpc(real='3.5', imag='-5.0') + + """ + if B is not None: + A = zip(A, B) + prec, rnd = ctx._prec_rounding + real = [] + imag = [] + hasattr_ = hasattr + types = (ctx.mpf, ctx.mpc) + for a, b in A: + if type(a) not in types: a = ctx.convert(a) + if type(b) not in types: b = ctx.convert(b) + a_real = hasattr_(a, "_mpf_") + b_real = hasattr_(b, "_mpf_") + if a_real and b_real: + real.append(mpf_mul(a._mpf_, b._mpf_)) + continue + a_complex = hasattr_(a, "_mpc_") + b_complex = hasattr_(b, "_mpc_") + if a_real and b_complex: + aval = a._mpf_ + bre, bim = b._mpc_ + if conjugate: + bim = mpf_neg(bim) + real.append(mpf_mul(aval, bre)) + imag.append(mpf_mul(aval, bim)) + elif b_real and a_complex: + are, aim = a._mpc_ + bval = b._mpf_ + real.append(mpf_mul(are, bval)) + imag.append(mpf_mul(aim, bval)) + elif a_complex and b_complex: + #re, im = mpc_mul(a._mpc_, b._mpc_, prec+20) + are, aim = a._mpc_ + bre, bim = b._mpc_ + if conjugate: + bim = mpf_neg(bim) + real.append(mpf_mul(are, bre)) + real.append(mpf_neg(mpf_mul(aim, bim))) + imag.append(mpf_mul(are, bim)) + imag.append(mpf_mul(aim, bre)) + else: + raise NotImplementedError + s = mpf_sum(real, prec, rnd) + if imag: + s = ctx.make_mpc((s, mpf_sum(imag, prec, rnd))) + else: + s = ctx.make_mpf(s) + return s + + def _wrap_libmp_function(ctx, mpf_f, mpc_f=None, mpi_f=None, doc=""): + """ + Given a low-level mpf_ function, and optionally similar functions + for mpc_ and mpi_, defines the function as a context method. + + It is assumed that the return type is the same as that of + the input; the exception is that propagation from mpf to mpc is possible + by raising ComplexResult. + + """ + def f(x, **kwargs): + if type(x) not in ctx.types: + x = ctx.convert(x) + prec, rounding = ctx._prec_rounding + if kwargs: + prec = kwargs.get('prec', prec) + if 'dps' in kwargs: + prec = dps_to_prec(kwargs['dps']) + rounding = kwargs.get('rounding', rounding) + if hasattr(x, '_mpf_'): + try: + return ctx.make_mpf(mpf_f(x._mpf_, prec, rounding)) + except ComplexResult: + # Handle propagation to complex + if ctx.trap_complex: + raise + return ctx.make_mpc(mpc_f((x._mpf_, fzero), prec, rounding)) + elif hasattr(x, '_mpc_'): + return ctx.make_mpc(mpc_f(x._mpc_, prec, rounding)) + raise NotImplementedError("%s of a %s" % (name, type(x))) + name = mpf_f.__name__[4:] + f.__doc__ = function_docs.__dict__.get(name, "Computes the %s of x" % doc) + return f + + # Called by SpecialFunctions.__init__() + @classmethod + def _wrap_specfun(cls, name, f, wrap): + if wrap: + def f_wrapped(ctx, *args, **kwargs): + convert = ctx.convert + args = [convert(a) for a in args] + prec = ctx.prec + try: + ctx.prec += 10 + retval = f(ctx, *args, **kwargs) + finally: + ctx.prec = prec + return +retval + else: + f_wrapped = f + f_wrapped.__doc__ = function_docs.__dict__.get(name, f.__doc__) + setattr(cls, name, f_wrapped) + + def _convert_param(ctx, x): + if hasattr(x, "_mpc_"): + v, im = x._mpc_ + if im != fzero: + return x, 'C' + elif hasattr(x, "_mpf_"): + v = x._mpf_ + else: + if type(x) in int_types: + return int(x), 'Z' + p = None + if isinstance(x, tuple): + p, q = x + elif hasattr(x, '_mpq_'): + p, q = x._mpq_ + elif isinstance(x, basestring) and '/' in x: + p, q = x.split('/') + p = int(p) + q = int(q) + if p is not None: + if not p % q: + return p // q, 'Z' + return ctx.mpq(p,q), 'Q' + x = ctx.convert(x) + if hasattr(x, "_mpc_"): + v, im = x._mpc_ + if im != fzero: + return x, 'C' + elif hasattr(x, "_mpf_"): + v = x._mpf_ + else: + return x, 'U' + sign, man, exp, bc = v + if man: + if exp >= -4: + if sign: + man = -man + if exp >= 0: + return int(man) << exp, 'Z' + if exp >= -4: + p, q = int(man), (1<<(-exp)) + return ctx.mpq(p,q), 'Q' + x = ctx.make_mpf(v) + return x, 'R' + elif not exp: + return 0, 'Z' + else: + return x, 'U' + + def _mpf_mag(ctx, x): + sign, man, exp, bc = x + if man: + return exp+bc + if x == fzero: + return ctx.ninf + if x == finf or x == fninf: + return ctx.inf + return ctx.nan + + def mag(ctx, x): + """ + Quick logarithmic magnitude estimate of a number. Returns an + integer or infinity `m` such that `|x| <= 2^m`. It is not + guaranteed that `m` is an optimal bound, but it will never + be too large by more than 2 (and probably not more than 1). + + **Examples** + + >>> from mpmath import * + >>> mp.pretty = True + >>> mag(10), mag(10.0), mag(mpf(10)), int(ceil(log(10,2))) + (4, 4, 4, 4) + >>> mag(10j), mag(10+10j) + (4, 5) + >>> mag(0.01), int(ceil(log(0.01,2))) + (-6, -6) + >>> mag(0), mag(inf), mag(-inf), mag(nan) + (-inf, +inf, +inf, nan) + + """ + if hasattr(x, "_mpf_"): + return ctx._mpf_mag(x._mpf_) + elif hasattr(x, "_mpc_"): + r, i = x._mpc_ + if r == fzero: + return ctx._mpf_mag(i) + if i == fzero: + return ctx._mpf_mag(r) + return 1+max(ctx._mpf_mag(r), ctx._mpf_mag(i)) + elif isinstance(x, int_types): + if x: + return bitcount(abs(x)) + return ctx.ninf + elif isinstance(x, rational.mpq): + p, q = x._mpq_ + if p: + return 1 + bitcount(abs(p)) - bitcount(q) + return ctx.ninf + else: + x = ctx.convert(x) + if hasattr(x, "_mpf_") or hasattr(x, "_mpc_"): + return ctx.mag(x) + else: + raise TypeError("requires an mpf/mpc") + + +# Register with "numbers" ABC +# We do not subclass, hence we do not use the @abstractmethod checks. While +# this is less invasive it may turn out that we do not actually support +# parts of the expected interfaces. See +# http://docs.python.org/2/library/numbers.html for list of abstract +# methods. +try: + import numbers + numbers.Complex.register(_mpc) + numbers.Real.register(_mpf) +except ImportError: + pass diff --git a/lib/python3.12/site-packages/mpmath/function_docs.py b/lib/python3.12/site-packages/mpmath/function_docs.py new file mode 100644 index 0000000000000000000000000000000000000000..73c071dc30a25c0ea1366e06a407a20206bd18a2 --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/function_docs.py @@ -0,0 +1,10201 @@ +""" +Extended docstrings for functions.py +""" + + +pi = r""" +`\pi`, roughly equal to 3.141592654, represents the area of the unit +circle, the half-period of trigonometric functions, and many other +things in mathematics. + +Mpmath can evaluate `\pi` to arbitrary precision:: + + >>> from mpmath import * + >>> mp.dps = 50; mp.pretty = True + >>> +pi + 3.1415926535897932384626433832795028841971693993751 + +This shows digits 99991-100000 of `\pi` (the last digit is actually +a 4 when the decimal expansion is truncated, but here the nearest +rounding is used):: + + >>> mp.dps = 100000 + >>> str(pi)[-10:] + '5549362465' + +**Possible issues** + +:data:`pi` always rounds to the nearest floating-point +number when used. This means that exact mathematical identities +involving `\pi` will generally not be preserved in floating-point +arithmetic. In particular, multiples of :data:`pi` (except for +the trivial case ``0*pi``) are *not* the exact roots of +:func:`~mpmath.sin`, but differ roughly by the current epsilon:: + + >>> mp.dps = 15 + >>> sin(pi) + 1.22464679914735e-16 + +One solution is to use the :func:`~mpmath.sinpi` function instead:: + + >>> sinpi(1) + 0.0 + +See the documentation of trigonometric functions for additional +details. + +**References** + +* [BorweinBorwein]_ + +""" + +degree = r""" +Represents one degree of angle, `1^{\circ} = \pi/180`, or +about 0.01745329. This constant may be evaluated to arbitrary +precision:: + + >>> from mpmath import * + >>> mp.dps = 50; mp.pretty = True + >>> +degree + 0.017453292519943295769236907684886127134428718885417 + +The :data:`degree` object is convenient for conversion +to radians:: + + >>> sin(30 * degree) + 0.5 + >>> asin(0.5) / degree + 30.0 +""" + +e = r""" +The transcendental number `e` = 2.718281828... is the base of the +natural logarithm (:func:`~mpmath.ln`) and of the exponential function +(:func:`~mpmath.exp`). + +Mpmath can be evaluate `e` to arbitrary precision:: + + >>> from mpmath import * + >>> mp.dps = 50; mp.pretty = True + >>> +e + 2.7182818284590452353602874713526624977572470937 + +This shows digits 99991-100000 of `e` (the last digit is actually +a 5 when the decimal expansion is truncated, but here the nearest +rounding is used):: + + >>> mp.dps = 100000 + >>> str(e)[-10:] + '2100427166' + +**Possible issues** + +:data:`e` always rounds to the nearest floating-point number +when used, and mathematical identities involving `e` may not +hold in floating-point arithmetic. For example, ``ln(e)`` +might not evaluate exactly to 1. + +In particular, don't use ``e**x`` to compute the exponential +function. Use ``exp(x)`` instead; this is both faster and more +accurate. +""" + +phi = r""" +Represents the golden ratio `\phi = (1+\sqrt 5)/2`, +approximately equal to 1.6180339887. To high precision, +its value is:: + + >>> from mpmath import * + >>> mp.dps = 50; mp.pretty = True + >>> +phi + 1.6180339887498948482045868343656381177203091798058 + +Formulas for the golden ratio include the following:: + + >>> (1+sqrt(5))/2 + 1.6180339887498948482045868343656381177203091798058 + >>> findroot(lambda x: x**2-x-1, 1) + 1.6180339887498948482045868343656381177203091798058 + >>> limit(lambda n: fib(n+1)/fib(n), inf) + 1.6180339887498948482045868343656381177203091798058 +""" + +euler = r""" +Euler's constant or the Euler-Mascheroni constant `\gamma` += 0.57721566... is a number of central importance to +number theory and special functions. It is defined as the limit + +.. math :: + + \gamma = \lim_{n\to\infty} H_n - \log n + +where `H_n = 1 + \frac{1}{2} + \ldots + \frac{1}{n}` is a harmonic +number (see :func:`~mpmath.harmonic`). + +Evaluation of `\gamma` is supported at arbitrary precision:: + + >>> from mpmath import * + >>> mp.dps = 50; mp.pretty = True + >>> +euler + 0.57721566490153286060651209008240243104215933593992 + +We can also compute `\gamma` directly from the definition, +although this is less efficient:: + + >>> limit(lambda n: harmonic(n)-log(n), inf) + 0.57721566490153286060651209008240243104215933593992 + +This shows digits 9991-10000 of `\gamma` (the last digit is actually +a 5 when the decimal expansion is truncated, but here the nearest +rounding is used):: + + >>> mp.dps = 10000 + >>> str(euler)[-10:] + '4679858166' + +Integrals, series, and representations for `\gamma` in terms of +special functions include the following (there are many others):: + + >>> mp.dps = 25 + >>> -quad(lambda x: exp(-x)*log(x), [0,inf]) + 0.5772156649015328606065121 + >>> quad(lambda x,y: (x-1)/(1-x*y)/log(x*y), [0,1], [0,1]) + 0.5772156649015328606065121 + >>> nsum(lambda k: 1/k-log(1+1/k), [1,inf]) + 0.5772156649015328606065121 + >>> nsum(lambda k: (-1)**k*zeta(k)/k, [2,inf]) + 0.5772156649015328606065121 + >>> -diff(gamma, 1) + 0.5772156649015328606065121 + >>> limit(lambda x: 1/x-gamma(x), 0) + 0.5772156649015328606065121 + >>> limit(lambda x: zeta(x)-1/(x-1), 1) + 0.5772156649015328606065121 + >>> (log(2*pi*nprod(lambda n: + ... exp(-2+2/n)*(1+2/n)**n, [1,inf]))-3)/2 + 0.5772156649015328606065121 + +For generalizations of the identities `\gamma = -\Gamma'(1)` +and `\gamma = \lim_{x\to1} \zeta(x)-1/(x-1)`, see +:func:`~mpmath.psi` and :func:`~mpmath.stieltjes` respectively. + +**References** + +* [BorweinBailey]_ + +""" + +catalan = r""" +Catalan's constant `K` = 0.91596559... is given by the infinite +series + +.. math :: + + K = \sum_{k=0}^{\infty} \frac{(-1)^k}{(2k+1)^2}. + +Mpmath can evaluate it to arbitrary precision:: + + >>> from mpmath import * + >>> mp.dps = 50; mp.pretty = True + >>> +catalan + 0.91596559417721901505460351493238411077414937428167 + +One can also compute `K` directly from the definition, although +this is significantly less efficient:: + + >>> nsum(lambda k: (-1)**k/(2*k+1)**2, [0, inf]) + 0.91596559417721901505460351493238411077414937428167 + +This shows digits 9991-10000 of `K` (the last digit is actually +a 3 when the decimal expansion is truncated, but here the nearest +rounding is used):: + + >>> mp.dps = 10000 + >>> str(catalan)[-10:] + '9537871504' + +Catalan's constant has numerous integral representations:: + + >>> mp.dps = 50 + >>> quad(lambda x: -log(x)/(1+x**2), [0, 1]) + 0.91596559417721901505460351493238411077414937428167 + >>> quad(lambda x: atan(x)/x, [0, 1]) + 0.91596559417721901505460351493238411077414937428167 + >>> quad(lambda x: ellipk(x**2)/2, [0, 1]) + 0.91596559417721901505460351493238411077414937428167 + >>> quad(lambda x,y: 1/(1+(x*y)**2), [0, 1], [0, 1]) + 0.91596559417721901505460351493238411077414937428167 + +As well as series representations:: + + >>> pi*log(sqrt(3)+2)/8 + 3*nsum(lambda n: + ... (fac(n)/(2*n+1))**2/fac(2*n), [0, inf])/8 + 0.91596559417721901505460351493238411077414937428167 + >>> 1-nsum(lambda n: n*zeta(2*n+1)/16**n, [1,inf]) + 0.91596559417721901505460351493238411077414937428167 +""" + +khinchin = r""" +Khinchin's constant `K` = 2.68542... is a number that +appears in the theory of continued fractions. Mpmath can evaluate +it to arbitrary precision:: + + >>> from mpmath import * + >>> mp.dps = 50; mp.pretty = True + >>> +khinchin + 2.6854520010653064453097148354817956938203822939945 + +An integral representation is:: + + >>> I = quad(lambda x: log((1-x**2)/sincpi(x))/x/(1+x), [0, 1]) + >>> 2*exp(1/log(2)*I) + 2.6854520010653064453097148354817956938203822939945 + +The computation of ``khinchin`` is based on an efficient +implementation of the following series:: + + >>> f = lambda n: (zeta(2*n)-1)/n*sum((-1)**(k+1)/mpf(k) + ... for k in range(1,2*int(n))) + >>> exp(nsum(f, [1,inf])/log(2)) + 2.6854520010653064453097148354817956938203822939945 +""" + +glaisher = r""" +Glaisher's constant `A`, also known as the Glaisher-Kinkelin +constant, is a number approximately equal to 1.282427129 that +sometimes appears in formulas related to gamma and zeta functions. +It is also related to the Barnes G-function (see :func:`~mpmath.barnesg`). + +The constant is defined as `A = \exp(1/12-\zeta'(-1))` where +`\zeta'(s)` denotes the derivative of the Riemann zeta function +(see :func:`~mpmath.zeta`). + +Mpmath can evaluate Glaisher's constant to arbitrary precision: + + >>> from mpmath import * + >>> mp.dps = 50; mp.pretty = True + >>> +glaisher + 1.282427129100622636875342568869791727767688927325 + +We can verify that the value computed by :data:`glaisher` is +correct using mpmath's facilities for numerical +differentiation and arbitrary evaluation of the zeta function: + + >>> exp(mpf(1)/12 - diff(zeta, -1)) + 1.282427129100622636875342568869791727767688927325 + +Here is an example of an integral that can be evaluated in +terms of Glaisher's constant: + + >>> mp.dps = 15 + >>> quad(lambda x: log(gamma(x)), [1, 1.5]) + -0.0428537406502909 + >>> -0.5 - 7*log(2)/24 + log(pi)/4 + 3*log(glaisher)/2 + -0.042853740650291 + +Mpmath computes Glaisher's constant by applying Euler-Maclaurin +summation to a slowly convergent series. The implementation is +reasonably efficient up to about 10,000 digits. See the source +code for additional details. + +References: +http://mathworld.wolfram.com/Glaisher-KinkelinConstant.html +""" + +apery = r""" +Represents Apery's constant, which is the irrational number +approximately equal to 1.2020569 given by + +.. math :: + + \zeta(3) = \sum_{k=1}^\infty\frac{1}{k^3}. + +The calculation is based on an efficient hypergeometric +series. To 50 decimal places, the value is given by:: + + >>> from mpmath import * + >>> mp.dps = 50; mp.pretty = True + >>> +apery + 1.2020569031595942853997381615114499907649862923405 + +Other ways to evaluate Apery's constant using mpmath +include:: + + >>> zeta(3) + 1.2020569031595942853997381615114499907649862923405 + >>> -psi(2,1)/2 + 1.2020569031595942853997381615114499907649862923405 + >>> 8*nsum(lambda k: 1/(2*k+1)**3, [0,inf])/7 + 1.2020569031595942853997381615114499907649862923405 + >>> f = lambda k: 2/k**3/(exp(2*pi*k)-1) + >>> 7*pi**3/180 - nsum(f, [1,inf]) + 1.2020569031595942853997381615114499907649862923405 + +This shows digits 9991-10000 of Apery's constant:: + + >>> mp.dps = 10000 + >>> str(apery)[-10:] + '3189504235' +""" + +mertens = r""" +Represents the Mertens or Meissel-Mertens constant, which is the +prime number analog of Euler's constant: + +.. math :: + + B_1 = \lim_{N\to\infty} + \left(\sum_{p_k \le N} \frac{1}{p_k} - \log \log N \right) + +Here `p_k` denotes the `k`-th prime number. Other names for this +constant include the Hadamard-de la Vallee-Poussin constant or +the prime reciprocal constant. + +The following gives the Mertens constant to 50 digits:: + + >>> from mpmath import * + >>> mp.dps = 50; mp.pretty = True + >>> +mertens + 0.2614972128476427837554268386086958590515666482612 + +References: +http://mathworld.wolfram.com/MertensConstant.html +""" + +twinprime = r""" +Represents the twin prime constant, which is the factor `C_2` +featuring in the Hardy-Littlewood conjecture for the growth of the +twin prime counting function, + +.. math :: + + \pi_2(n) \sim 2 C_2 \frac{n}{\log^2 n}. + +It is given by the product over primes + +.. math :: + + C_2 = \prod_{p\ge3} \frac{p(p-2)}{(p-1)^2} \approx 0.66016 + +Computing `C_2` to 50 digits:: + + >>> from mpmath import * + >>> mp.dps = 50; mp.pretty = True + >>> +twinprime + 0.66016181584686957392781211001455577843262336028473 + +References: +http://mathworld.wolfram.com/TwinPrimesConstant.html +""" + +ln = r""" +Computes the natural logarithm of `x`, `\ln x`. +See :func:`~mpmath.log` for additional documentation.""" + +sqrt = r""" +``sqrt(x)`` gives the principal square root of `x`, `\sqrt x`. +For positive real numbers, the principal root is simply the +positive square root. For arbitrary complex numbers, the principal +square root is defined to satisfy `\sqrt x = \exp(\log(x)/2)`. +The function thus has a branch cut along the negative half real axis. + +For all mpmath numbers ``x``, calling ``sqrt(x)`` is equivalent to +performing ``x**0.5``. + +**Examples** + +Basic examples and limits:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> sqrt(10) + 3.16227766016838 + >>> sqrt(100) + 10.0 + >>> sqrt(-4) + (0.0 + 2.0j) + >>> sqrt(1+1j) + (1.09868411346781 + 0.455089860562227j) + >>> sqrt(inf) + +inf + +Square root evaluation is fast at huge precision:: + + >>> mp.dps = 50000 + >>> a = sqrt(3) + >>> str(a)[-10:] + '9329332815' + +:func:`mpmath.iv.sqrt` supports interval arguments:: + + >>> iv.dps = 15; iv.pretty = True + >>> iv.sqrt([16,100]) + [4.0, 10.0] + >>> iv.sqrt(2) + [1.4142135623730949234, 1.4142135623730951455] + >>> iv.sqrt(2) ** 2 + [1.9999999999999995559, 2.0000000000000004441] + +""" + +cbrt = r""" +``cbrt(x)`` computes the cube root of `x`, `x^{1/3}`. This +function is faster and more accurate than raising to a floating-point +fraction:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = False + >>> 125**(mpf(1)/3) + mpf('4.9999999999999991') + >>> cbrt(125) + mpf('5.0') + +Every nonzero complex number has three cube roots. This function +returns the cube root defined by `\exp(\log(x)/3)` where the +principal branch of the natural logarithm is used. Note that this +does not give a real cube root for negative real numbers:: + + >>> mp.pretty = True + >>> cbrt(-1) + (0.5 + 0.866025403784439j) +""" + +exp = r""" +Computes the exponential function, + +.. math :: + + \exp(x) = e^x = \sum_{k=0}^{\infty} \frac{x^k}{k!}. + +For complex numbers, the exponential function also satisfies + +.. math :: + + \exp(x+yi) = e^x (\cos y + i \sin y). + +**Basic examples** + +Some values of the exponential function:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> exp(0) + 1.0 + >>> exp(1) + 2.718281828459045235360287 + >>> exp(-1) + 0.3678794411714423215955238 + >>> exp(inf) + +inf + >>> exp(-inf) + 0.0 + +Arguments can be arbitrarily large:: + + >>> exp(10000) + 8.806818225662921587261496e+4342 + >>> exp(-10000) + 1.135483865314736098540939e-4343 + +Evaluation is supported for interval arguments via +:func:`mpmath.iv.exp`:: + + >>> iv.dps = 25; iv.pretty = True + >>> iv.exp([-inf,0]) + [0.0, 1.0] + >>> iv.exp([0,1]) + [1.0, 2.71828182845904523536028749558] + +The exponential function can be evaluated efficiently to arbitrary +precision:: + + >>> mp.dps = 10000 + >>> exp(pi) #doctest: +ELLIPSIS + 23.140692632779269005729...8984304016040616 + +**Functional properties** + +Numerical verification of Euler's identity for the complex +exponential function:: + + >>> mp.dps = 15 + >>> exp(j*pi)+1 + (0.0 + 1.22464679914735e-16j) + >>> chop(exp(j*pi)+1) + 0.0 + +This recovers the coefficients (reciprocal factorials) in the +Maclaurin series expansion of exp:: + + >>> nprint(taylor(exp, 0, 5)) + [1.0, 1.0, 0.5, 0.166667, 0.0416667, 0.00833333] + +The exponential function is its own derivative and antiderivative:: + + >>> exp(pi) + 23.1406926327793 + >>> diff(exp, pi) + 23.1406926327793 + >>> quad(exp, [-inf, pi]) + 23.1406926327793 + +The exponential function can be evaluated using various methods, +including direct summation of the series, limits, and solving +the defining differential equation:: + + >>> nsum(lambda k: pi**k/fac(k), [0,inf]) + 23.1406926327793 + >>> limit(lambda k: (1+pi/k)**k, inf) + 23.1406926327793 + >>> odefun(lambda t, x: x, 0, 1)(pi) + 23.1406926327793 +""" + +cosh = r""" +Computes the hyperbolic cosine of `x`, +`\cosh(x) = (e^x + e^{-x})/2`. Values and limits include:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> cosh(0) + 1.0 + >>> cosh(1) + 1.543080634815243778477906 + >>> cosh(-inf), cosh(+inf) + (+inf, +inf) + +The hyperbolic cosine is an even, convex function with +a global minimum at `x = 0`, having a Maclaurin series +that starts:: + + >>> nprint(chop(taylor(cosh, 0, 5))) + [1.0, 0.0, 0.5, 0.0, 0.0416667, 0.0] + +Generalized to complex numbers, the hyperbolic cosine is +equivalent to a cosine with the argument rotated +in the imaginary direction, or `\cosh x = \cos ix`:: + + >>> cosh(2+3j) + (-3.724545504915322565473971 + 0.5118225699873846088344638j) + >>> cos(3-2j) + (-3.724545504915322565473971 + 0.5118225699873846088344638j) +""" + +sinh = r""" +Computes the hyperbolic sine of `x`, +`\sinh(x) = (e^x - e^{-x})/2`. Values and limits include:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> sinh(0) + 0.0 + >>> sinh(1) + 1.175201193643801456882382 + >>> sinh(-inf), sinh(+inf) + (-inf, +inf) + +The hyperbolic sine is an odd function, with a Maclaurin +series that starts:: + + >>> nprint(chop(taylor(sinh, 0, 5))) + [0.0, 1.0, 0.0, 0.166667, 0.0, 0.00833333] + +Generalized to complex numbers, the hyperbolic sine is +essentially a sine with a rotation `i` applied to +the argument; more precisely, `\sinh x = -i \sin ix`:: + + >>> sinh(2+3j) + (-3.590564589985779952012565 + 0.5309210862485198052670401j) + >>> j*sin(3-2j) + (-3.590564589985779952012565 + 0.5309210862485198052670401j) +""" + +tanh = r""" +Computes the hyperbolic tangent of `x`, +`\tanh(x) = \sinh(x)/\cosh(x)`. Values and limits include:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> tanh(0) + 0.0 + >>> tanh(1) + 0.7615941559557648881194583 + >>> tanh(-inf), tanh(inf) + (-1.0, 1.0) + +The hyperbolic tangent is an odd, sigmoidal function, similar +to the inverse tangent and error function. Its Maclaurin +series is:: + + >>> nprint(chop(taylor(tanh, 0, 5))) + [0.0, 1.0, 0.0, -0.333333, 0.0, 0.133333] + +Generalized to complex numbers, the hyperbolic tangent is +essentially a tangent with a rotation `i` applied to +the argument; more precisely, `\tanh x = -i \tan ix`:: + + >>> tanh(2+3j) + (0.9653858790221331242784803 - 0.009884375038322493720314034j) + >>> j*tan(3-2j) + (0.9653858790221331242784803 - 0.009884375038322493720314034j) +""" + +cos = r""" +Computes the cosine of `x`, `\cos(x)`. + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> cos(pi/3) + 0.5 + >>> cos(100000001) + -0.9802850113244713353133243 + >>> cos(2+3j) + (-4.189625690968807230132555 - 9.109227893755336597979197j) + >>> cos(inf) + nan + >>> nprint(chop(taylor(cos, 0, 6))) + [1.0, 0.0, -0.5, 0.0, 0.0416667, 0.0, -0.00138889] + +Intervals are supported via :func:`mpmath.iv.cos`:: + + >>> iv.dps = 25; iv.pretty = True + >>> iv.cos([0,1]) + [0.540302305868139717400936602301, 1.0] + >>> iv.cos([0,2]) + [-0.41614683654714238699756823214, 1.0] +""" + +sin = r""" +Computes the sine of `x`, `\sin(x)`. + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> sin(pi/3) + 0.8660254037844386467637232 + >>> sin(100000001) + 0.1975887055794968911438743 + >>> sin(2+3j) + (9.1544991469114295734673 - 4.168906959966564350754813j) + >>> sin(inf) + nan + >>> nprint(chop(taylor(sin, 0, 6))) + [0.0, 1.0, 0.0, -0.166667, 0.0, 0.00833333, 0.0] + +Intervals are supported via :func:`mpmath.iv.sin`:: + + >>> iv.dps = 25; iv.pretty = True + >>> iv.sin([0,1]) + [0.0, 0.841470984807896506652502331201] + >>> iv.sin([0,2]) + [0.0, 1.0] +""" + +tan = r""" +Computes the tangent of `x`, `\tan(x) = \frac{\sin(x)}{\cos(x)}`. +The tangent function is singular at `x = (n+1/2)\pi`, but +``tan(x)`` always returns a finite result since `(n+1/2)\pi` +cannot be represented exactly using floating-point arithmetic. + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> tan(pi/3) + 1.732050807568877293527446 + >>> tan(100000001) + -0.2015625081449864533091058 + >>> tan(2+3j) + (-0.003764025641504248292751221 + 1.003238627353609801446359j) + >>> tan(inf) + nan + >>> nprint(chop(taylor(tan, 0, 6))) + [0.0, 1.0, 0.0, 0.333333, 0.0, 0.133333, 0.0] + +Intervals are supported via :func:`mpmath.iv.tan`:: + + >>> iv.dps = 25; iv.pretty = True + >>> iv.tan([0,1]) + [0.0, 1.55740772465490223050697482944] + >>> iv.tan([0,2]) # Interval includes a singularity + [-inf, +inf] +""" + +sec = r""" +Computes the secant of `x`, `\mathrm{sec}(x) = \frac{1}{\cos(x)}`. +The secant function is singular at `x = (n+1/2)\pi`, but +``sec(x)`` always returns a finite result since `(n+1/2)\pi` +cannot be represented exactly using floating-point arithmetic. + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> sec(pi/3) + 2.0 + >>> sec(10000001) + -1.184723164360392819100265 + >>> sec(2+3j) + (-0.04167496441114427004834991 + 0.0906111371962375965296612j) + >>> sec(inf) + nan + >>> nprint(chop(taylor(sec, 0, 6))) + [1.0, 0.0, 0.5, 0.0, 0.208333, 0.0, 0.0847222] + +Intervals are supported via :func:`mpmath.iv.sec`:: + + >>> iv.dps = 25; iv.pretty = True + >>> iv.sec([0,1]) + [1.0, 1.85081571768092561791175326276] + >>> iv.sec([0,2]) # Interval includes a singularity + [-inf, +inf] +""" + +csc = r""" +Computes the cosecant of `x`, `\mathrm{csc}(x) = \frac{1}{\sin(x)}`. +This cosecant function is singular at `x = n \pi`, but with the +exception of the point `x = 0`, ``csc(x)`` returns a finite result +since `n \pi` cannot be represented exactly using floating-point +arithmetic. + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> csc(pi/3) + 1.154700538379251529018298 + >>> csc(10000001) + -1.864910497503629858938891 + >>> csc(2+3j) + (0.09047320975320743980579048 + 0.04120098628857412646300981j) + >>> csc(inf) + nan + +Intervals are supported via :func:`mpmath.iv.csc`:: + + >>> iv.dps = 25; iv.pretty = True + >>> iv.csc([0,1]) # Interval includes a singularity + [1.18839510577812121626159943988, +inf] + >>> iv.csc([0,2]) + [1.0, +inf] +""" + +cot = r""" +Computes the cotangent of `x`, +`\mathrm{cot}(x) = \frac{1}{\tan(x)} = \frac{\cos(x)}{\sin(x)}`. +This cotangent function is singular at `x = n \pi`, but with the +exception of the point `x = 0`, ``cot(x)`` returns a finite result +since `n \pi` cannot be represented exactly using floating-point +arithmetic. + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> cot(pi/3) + 0.5773502691896257645091488 + >>> cot(10000001) + 1.574131876209625656003562 + >>> cot(2+3j) + (-0.003739710376336956660117409 - 0.9967577965693583104609688j) + >>> cot(inf) + nan + +Intervals are supported via :func:`mpmath.iv.cot`:: + + >>> iv.dps = 25; iv.pretty = True + >>> iv.cot([0,1]) # Interval includes a singularity + [0.642092615934330703006419974862, +inf] + >>> iv.cot([1,2]) + [-inf, +inf] +""" + +acos = r""" +Computes the inverse cosine or arccosine of `x`, `\cos^{-1}(x)`. +Since `-1 \le \cos(x) \le 1` for real `x`, the inverse +cosine is real-valued only for `-1 \le x \le 1`. On this interval, +:func:`~mpmath.acos` is defined to be a monotonically decreasing +function assuming values between `+\pi` and `0`. + +Basic values are:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> acos(-1) + 3.141592653589793238462643 + >>> acos(0) + 1.570796326794896619231322 + >>> acos(1) + 0.0 + >>> nprint(chop(taylor(acos, 0, 6))) + [1.5708, -1.0, 0.0, -0.166667, 0.0, -0.075, 0.0] + +:func:`~mpmath.acos` is defined so as to be a proper inverse function of +`\cos(\theta)` for `0 \le \theta < \pi`. +We have `\cos(\cos^{-1}(x)) = x` for all `x`, but +`\cos^{-1}(\cos(x)) = x` only for `0 \le \Re[x] < \pi`:: + + >>> for x in [1, 10, -1, 2+3j, 10+3j]: + ... print("%s %s" % (cos(acos(x)), acos(cos(x)))) + ... + 1.0 1.0 + (10.0 + 0.0j) 2.566370614359172953850574 + -1.0 1.0 + (2.0 + 3.0j) (2.0 + 3.0j) + (10.0 + 3.0j) (2.566370614359172953850574 - 3.0j) + +The inverse cosine has two branch points: `x = \pm 1`. :func:`~mpmath.acos` +places the branch cuts along the line segments `(-\infty, -1)` and +`(+1, +\infty)`. In general, + +.. math :: + + \cos^{-1}(x) = \frac{\pi}{2} + i \log\left(ix + \sqrt{1-x^2} \right) + +where the principal-branch log and square root are implied. +""" + +asin = r""" +Computes the inverse sine or arcsine of `x`, `\sin^{-1}(x)`. +Since `-1 \le \sin(x) \le 1` for real `x`, the inverse +sine is real-valued only for `-1 \le x \le 1`. +On this interval, it is defined to be a monotonically increasing +function assuming values between `-\pi/2` and `\pi/2`. + +Basic values are:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> asin(-1) + -1.570796326794896619231322 + >>> asin(0) + 0.0 + >>> asin(1) + 1.570796326794896619231322 + >>> nprint(chop(taylor(asin, 0, 6))) + [0.0, 1.0, 0.0, 0.166667, 0.0, 0.075, 0.0] + +:func:`~mpmath.asin` is defined so as to be a proper inverse function of +`\sin(\theta)` for `-\pi/2 < \theta < \pi/2`. +We have `\sin(\sin^{-1}(x)) = x` for all `x`, but +`\sin^{-1}(\sin(x)) = x` only for `-\pi/2 < \Re[x] < \pi/2`:: + + >>> for x in [1, 10, -1, 1+3j, -2+3j]: + ... print("%s %s" % (chop(sin(asin(x))), asin(sin(x)))) + ... + 1.0 1.0 + 10.0 -0.5752220392306202846120698 + -1.0 -1.0 + (1.0 + 3.0j) (1.0 + 3.0j) + (-2.0 + 3.0j) (-1.141592653589793238462643 - 3.0j) + +The inverse sine has two branch points: `x = \pm 1`. :func:`~mpmath.asin` +places the branch cuts along the line segments `(-\infty, -1)` and +`(+1, +\infty)`. In general, + +.. math :: + + \sin^{-1}(x) = -i \log\left(ix + \sqrt{1-x^2} \right) + +where the principal-branch log and square root are implied. +""" + +atan = r""" +Computes the inverse tangent or arctangent of `x`, `\tan^{-1}(x)`. +This is a real-valued function for all real `x`, with range +`(-\pi/2, \pi/2)`. + +Basic values are:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> atan(-inf) + -1.570796326794896619231322 + >>> atan(-1) + -0.7853981633974483096156609 + >>> atan(0) + 0.0 + >>> atan(1) + 0.7853981633974483096156609 + >>> atan(inf) + 1.570796326794896619231322 + >>> nprint(chop(taylor(atan, 0, 6))) + [0.0, 1.0, 0.0, -0.333333, 0.0, 0.2, 0.0] + +The inverse tangent is often used to compute angles. However, +the atan2 function is often better for this as it preserves sign +(see :func:`~mpmath.atan2`). + +:func:`~mpmath.atan` is defined so as to be a proper inverse function of +`\tan(\theta)` for `-\pi/2 < \theta < \pi/2`. +We have `\tan(\tan^{-1}(x)) = x` for all `x`, but +`\tan^{-1}(\tan(x)) = x` only for `-\pi/2 < \Re[x] < \pi/2`:: + + >>> mp.dps = 25 + >>> for x in [1, 10, -1, 1+3j, -2+3j]: + ... print("%s %s" % (tan(atan(x)), atan(tan(x)))) + ... + 1.0 1.0 + 10.0 0.5752220392306202846120698 + -1.0 -1.0 + (1.0 + 3.0j) (1.000000000000000000000001 + 3.0j) + (-2.0 + 3.0j) (1.141592653589793238462644 + 3.0j) + +The inverse tangent has two branch points: `x = \pm i`. :func:`~mpmath.atan` +places the branch cuts along the line segments `(-i \infty, -i)` and +`(+i, +i \infty)`. In general, + +.. math :: + + \tan^{-1}(x) = \frac{i}{2}\left(\log(1-ix)-\log(1+ix)\right) + +where the principal-branch log is implied. +""" + +acot = r"""Computes the inverse cotangent of `x`, +`\mathrm{cot}^{-1}(x) = \tan^{-1}(1/x)`.""" + +asec = r"""Computes the inverse secant of `x`, +`\mathrm{sec}^{-1}(x) = \cos^{-1}(1/x)`.""" + +acsc = r"""Computes the inverse cosecant of `x`, +`\mathrm{csc}^{-1}(x) = \sin^{-1}(1/x)`.""" + +coth = r"""Computes the hyperbolic cotangent of `x`, +`\mathrm{coth}(x) = \frac{\cosh(x)}{\sinh(x)}`. +""" + +sech = r"""Computes the hyperbolic secant of `x`, +`\mathrm{sech}(x) = \frac{1}{\cosh(x)}`. +""" + +csch = r"""Computes the hyperbolic cosecant of `x`, +`\mathrm{csch}(x) = \frac{1}{\sinh(x)}`. +""" + +acosh = r"""Computes the inverse hyperbolic cosine of `x`, +`\mathrm{cosh}^{-1}(x) = \log(x+\sqrt{x+1}\sqrt{x-1})`. +""" + +asinh = r"""Computes the inverse hyperbolic sine of `x`, +`\mathrm{sinh}^{-1}(x) = \log(x+\sqrt{1+x^2})`. +""" + +atanh = r"""Computes the inverse hyperbolic tangent of `x`, +`\mathrm{tanh}^{-1}(x) = \frac{1}{2}\left(\log(1+x)-\log(1-x)\right)`. +""" + +acoth = r"""Computes the inverse hyperbolic cotangent of `x`, +`\mathrm{coth}^{-1}(x) = \tanh^{-1}(1/x)`.""" + +asech = r"""Computes the inverse hyperbolic secant of `x`, +`\mathrm{sech}^{-1}(x) = \cosh^{-1}(1/x)`.""" + +acsch = r"""Computes the inverse hyperbolic cosecant of `x`, +`\mathrm{csch}^{-1}(x) = \sinh^{-1}(1/x)`.""" + + + +sinpi = r""" +Computes `\sin(\pi x)`, more accurately than the expression +``sin(pi*x)``:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> sinpi(10**10), sin(pi*(10**10)) + (0.0, -2.23936276195592e-6) + >>> sinpi(10**10+0.5), sin(pi*(10**10+0.5)) + (1.0, 0.999999999998721) +""" + +cospi = r""" +Computes `\cos(\pi x)`, more accurately than the expression +``cos(pi*x)``:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> cospi(10**10), cos(pi*(10**10)) + (1.0, 0.999999999997493) + >>> cospi(10**10+0.5), cos(pi*(10**10+0.5)) + (0.0, 1.59960492420134e-6) +""" + +sinc = r""" +``sinc(x)`` computes the unnormalized sinc function, defined as + +.. math :: + + \mathrm{sinc}(x) = \begin{cases} + \sin(x)/x, & \mbox{if } x \ne 0 \\ + 1, & \mbox{if } x = 0. + \end{cases} + +See :func:`~mpmath.sincpi` for the normalized sinc function. + +Simple values and limits include:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> sinc(0) + 1.0 + >>> sinc(1) + 0.841470984807897 + >>> sinc(inf) + 0.0 + +The integral of the sinc function is the sine integral Si:: + + >>> quad(sinc, [0, 1]) + 0.946083070367183 + >>> si(1) + 0.946083070367183 +""" + +sincpi = r""" +``sincpi(x)`` computes the normalized sinc function, defined as + +.. math :: + + \mathrm{sinc}_{\pi}(x) = \begin{cases} + \sin(\pi x)/(\pi x), & \mbox{if } x \ne 0 \\ + 1, & \mbox{if } x = 0. + \end{cases} + +Equivalently, we have +`\mathrm{sinc}_{\pi}(x) = \mathrm{sinc}(\pi x)`. + +The normalization entails that the function integrates +to unity over the entire real line:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> quadosc(sincpi, [-inf, inf], period=2.0) + 1.0 + +Like, :func:`~mpmath.sinpi`, :func:`~mpmath.sincpi` is evaluated accurately +at its roots:: + + >>> sincpi(10) + 0.0 +""" + +expj = r""" +Convenience function for computing `e^{ix}`:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> expj(0) + (1.0 + 0.0j) + >>> expj(-1) + (0.5403023058681397174009366 - 0.8414709848078965066525023j) + >>> expj(j) + (0.3678794411714423215955238 + 0.0j) + >>> expj(1+j) + (0.1987661103464129406288032 + 0.3095598756531121984439128j) +""" + +expjpi = r""" +Convenience function for computing `e^{i \pi x}`. +Evaluation is accurate near zeros (see also :func:`~mpmath.cospi`, +:func:`~mpmath.sinpi`):: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> expjpi(0) + (1.0 + 0.0j) + >>> expjpi(1) + (-1.0 + 0.0j) + >>> expjpi(0.5) + (0.0 + 1.0j) + >>> expjpi(-1) + (-1.0 + 0.0j) + >>> expjpi(j) + (0.04321391826377224977441774 + 0.0j) + >>> expjpi(1+j) + (-0.04321391826377224977441774 + 0.0j) +""" + +floor = r""" +Computes the floor of `x`, `\lfloor x \rfloor`, defined as +the largest integer less than or equal to `x`:: + + >>> from mpmath import * + >>> mp.pretty = False + >>> floor(3.5) + mpf('3.0') + +.. note :: + + :func:`~mpmath.floor`, :func:`~mpmath.ceil` and :func:`~mpmath.nint` return a + floating-point number, not a Python ``int``. If `\lfloor x \rfloor` is + too large to be represented exactly at the present working precision, + the result will be rounded, not necessarily in the direction + implied by the mathematical definition of the function. + +To avoid rounding, use *prec=0*:: + + >>> mp.dps = 15 + >>> print(int(floor(10**30+1))) + 1000000000000000019884624838656 + >>> print(int(floor(10**30+1, prec=0))) + 1000000000000000000000000000001 + +The floor function is defined for complex numbers and +acts on the real and imaginary parts separately:: + + >>> floor(3.25+4.75j) + mpc(real='3.0', imag='4.0') +""" + +ceil = r""" +Computes the ceiling of `x`, `\lceil x \rceil`, defined as +the smallest integer greater than or equal to `x`:: + + >>> from mpmath import * + >>> mp.pretty = False + >>> ceil(3.5) + mpf('4.0') + +The ceiling function is defined for complex numbers and +acts on the real and imaginary parts separately:: + + >>> ceil(3.25+4.75j) + mpc(real='4.0', imag='5.0') + +See notes about rounding for :func:`~mpmath.floor`. +""" + +nint = r""" +Evaluates the nearest integer function, `\mathrm{nint}(x)`. +This gives the nearest integer to `x`; on a tie, it +gives the nearest even integer:: + + >>> from mpmath import * + >>> mp.pretty = False + >>> nint(3.2) + mpf('3.0') + >>> nint(3.8) + mpf('4.0') + >>> nint(3.5) + mpf('4.0') + >>> nint(4.5) + mpf('4.0') + +The nearest integer function is defined for complex numbers and +acts on the real and imaginary parts separately:: + + >>> nint(3.25+4.75j) + mpc(real='3.0', imag='5.0') + +See notes about rounding for :func:`~mpmath.floor`. +""" + +frac = r""" +Gives the fractional part of `x`, defined as +`\mathrm{frac}(x) = x - \lfloor x \rfloor` (see :func:`~mpmath.floor`). +In effect, this computes `x` modulo 1, or `x+n` where +`n \in \mathbb{Z}` is such that `x+n \in [0,1)`:: + + >>> from mpmath import * + >>> mp.pretty = False + >>> frac(1.25) + mpf('0.25') + >>> frac(3) + mpf('0.0') + >>> frac(-1.25) + mpf('0.75') + +For a complex number, the fractional part function applies to +the real and imaginary parts separately:: + + >>> frac(2.25+3.75j) + mpc(real='0.25', imag='0.75') + +Plotted, the fractional part function gives a sawtooth +wave. The Fourier series coefficients have a simple +form:: + + >>> mp.dps = 15 + >>> nprint(fourier(lambda x: frac(x)-0.5, [0,1], 4)) + ([0.0, 0.0, 0.0, 0.0, 0.0], [0.0, -0.31831, -0.159155, -0.106103, -0.0795775]) + >>> nprint([-1/(pi*k) for k in range(1,5)]) + [-0.31831, -0.159155, -0.106103, -0.0795775] + +.. note:: + + The fractional part is sometimes defined as a symmetric + function, i.e. returning `-\mathrm{frac}(-x)` if `x < 0`. + This convention is used, for instance, by Mathematica's + ``FractionalPart``. + +""" + +sign = r""" +Returns the sign of `x`, defined as `\mathrm{sign}(x) = x / |x|` +(with the special case `\mathrm{sign}(0) = 0`):: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = False + >>> sign(10) + mpf('1.0') + >>> sign(-10) + mpf('-1.0') + >>> sign(0) + mpf('0.0') + +Note that the sign function is also defined for complex numbers, +for which it gives the projection onto the unit circle:: + + >>> mp.dps = 15; mp.pretty = True + >>> sign(1+j) + (0.707106781186547 + 0.707106781186547j) + +""" + +arg = r""" +Computes the complex argument (phase) of `x`, defined as the +signed angle between the positive real axis and `x` in the +complex plane:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> arg(3) + 0.0 + >>> arg(3+3j) + 0.785398163397448 + >>> arg(3j) + 1.5707963267949 + >>> arg(-3) + 3.14159265358979 + >>> arg(-3j) + -1.5707963267949 + +The angle is defined to satisfy `-\pi < \arg(x) \le \pi` and +with the sign convention that a nonnegative imaginary part +results in a nonnegative argument. + +The value returned by :func:`~mpmath.arg` is an ``mpf`` instance. +""" + +fabs = r""" +Returns the absolute value of `x`, `|x|`. Unlike :func:`abs`, +:func:`~mpmath.fabs` converts non-mpmath numbers (such as ``int``) +into mpmath numbers:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = False + >>> fabs(3) + mpf('3.0') + >>> fabs(-3) + mpf('3.0') + >>> fabs(3+4j) + mpf('5.0') +""" + +re = r""" +Returns the real part of `x`, `\Re(x)`. :func:`~mpmath.re` +converts a non-mpmath number to an mpmath number:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = False + >>> re(3) + mpf('3.0') + >>> re(-1+4j) + mpf('-1.0') +""" + +im = r""" +Returns the imaginary part of `x`, `\Im(x)`. :func:`~mpmath.im` +converts a non-mpmath number to an mpmath number:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = False + >>> im(3) + mpf('0.0') + >>> im(-1+4j) + mpf('4.0') +""" + +conj = r""" +Returns the complex conjugate of `x`, `\overline{x}`. Unlike +``x.conjugate()``, :func:`~mpmath.im` converts `x` to a mpmath number:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = False + >>> conj(3) + mpf('3.0') + >>> conj(-1+4j) + mpc(real='-1.0', imag='-4.0') +""" + +polar = r""" +Returns the polar representation of the complex number `z` +as a pair `(r, \phi)` such that `z = r e^{i \phi}`:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> polar(-2) + (2.0, 3.14159265358979) + >>> polar(3-4j) + (5.0, -0.927295218001612) +""" + +rect = r""" +Returns the complex number represented by polar +coordinates `(r, \phi)`:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> chop(rect(2, pi)) + -2.0 + >>> rect(sqrt(2), -pi/4) + (1.0 - 1.0j) +""" + +expm1 = r""" +Computes `e^x - 1`, accurately for small `x`. + +Unlike the expression ``exp(x) - 1``, ``expm1(x)`` does not suffer from +potentially catastrophic cancellation:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> exp(1e-10)-1; print(expm1(1e-10)) + 1.00000008274037e-10 + 1.00000000005e-10 + >>> exp(1e-20)-1; print(expm1(1e-20)) + 0.0 + 1.0e-20 + >>> 1/(exp(1e-20)-1) + Traceback (most recent call last): + ... + ZeroDivisionError + >>> 1/expm1(1e-20) + 1.0e+20 + +Evaluation works for extremely tiny values:: + + >>> expm1(0) + 0.0 + >>> expm1('1e-10000000') + 1.0e-10000000 + +""" + +log1p = r""" +Computes `\log(1+x)`, accurately for small `x`. + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> log(1+1e-10); print(mp.log1p(1e-10)) + 1.00000008269037e-10 + 9.9999999995e-11 + >>> mp.log1p(1e-100j) + (5.0e-201 + 1.0e-100j) + >>> mp.log1p(0) + 0.0 + +""" + + +powm1 = r""" +Computes `x^y - 1`, accurately when `x^y` is very close to 1. + +This avoids potentially catastrophic cancellation:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> power(0.99999995, 1e-10) - 1 + 0.0 + >>> powm1(0.99999995, 1e-10) + -5.00000012791934e-18 + +Powers exactly equal to 1, and only those powers, yield 0 exactly:: + + >>> powm1(-j, 4) + (0.0 + 0.0j) + >>> powm1(3, 0) + 0.0 + >>> powm1(fadd(-1, 1e-100, exact=True), 4) + -4.0e-100 + +Evaluation works for extremely tiny `y`:: + + >>> powm1(2, '1e-100000') + 6.93147180559945e-100001 + >>> powm1(j, '1e-1000') + (-1.23370055013617e-2000 + 1.5707963267949e-1000j) + +""" + +root = r""" +``root(z, n, k=0)`` computes an `n`-th root of `z`, i.e. returns a number +`r` that (up to possible approximation error) satisfies `r^n = z`. +(``nthroot`` is available as an alias for ``root``.) + +Every complex number `z \ne 0` has `n` distinct `n`-th roots, which are +equidistant points on a circle with radius `|z|^{1/n}`, centered around the +origin. A specific root may be selected using the optional index +`k`. The roots are indexed counterclockwise, starting with `k = 0` for the root +closest to the positive real half-axis. + +The `k = 0` root is the so-called principal `n`-th root, often denoted by +`\sqrt[n]{z}` or `z^{1/n}`, and also given by `\exp(\log(z) / n)`. If `z` is +a positive real number, the principal root is just the unique positive +`n`-th root of `z`. Under some circumstances, non-principal real roots exist: +for positive real `z`, `n` even, there is a negative root given by `k = n/2`; +for negative real `z`, `n` odd, there is a negative root given by `k = (n-1)/2`. + +To obtain all roots with a simple expression, use +``[root(z,n,k) for k in range(n)]``. + +An important special case, ``root(1, n, k)`` returns the `k`-th `n`-th root of +unity, `\zeta_k = e^{2 \pi i k / n}`. Alternatively, :func:`~mpmath.unitroots` +provides a slightly more convenient way to obtain the roots of unity, +including the option to compute only the primitive roots of unity. + +Both `k` and `n` should be integers; `k` outside of ``range(n)`` will be +reduced modulo `n`. If `n` is negative, `x^{-1/n} = 1/x^{1/n}` (or +the equivalent reciprocal for a non-principal root with `k \ne 0`) is computed. + +:func:`~mpmath.root` is implemented to use Newton's method for small +`n`. At high precision, this makes `x^{1/n}` not much more +expensive than the regular exponentiation, `x^n`. For very large +`n`, :func:`~mpmath.nthroot` falls back to use the exponential function. + +**Examples** + +:func:`~mpmath.nthroot`/:func:`~mpmath.root` is faster and more accurate than raising to a +floating-point fraction:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = False + >>> 16807 ** (mpf(1)/5) + mpf('7.0000000000000009') + >>> root(16807, 5) + mpf('7.0') + >>> nthroot(16807, 5) # Alias + mpf('7.0') + +A high-precision root:: + + >>> mp.dps = 50; mp.pretty = True + >>> nthroot(10, 5) + 1.584893192461113485202101373391507013269442133825 + >>> nthroot(10, 5) ** 5 + 10.0 + +Computing principal and non-principal square and cube roots:: + + >>> mp.dps = 15 + >>> root(10, 2) + 3.16227766016838 + >>> root(10, 2, 1) + -3.16227766016838 + >>> root(-10, 3) + (1.07721734501594 + 1.86579517236206j) + >>> root(-10, 3, 1) + -2.15443469003188 + >>> root(-10, 3, 2) + (1.07721734501594 - 1.86579517236206j) + +All the 7th roots of a complex number:: + + >>> for r in [root(3+4j, 7, k) for k in range(7)]: + ... print("%s %s" % (r, r**7)) + ... + (1.24747270589553 + 0.166227124177353j) (3.0 + 4.0j) + (0.647824911301003 + 1.07895435170559j) (3.0 + 4.0j) + (-0.439648254723098 + 1.17920694574172j) (3.0 + 4.0j) + (-1.19605731775069 + 0.391492658196305j) (3.0 + 4.0j) + (-1.05181082538903 - 0.691023585965793j) (3.0 + 4.0j) + (-0.115529328478668 - 1.25318497558335j) (3.0 + 4.0j) + (0.907748109144957 - 0.871672518271819j) (3.0 + 4.0j) + +Cube roots of unity:: + + >>> for k in range(3): print(root(1, 3, k)) + ... + 1.0 + (-0.5 + 0.866025403784439j) + (-0.5 - 0.866025403784439j) + +Some exact high order roots:: + + >>> root(75**210, 105) + 5625.0 + >>> root(1, 128, 96) + (0.0 - 1.0j) + >>> root(4**128, 128, 96) + (0.0 - 4.0j) + +""" + +unitroots = r""" +``unitroots(n)`` returns `\zeta_0, \zeta_1, \ldots, \zeta_{n-1}`, +all the distinct `n`-th roots of unity, as a list. If the option +*primitive=True* is passed, only the primitive roots are returned. + +Every `n`-th root of unity satisfies `(\zeta_k)^n = 1`. There are `n` distinct +roots for each `n` (`\zeta_k` and `\zeta_j` are the same when +`k = j \pmod n`), which form a regular polygon with vertices on the unit +circle. They are ordered counterclockwise with increasing `k`, starting +with `\zeta_0 = 1`. + +**Examples** + +The roots of unity up to `n = 4`:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> nprint(unitroots(1)) + [1.0] + >>> nprint(unitroots(2)) + [1.0, -1.0] + >>> nprint(unitroots(3)) + [1.0, (-0.5 + 0.866025j), (-0.5 - 0.866025j)] + >>> nprint(unitroots(4)) + [1.0, (0.0 + 1.0j), -1.0, (0.0 - 1.0j)] + +Roots of unity form a geometric series that sums to 0:: + + >>> mp.dps = 50 + >>> chop(fsum(unitroots(25))) + 0.0 + +Primitive roots up to `n = 4`:: + + >>> mp.dps = 15 + >>> nprint(unitroots(1, primitive=True)) + [1.0] + >>> nprint(unitroots(2, primitive=True)) + [-1.0] + >>> nprint(unitroots(3, primitive=True)) + [(-0.5 + 0.866025j), (-0.5 - 0.866025j)] + >>> nprint(unitroots(4, primitive=True)) + [(0.0 + 1.0j), (0.0 - 1.0j)] + +There are only four primitive 12th roots:: + + >>> nprint(unitroots(12, primitive=True)) + [(0.866025 + 0.5j), (-0.866025 + 0.5j), (-0.866025 - 0.5j), (0.866025 - 0.5j)] + +The `n`-th roots of unity form a group, the cyclic group of order `n`. +Any primitive root `r` is a generator for this group, meaning that +`r^0, r^1, \ldots, r^{n-1}` gives the whole set of unit roots (in +some permuted order):: + + >>> for r in unitroots(6): print(r) + ... + 1.0 + (0.5 + 0.866025403784439j) + (-0.5 + 0.866025403784439j) + -1.0 + (-0.5 - 0.866025403784439j) + (0.5 - 0.866025403784439j) + >>> r = unitroots(6, primitive=True)[1] + >>> for k in range(6): print(chop(r**k)) + ... + 1.0 + (0.5 - 0.866025403784439j) + (-0.5 - 0.866025403784439j) + -1.0 + (-0.5 + 0.866025403784438j) + (0.5 + 0.866025403784438j) + +The number of primitive roots equals the Euler totient function `\phi(n)`:: + + >>> [len(unitroots(n, primitive=True)) for n in range(1,20)] + [1, 1, 2, 2, 4, 2, 6, 4, 6, 4, 10, 4, 12, 6, 8, 8, 16, 6, 18] + +""" + + +log = r""" +Computes the base-`b` logarithm of `x`, `\log_b(x)`. If `b` is +unspecified, :func:`~mpmath.log` computes the natural (base `e`) logarithm +and is equivalent to :func:`~mpmath.ln`. In general, the base `b` logarithm +is defined in terms of the natural logarithm as +`\log_b(x) = \ln(x)/\ln(b)`. + +By convention, we take `\log(0) = -\infty`. + +The natural logarithm is real if `x > 0` and complex if `x < 0` or if +`x` is complex. The principal branch of the complex logarithm is +used, meaning that `\Im(\ln(x)) = -\pi < \arg(x) \le \pi`. + +**Examples** + +Some basic values and limits:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> log(1) + 0.0 + >>> log(2) + 0.693147180559945 + >>> log(1000,10) + 3.0 + >>> log(4, 16) + 0.5 + >>> log(j) + (0.0 + 1.5707963267949j) + >>> log(-1) + (0.0 + 3.14159265358979j) + >>> log(0) + -inf + >>> log(inf) + +inf + +The natural logarithm is the antiderivative of `1/x`:: + + >>> quad(lambda x: 1/x, [1, 5]) + 1.6094379124341 + >>> log(5) + 1.6094379124341 + >>> diff(log, 10) + 0.1 + +The Taylor series expansion of the natural logarithm around +`x = 1` has coefficients `(-1)^{n+1}/n`:: + + >>> nprint(taylor(log, 1, 7)) + [0.0, 1.0, -0.5, 0.333333, -0.25, 0.2, -0.166667, 0.142857] + +:func:`~mpmath.log` supports arbitrary precision evaluation:: + + >>> mp.dps = 50 + >>> log(pi) + 1.1447298858494001741434273513530587116472948129153 + >>> log(pi, pi**3) + 0.33333333333333333333333333333333333333333333333333 + >>> mp.dps = 25 + >>> log(3+4j) + (1.609437912434100374600759 + 0.9272952180016122324285125j) +""" + +log10 = r""" +Computes the base-10 logarithm of `x`, `\log_{10}(x)`. ``log10(x)`` +is equivalent to ``log(x, 10)``. +""" + +fmod = r""" +Converts `x` and `y` to mpmath numbers and returns `x \mod y`. +For mpmath numbers, this is equivalent to ``x % y``. + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> fmod(100, pi) + 2.61062773871641 + +You can use :func:`~mpmath.fmod` to compute fractional parts of numbers:: + + >>> fmod(10.25, 1) + 0.25 + +""" + +radians = r""" +Converts the degree angle `x` to radians:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> radians(60) + 1.0471975511966 +""" + +degrees = r""" +Converts the radian angle `x` to a degree angle:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> degrees(pi/3) + 60.0 +""" + +atan2 = r""" +Computes the two-argument arctangent, `\mathrm{atan2}(y, x)`, +giving the signed angle between the positive `x`-axis and the +point `(x, y)` in the 2D plane. This function is defined for +real `x` and `y` only. + +The two-argument arctangent essentially computes +`\mathrm{atan}(y/x)`, but accounts for the signs of both +`x` and `y` to give the angle for the correct quadrant. The +following examples illustrate the difference:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> atan2(1,1), atan(1/1.) + (0.785398163397448, 0.785398163397448) + >>> atan2(1,-1), atan(1/-1.) + (2.35619449019234, -0.785398163397448) + >>> atan2(-1,1), atan(-1/1.) + (-0.785398163397448, -0.785398163397448) + >>> atan2(-1,-1), atan(-1/-1.) + (-2.35619449019234, 0.785398163397448) + +The angle convention is the same as that used for the complex +argument; see :func:`~mpmath.arg`. +""" + +fibonacci = r""" +``fibonacci(n)`` computes the `n`-th Fibonacci number, `F(n)`. The +Fibonacci numbers are defined by the recurrence `F(n) = F(n-1) + F(n-2)` +with the initial values `F(0) = 0`, `F(1) = 1`. :func:`~mpmath.fibonacci` +extends this definition to arbitrary real and complex arguments +using the formula + +.. math :: + + F(z) = \frac{\phi^z - \cos(\pi z) \phi^{-z}}{\sqrt 5} + +where `\phi` is the golden ratio. :func:`~mpmath.fibonacci` also uses this +continuous formula to compute `F(n)` for extremely large `n`, where +calculating the exact integer would be wasteful. + +For convenience, :func:`~mpmath.fib` is available as an alias for +:func:`~mpmath.fibonacci`. + +**Basic examples** + +Some small Fibonacci numbers are:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> for i in range(10): + ... print(fibonacci(i)) + ... + 0.0 + 1.0 + 1.0 + 2.0 + 3.0 + 5.0 + 8.0 + 13.0 + 21.0 + 34.0 + >>> fibonacci(50) + 12586269025.0 + +The recurrence for `F(n)` extends backwards to negative `n`:: + + >>> for i in range(10): + ... print(fibonacci(-i)) + ... + 0.0 + 1.0 + -1.0 + 2.0 + -3.0 + 5.0 + -8.0 + 13.0 + -21.0 + 34.0 + +Large Fibonacci numbers will be computed approximately unless +the precision is set high enough:: + + >>> fib(200) + 2.8057117299251e+41 + >>> mp.dps = 45 + >>> fib(200) + 280571172992510140037611932413038677189525.0 + +:func:`~mpmath.fibonacci` can compute approximate Fibonacci numbers +of stupendous size:: + + >>> mp.dps = 15 + >>> fibonacci(10**25) + 3.49052338550226e+2089876402499787337692720 + +**Real and complex arguments** + +The extended Fibonacci function is an analytic function. The +property `F(z) = F(z-1) + F(z-2)` holds for arbitrary `z`:: + + >>> mp.dps = 15 + >>> fib(pi) + 2.1170270579161 + >>> fib(pi-1) + fib(pi-2) + 2.1170270579161 + >>> fib(3+4j) + (-5248.51130728372 - 14195.962288353j) + >>> fib(2+4j) + fib(1+4j) + (-5248.51130728372 - 14195.962288353j) + +The Fibonacci function has infinitely many roots on the +negative half-real axis. The first root is at 0, the second is +close to -0.18, and then there are infinitely many roots that +asymptotically approach `-n+1/2`:: + + >>> findroot(fib, -0.2) + -0.183802359692956 + >>> findroot(fib, -2) + -1.57077646820395 + >>> findroot(fib, -17) + -16.4999999596115 + >>> findroot(fib, -24) + -23.5000000000479 + +**Mathematical relationships** + +For large `n`, `F(n+1)/F(n)` approaches the golden ratio:: + + >>> mp.dps = 50 + >>> fibonacci(101)/fibonacci(100) + 1.6180339887498948482045868343656381177203127439638 + >>> +phi + 1.6180339887498948482045868343656381177203091798058 + +The sum of reciprocal Fibonacci numbers converges to an irrational +number for which no closed form expression is known:: + + >>> mp.dps = 15 + >>> nsum(lambda n: 1/fib(n), [1, inf]) + 3.35988566624318 + +Amazingly, however, the sum of odd-index reciprocal Fibonacci +numbers can be expressed in terms of a Jacobi theta function:: + + >>> nsum(lambda n: 1/fib(2*n+1), [0, inf]) + 1.82451515740692 + >>> sqrt(5)*jtheta(2,0,(3-sqrt(5))/2)**2/4 + 1.82451515740692 + +Some related sums can be done in closed form:: + + >>> nsum(lambda k: 1/(1+fib(2*k+1)), [0, inf]) + 1.11803398874989 + >>> phi - 0.5 + 1.11803398874989 + >>> f = lambda k:(-1)**(k+1) / sum(fib(n)**2 for n in range(1,int(k+1))) + >>> nsum(f, [1, inf]) + 0.618033988749895 + >>> phi-1 + 0.618033988749895 + +**References** + +1. http://mathworld.wolfram.com/FibonacciNumber.html +""" + +altzeta = r""" +Gives the Dirichlet eta function, `\eta(s)`, also known as the +alternating zeta function. This function is defined in analogy +with the Riemann zeta function as providing the sum of the +alternating series + +.. math :: + + \eta(s) = \sum_{k=0}^{\infty} \frac{(-1)^k}{k^s} + = 1-\frac{1}{2^s}+\frac{1}{3^s}-\frac{1}{4^s}+\ldots + +The eta function, unlike the Riemann zeta function, is an entire +function, having a finite value for all complex `s`. The special case +`\eta(1) = \log(2)` gives the value of the alternating harmonic series. + +The alternating zeta function may expressed using the Riemann zeta function +as `\eta(s) = (1 - 2^{1-s}) \zeta(s)`. It can also be expressed +in terms of the Hurwitz zeta function, for example using +:func:`~mpmath.dirichlet` (see documentation for that function). + +**Examples** + +Some special values are:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> altzeta(1) + 0.693147180559945 + >>> altzeta(0) + 0.5 + >>> altzeta(-1) + 0.25 + >>> altzeta(-2) + 0.0 + +An example of a sum that can be computed more accurately and +efficiently via :func:`~mpmath.altzeta` than via numerical summation:: + + >>> sum(-(-1)**n / mpf(n)**2.5 for n in range(1, 100)) + 0.867204951503984 + >>> altzeta(2.5) + 0.867199889012184 + +At positive even integers, the Dirichlet eta function +evaluates to a rational multiple of a power of `\pi`:: + + >>> altzeta(2) + 0.822467033424113 + >>> pi**2/12 + 0.822467033424113 + +Like the Riemann zeta function, `\eta(s)`, approaches 1 +as `s` approaches positive infinity, although it does +so from below rather than from above:: + + >>> altzeta(30) + 0.999999999068682 + >>> altzeta(inf) + 1.0 + >>> mp.pretty = False + >>> altzeta(1000, rounding='d') + mpf('0.99999999999999989') + >>> altzeta(1000, rounding='u') + mpf('1.0') + +**References** + +1. http://mathworld.wolfram.com/DirichletEtaFunction.html + +2. http://en.wikipedia.org/wiki/Dirichlet_eta_function +""" + +factorial = r""" +Computes the factorial, `x!`. For integers `n \ge 0`, we have +`n! = 1 \cdot 2 \cdots (n-1) \cdot n` and more generally the factorial +is defined for real or complex `x` by `x! = \Gamma(x+1)`. + +**Examples** + +Basic values and limits:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> for k in range(6): + ... print("%s %s" % (k, fac(k))) + ... + 0 1.0 + 1 1.0 + 2 2.0 + 3 6.0 + 4 24.0 + 5 120.0 + >>> fac(inf) + +inf + >>> fac(0.5), sqrt(pi)/2 + (0.886226925452758, 0.886226925452758) + +For large positive `x`, `x!` can be approximated by +Stirling's formula:: + + >>> x = 10**10 + >>> fac(x) + 2.32579620567308e+95657055186 + >>> sqrt(2*pi*x)*(x/e)**x + 2.32579597597705e+95657055186 + +:func:`~mpmath.fac` supports evaluation for astronomically large values:: + + >>> fac(10**30) + 6.22311232304258e+29565705518096748172348871081098 + +Reciprocal factorials appear in the Taylor series of the +exponential function (among many other contexts):: + + >>> nsum(lambda k: 1/fac(k), [0, inf]), exp(1) + (2.71828182845905, 2.71828182845905) + >>> nsum(lambda k: pi**k/fac(k), [0, inf]), exp(pi) + (23.1406926327793, 23.1406926327793) + +""" + +gamma = r""" +Computes the gamma function, `\Gamma(x)`. The gamma function is a +shifted version of the ordinary factorial, satisfying +`\Gamma(n) = (n-1)!` for integers `n > 0`. More generally, it +is defined by + +.. math :: + + \Gamma(x) = \int_0^{\infty} t^{x-1} e^{-t}\, dt + +for any real or complex `x` with `\Re(x) > 0` and for `\Re(x) < 0` +by analytic continuation. + +**Examples** + +Basic values and limits:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> for k in range(1, 6): + ... print("%s %s" % (k, gamma(k))) + ... + 1 1.0 + 2 1.0 + 3 2.0 + 4 6.0 + 5 24.0 + >>> gamma(inf) + +inf + >>> gamma(0) + Traceback (most recent call last): + ... + ValueError: gamma function pole + +The gamma function of a half-integer is a rational multiple of +`\sqrt{\pi}`:: + + >>> gamma(0.5), sqrt(pi) + (1.77245385090552, 1.77245385090552) + >>> gamma(1.5), sqrt(pi)/2 + (0.886226925452758, 0.886226925452758) + +We can check the integral definition:: + + >>> gamma(3.5) + 3.32335097044784 + >>> quad(lambda t: t**2.5*exp(-t), [0,inf]) + 3.32335097044784 + +:func:`~mpmath.gamma` supports arbitrary-precision evaluation and +complex arguments:: + + >>> mp.dps = 50 + >>> gamma(sqrt(3)) + 0.91510229697308632046045539308226554038315280564184 + >>> mp.dps = 25 + >>> gamma(2j) + (0.009902440080927490985955066 - 0.07595200133501806872408048j) + +Arguments can also be large. Note that the gamma function grows +very quickly:: + + >>> mp.dps = 15 + >>> gamma(10**20) + 1.9328495143101e+1956570551809674817225 + +**References** + +* [Spouge]_ + +""" + +psi = r""" +Gives the polygamma function of order `m` of `z`, `\psi^{(m)}(z)`. +Special cases are known as the *digamma function* (`\psi^{(0)}(z)`), +the *trigamma function* (`\psi^{(1)}(z)`), etc. The polygamma +functions are defined as the logarithmic derivatives of the gamma +function: + +.. math :: + + \psi^{(m)}(z) = \left(\frac{d}{dz}\right)^{m+1} \log \Gamma(z) + +In particular, `\psi^{(0)}(z) = \Gamma'(z)/\Gamma(z)`. In the +present implementation of :func:`~mpmath.psi`, the order `m` must be a +nonnegative integer, while the argument `z` may be an arbitrary +complex number (with exception for the polygamma function's poles +at `z = 0, -1, -2, \ldots`). + +**Examples** + +For various rational arguments, the polygamma function reduces to +a combination of standard mathematical constants:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> psi(0, 1), -euler + (-0.5772156649015328606065121, -0.5772156649015328606065121) + >>> psi(1, '1/4'), pi**2+8*catalan + (17.19732915450711073927132, 17.19732915450711073927132) + >>> psi(2, '1/2'), -14*apery + (-16.82879664423431999559633, -16.82879664423431999559633) + +The polygamma functions are derivatives of each other:: + + >>> diff(lambda x: psi(3, x), pi), psi(4, pi) + (-0.1105749312578862734526952, -0.1105749312578862734526952) + >>> quad(lambda x: psi(4, x), [2, 3]), psi(3,3)-psi(3,2) + (-0.375, -0.375) + +The digamma function diverges logarithmically as `z \to \infty`, +while higher orders tend to zero:: + + >>> psi(0,inf), psi(1,inf), psi(2,inf) + (+inf, 0.0, 0.0) + +Evaluation for a complex argument:: + + >>> psi(2, -1-2j) + (0.03902435405364952654838445 + 0.1574325240413029954685366j) + +Evaluation is supported for large orders `m` and/or large +arguments `z`:: + + >>> psi(3, 10**100) + 2.0e-300 + >>> psi(250, 10**30+10**20*j) + (-1.293142504363642687204865e-7010 + 3.232856260909107391513108e-7018j) + +**Application to infinite series** + +Any infinite series where the summand is a rational function of +the index `k` can be evaluated in closed form in terms of polygamma +functions of the roots and poles of the summand:: + + >>> a = sqrt(2) + >>> b = sqrt(3) + >>> nsum(lambda k: 1/((k+a)**2*(k+b)), [0, inf]) + 0.4049668927517857061917531 + >>> (psi(0,a)-psi(0,b)-a*psi(1,a)+b*psi(1,a))/(a-b)**2 + 0.4049668927517857061917531 + +This follows from the series representation (`m > 0`) + +.. math :: + + \psi^{(m)}(z) = (-1)^{m+1} m! \sum_{k=0}^{\infty} + \frac{1}{(z+k)^{m+1}}. + +Since the roots of a polynomial may be complex, it is sometimes +necessary to use the complex polygamma function to evaluate +an entirely real-valued sum:: + + >>> nsum(lambda k: 1/(k**2-2*k+3), [0, inf]) + 1.694361433907061256154665 + >>> nprint(polyroots([1,-2,3])) + [(1.0 - 1.41421j), (1.0 + 1.41421j)] + >>> r1 = 1-sqrt(2)*j + >>> r2 = r1.conjugate() + >>> (psi(0,-r2)-psi(0,-r1))/(r1-r2) + (1.694361433907061256154665 + 0.0j) + +""" + +digamma = r""" +Shortcut for ``psi(0,z)``. +""" + +harmonic = r""" +If `n` is an integer, ``harmonic(n)`` gives a floating-point +approximation of the `n`-th harmonic number `H(n)`, defined as + +.. math :: + + H(n) = 1 + \frac{1}{2} + \frac{1}{3} + \ldots + \frac{1}{n} + +The first few harmonic numbers are:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> for n in range(8): + ... print("%s %s" % (n, harmonic(n))) + ... + 0 0.0 + 1 1.0 + 2 1.5 + 3 1.83333333333333 + 4 2.08333333333333 + 5 2.28333333333333 + 6 2.45 + 7 2.59285714285714 + +The infinite harmonic series `1 + 1/2 + 1/3 + \ldots` diverges:: + + >>> harmonic(inf) + +inf + +:func:`~mpmath.harmonic` is evaluated using the digamma function rather +than by summing the harmonic series term by term. It can therefore +be computed quickly for arbitrarily large `n`, and even for +nonintegral arguments:: + + >>> harmonic(10**100) + 230.835724964306 + >>> harmonic(0.5) + 0.613705638880109 + >>> harmonic(3+4j) + (2.24757548223494 + 0.850502209186044j) + +:func:`~mpmath.harmonic` supports arbitrary precision evaluation:: + + >>> mp.dps = 50 + >>> harmonic(11) + 3.0198773448773448773448773448773448773448773448773 + >>> harmonic(pi) + 1.8727388590273302654363491032336134987519132374152 + +The harmonic series diverges, but at a glacial pace. It is possible +to calculate the exact number of terms required before the sum +exceeds a given amount, say 100:: + + >>> mp.dps = 50 + >>> v = 10**findroot(lambda x: harmonic(10**x) - 100, 10) + >>> v + 15092688622113788323693563264538101449859496.864101 + >>> v = int(ceil(v)) + >>> print(v) + 15092688622113788323693563264538101449859497 + >>> harmonic(v-1) + 99.999999999999999999999999999999999999999999942747 + >>> harmonic(v) + 100.000000000000000000000000000000000000000000009 + +""" + +bernoulli = r""" +Computes the nth Bernoulli number, `B_n`, for any integer `n \ge 0`. + +The Bernoulli numbers are rational numbers, but this function +returns a floating-point approximation. To obtain an exact +fraction, use :func:`~mpmath.bernfrac` instead. + +**Examples** + +Numerical values of the first few Bernoulli numbers:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> for n in range(15): + ... print("%s %s" % (n, bernoulli(n))) + ... + 0 1.0 + 1 -0.5 + 2 0.166666666666667 + 3 0.0 + 4 -0.0333333333333333 + 5 0.0 + 6 0.0238095238095238 + 7 0.0 + 8 -0.0333333333333333 + 9 0.0 + 10 0.0757575757575758 + 11 0.0 + 12 -0.253113553113553 + 13 0.0 + 14 1.16666666666667 + +Bernoulli numbers can be approximated with arbitrary precision:: + + >>> mp.dps = 50 + >>> bernoulli(100) + -2.8382249570693706959264156336481764738284680928013e+78 + +Arbitrarily large `n` are supported:: + + >>> mp.dps = 15 + >>> bernoulli(10**20 + 2) + 3.09136296657021e+1876752564973863312327 + +The Bernoulli numbers are related to the Riemann zeta function +at integer arguments:: + + >>> -bernoulli(8) * (2*pi)**8 / (2*fac(8)) + 1.00407735619794 + >>> zeta(8) + 1.00407735619794 + +**Algorithm** + +For small `n` (`n < 3000`) :func:`~mpmath.bernoulli` uses a recurrence +formula due to Ramanujan. All results in this range are cached, +so sequential computation of small Bernoulli numbers is +guaranteed to be fast. + +For larger `n`, `B_n` is evaluated in terms of the Riemann zeta +function. +""" + +stieltjes = r""" +For a nonnegative integer `n`, ``stieltjes(n)`` computes the +`n`-th Stieltjes constant `\gamma_n`, defined as the +`n`-th coefficient in the Laurent series expansion of the +Riemann zeta function around the pole at `s = 1`. That is, +we have: + +.. math :: + + \zeta(s) = \frac{1}{s-1} \sum_{n=0}^{\infty} + \frac{(-1)^n}{n!} \gamma_n (s-1)^n + +More generally, ``stieltjes(n, a)`` gives the corresponding +coefficient `\gamma_n(a)` for the Hurwitz zeta function +`\zeta(s,a)` (with `\gamma_n = \gamma_n(1)`). + +**Examples** + +The zeroth Stieltjes constant is just Euler's constant `\gamma`:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> stieltjes(0) + 0.577215664901533 + +Some more values are:: + + >>> stieltjes(1) + -0.0728158454836767 + >>> stieltjes(10) + 0.000205332814909065 + >>> stieltjes(30) + 0.00355772885557316 + >>> stieltjes(1000) + -1.57095384420474e+486 + >>> stieltjes(2000) + 2.680424678918e+1109 + >>> stieltjes(1, 2.5) + -0.23747539175716 + +An alternative way to compute `\gamma_1`:: + + >>> diff(extradps(15)(lambda x: 1/(x-1) - zeta(x)), 1) + -0.0728158454836767 + +:func:`~mpmath.stieltjes` supports arbitrary precision evaluation:: + + >>> mp.dps = 50 + >>> stieltjes(2) + -0.0096903631928723184845303860352125293590658061013408 + +**Algorithm** + +:func:`~mpmath.stieltjes` numerically evaluates the integral in +the following representation due to Ainsworth, Howell and +Coffey [1], [2]: + +.. math :: + + \gamma_n(a) = \frac{\log^n a}{2a} - \frac{\log^{n+1}(a)}{n+1} + + \frac{2}{a} \Re \int_0^{\infty} + \frac{(x/a-i)\log^n(a-ix)}{(1+x^2/a^2)(e^{2\pi x}-1)} dx. + +For some reference values with `a = 1`, see e.g. [4]. + +**References** + +1. O. R. Ainsworth & L. W. Howell, "An integral representation of + the generalized Euler-Mascheroni constants", NASA Technical + Paper 2456 (1985), + http://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/19850014994_1985014994.pdf + +2. M. W. Coffey, "The Stieltjes constants, their relation to the + `\eta_j` coefficients, and representation of the Hurwitz + zeta function", arXiv:0706.0343v1 http://arxiv.org/abs/0706.0343 + +3. http://mathworld.wolfram.com/StieltjesConstants.html + +4. http://pi.lacim.uqam.ca/piDATA/stieltjesgamma.txt + +""" + +gammaprod = r""" +Given iterables `a` and `b`, ``gammaprod(a, b)`` computes the +product / quotient of gamma functions: + +.. math :: + + \frac{\Gamma(a_0) \Gamma(a_1) \cdots \Gamma(a_p)} + {\Gamma(b_0) \Gamma(b_1) \cdots \Gamma(b_q)} + +Unlike direct calls to :func:`~mpmath.gamma`, :func:`~mpmath.gammaprod` considers +the entire product as a limit and evaluates this limit properly if +any of the numerator or denominator arguments are nonpositive +integers such that poles of the gamma function are encountered. +That is, :func:`~mpmath.gammaprod` evaluates + +.. math :: + + \lim_{\epsilon \to 0} + \frac{\Gamma(a_0+\epsilon) \Gamma(a_1+\epsilon) \cdots + \Gamma(a_p+\epsilon)} + {\Gamma(b_0+\epsilon) \Gamma(b_1+\epsilon) \cdots + \Gamma(b_q+\epsilon)} + +In particular: + +* If there are equally many poles in the numerator and the + denominator, the limit is a rational number times the remaining, + regular part of the product. + +* If there are more poles in the numerator, :func:`~mpmath.gammaprod` + returns ``+inf``. + +* If there are more poles in the denominator, :func:`~mpmath.gammaprod` + returns 0. + +**Examples** + +The reciprocal gamma function `1/\Gamma(x)` evaluated at `x = 0`:: + + >>> from mpmath import * + >>> mp.dps = 15 + >>> gammaprod([], [0]) + 0.0 + +A limit:: + + >>> gammaprod([-4], [-3]) + -0.25 + >>> limit(lambda x: gamma(x-1)/gamma(x), -3, direction=1) + -0.25 + >>> limit(lambda x: gamma(x-1)/gamma(x), -3, direction=-1) + -0.25 + +""" + +beta = r""" +Computes the beta function, +`B(x,y) = \Gamma(x) \Gamma(y) / \Gamma(x+y)`. +The beta function is also commonly defined by the integral +representation + +.. math :: + + B(x,y) = \int_0^1 t^{x-1} (1-t)^{y-1} \, dt + +**Examples** + +For integer and half-integer arguments where all three gamma +functions are finite, the beta function becomes either rational +number or a rational multiple of `\pi`:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> beta(5, 2) + 0.0333333333333333 + >>> beta(1.5, 2) + 0.266666666666667 + >>> 16*beta(2.5, 1.5) + 3.14159265358979 + +Where appropriate, :func:`~mpmath.beta` evaluates limits. A pole +of the beta function is taken to result in ``+inf``:: + + >>> beta(-0.5, 0.5) + 0.0 + >>> beta(-3, 3) + -0.333333333333333 + >>> beta(-2, 3) + +inf + >>> beta(inf, 1) + 0.0 + >>> beta(inf, 0) + nan + +:func:`~mpmath.beta` supports complex numbers and arbitrary precision +evaluation:: + + >>> beta(1, 2+j) + (0.4 - 0.2j) + >>> mp.dps = 25 + >>> beta(j,0.5) + (1.079424249270925780135675 - 1.410032405664160838288752j) + >>> mp.dps = 50 + >>> beta(pi, e) + 0.037890298781212201348153837138927165984170287886464 + +Various integrals can be computed by means of the +beta function:: + + >>> mp.dps = 15 + >>> quad(lambda t: t**2.5*(1-t)**2, [0, 1]) + 0.0230880230880231 + >>> beta(3.5, 3) + 0.0230880230880231 + >>> quad(lambda t: sin(t)**4 * sqrt(cos(t)), [0, pi/2]) + 0.319504062596158 + >>> beta(2.5, 0.75)/2 + 0.319504062596158 + +""" + +betainc = r""" +``betainc(a, b, x1=0, x2=1, regularized=False)`` gives the generalized +incomplete beta function, + +.. math :: + + I_{x_1}^{x_2}(a,b) = \int_{x_1}^{x_2} t^{a-1} (1-t)^{b-1} dt. + +When `x_1 = 0, x_2 = 1`, this reduces to the ordinary (complete) +beta function `B(a,b)`; see :func:`~mpmath.beta`. + +With the keyword argument ``regularized=True``, :func:`~mpmath.betainc` +computes the regularized incomplete beta function +`I_{x_1}^{x_2}(a,b) / B(a,b)`. This is the cumulative distribution of the +beta distribution with parameters `a`, `b`. + +.. note : + + Implementations of the incomplete beta function in some other + software uses a different argument order. For example, Mathematica uses the + reversed argument order ``Beta[x1,x2,a,b]``. For the equivalent of SciPy's + three-argument incomplete beta integral (implicitly with `x1 = 0`), use + ``betainc(a,b,0,x2,regularized=True)``. + +**Examples** + +Verifying that :func:`~mpmath.betainc` computes the integral in the +definition:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> x,y,a,b = 3, 4, 0, 6 + >>> betainc(x, y, a, b) + -4010.4 + >>> quad(lambda t: t**(x-1) * (1-t)**(y-1), [a, b]) + -4010.4 + +The arguments may be arbitrary complex numbers:: + + >>> betainc(0.75, 1-4j, 0, 2+3j) + (0.2241657956955709603655887 + 0.3619619242700451992411724j) + +With regularization:: + + >>> betainc(1, 2, 0, 0.25, regularized=True) + 0.4375 + >>> betainc(pi, e, 0, 1, regularized=True) # Complete + 1.0 + +The beta integral satisfies some simple argument transformation +symmetries:: + + >>> mp.dps = 15 + >>> betainc(2,3,4,5), -betainc(2,3,5,4), betainc(3,2,1-5,1-4) + (56.0833333333333, 56.0833333333333, 56.0833333333333) + +The beta integral can often be evaluated analytically. For integer and +rational arguments, the incomplete beta function typically reduces to a +simple algebraic-logarithmic expression:: + + >>> mp.dps = 25 + >>> identify(chop(betainc(0, 0, 3, 4))) + '-(log((9/8)))' + >>> identify(betainc(2, 3, 4, 5)) + '(673/12)' + >>> identify(betainc(1.5, 1, 1, 2)) + '((-12+sqrt(1152))/18)' + +""" + +binomial = r""" +Computes the binomial coefficient + +.. math :: + + {n \choose k} = \frac{n!}{k!(n-k)!}. + +The binomial coefficient gives the number of ways that `k` items +can be chosen from a set of `n` items. More generally, the binomial +coefficient is a well-defined function of arbitrary real or +complex `n` and `k`, via the gamma function. + +**Examples** + +Generate Pascal's triangle:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> for n in range(5): + ... nprint([binomial(n,k) for k in range(n+1)]) + ... + [1.0] + [1.0, 1.0] + [1.0, 2.0, 1.0] + [1.0, 3.0, 3.0, 1.0] + [1.0, 4.0, 6.0, 4.0, 1.0] + +There is 1 way to select 0 items from the empty set, and 0 ways to +select 1 item from the empty set:: + + >>> binomial(0, 0) + 1.0 + >>> binomial(0, 1) + 0.0 + +:func:`~mpmath.binomial` supports large arguments:: + + >>> binomial(10**20, 10**20-5) + 8.33333333333333e+97 + >>> binomial(10**20, 10**10) + 2.60784095465201e+104342944813 + +Nonintegral binomial coefficients find use in series +expansions:: + + >>> nprint(taylor(lambda x: (1+x)**0.25, 0, 4)) + [1.0, 0.25, -0.09375, 0.0546875, -0.0375977] + >>> nprint([binomial(0.25, k) for k in range(5)]) + [1.0, 0.25, -0.09375, 0.0546875, -0.0375977] + +An integral representation:: + + >>> n, k = 5, 3 + >>> f = lambda t: exp(-j*k*t)*(1+exp(j*t))**n + >>> chop(quad(f, [-pi,pi])/(2*pi)) + 10.0 + >>> binomial(n,k) + 10.0 + +""" + +rf = r""" +Computes the rising factorial or Pochhammer symbol, + +.. math :: + + x^{(n)} = x (x+1) \cdots (x+n-1) = \frac{\Gamma(x+n)}{\Gamma(x)} + +where the rightmost expression is valid for nonintegral `n`. + +**Examples** + +For integral `n`, the rising factorial is a polynomial:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> for n in range(5): + ... nprint(taylor(lambda x: rf(x,n), 0, n)) + ... + [1.0] + [0.0, 1.0] + [0.0, 1.0, 1.0] + [0.0, 2.0, 3.0, 1.0] + [0.0, 6.0, 11.0, 6.0, 1.0] + +Evaluation is supported for arbitrary arguments:: + + >>> rf(2+3j, 5.5) + (-7202.03920483347 - 3777.58810701527j) +""" + +ff = r""" +Computes the falling factorial, + +.. math :: + + (x)_n = x (x-1) \cdots (x-n+1) = \frac{\Gamma(x+1)}{\Gamma(x-n+1)} + +where the rightmost expression is valid for nonintegral `n`. + +**Examples** + +For integral `n`, the falling factorial is a polynomial:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> for n in range(5): + ... nprint(taylor(lambda x: ff(x,n), 0, n)) + ... + [1.0] + [0.0, 1.0] + [0.0, -1.0, 1.0] + [0.0, 2.0, -3.0, 1.0] + [0.0, -6.0, 11.0, -6.0, 1.0] + +Evaluation is supported for arbitrary arguments:: + + >>> ff(2+3j, 5.5) + (-720.41085888203 + 316.101124983878j) +""" + +fac2 = r""" +Computes the double factorial `x!!`, defined for integers +`x > 0` by + +.. math :: + + x!! = \begin{cases} + 1 \cdot 3 \cdots (x-2) \cdot x & x \;\mathrm{odd} \\ + 2 \cdot 4 \cdots (x-2) \cdot x & x \;\mathrm{even} + \end{cases} + +and more generally by [1] + +.. math :: + + x!! = 2^{x/2} \left(\frac{\pi}{2}\right)^{(\cos(\pi x)-1)/4} + \Gamma\left(\frac{x}{2}+1\right). + +**Examples** + +The integer sequence of double factorials begins:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> nprint([fac2(n) for n in range(10)]) + [1.0, 1.0, 2.0, 3.0, 8.0, 15.0, 48.0, 105.0, 384.0, 945.0] + +For large `x`, double factorials follow a Stirling-like asymptotic +approximation:: + + >>> x = mpf(10000) + >>> fac2(x) + 5.97272691416282e+17830 + >>> sqrt(pi)*x**((x+1)/2)*exp(-x/2) + 5.97262736954392e+17830 + +The recurrence formula `x!! = x (x-2)!!` can be reversed to +define the double factorial of negative odd integers (but +not negative even integers):: + + >>> fac2(-1), fac2(-3), fac2(-5), fac2(-7) + (1.0, -1.0, 0.333333333333333, -0.0666666666666667) + >>> fac2(-2) + Traceback (most recent call last): + ... + ValueError: gamma function pole + +With the exception of the poles at negative even integers, +:func:`~mpmath.fac2` supports evaluation for arbitrary complex arguments. +The recurrence formula is valid generally:: + + >>> fac2(pi+2j) + (-1.3697207890154e-12 + 3.93665300979176e-12j) + >>> (pi+2j)*fac2(pi-2+2j) + (-1.3697207890154e-12 + 3.93665300979176e-12j) + +Double factorials should not be confused with nested factorials, +which are immensely larger:: + + >>> fac(fac(20)) + 5.13805976125208e+43675043585825292774 + >>> fac2(20) + 3715891200.0 + +Double factorials appear, among other things, in series expansions +of Gaussian functions and the error function. Infinite series +include:: + + >>> nsum(lambda k: 1/fac2(k), [0, inf]) + 3.05940740534258 + >>> sqrt(e)*(1+sqrt(pi/2)*erf(sqrt(2)/2)) + 3.05940740534258 + >>> nsum(lambda k: 2**k/fac2(2*k-1), [1, inf]) + 4.06015693855741 + >>> e * erf(1) * sqrt(pi) + 4.06015693855741 + +A beautiful Ramanujan sum:: + + >>> nsum(lambda k: (-1)**k*(fac2(2*k-1)/fac2(2*k))**3, [0,inf]) + 0.90917279454693 + >>> (gamma('9/8')/gamma('5/4')/gamma('7/8'))**2 + 0.90917279454693 + +**References** + +1. http://functions.wolfram.com/GammaBetaErf/Factorial2/27/01/0002/ + +2. http://mathworld.wolfram.com/DoubleFactorial.html + +""" + +hyper = r""" +Evaluates the generalized hypergeometric function + +.. math :: + + \,_pF_q(a_1,\ldots,a_p; b_1,\ldots,b_q; z) = + \sum_{n=0}^\infty \frac{(a_1)_n (a_2)_n \ldots (a_p)_n} + {(b_1)_n(b_2)_n\ldots(b_q)_n} \frac{z^n}{n!} + +where `(x)_n` denotes the rising factorial (see :func:`~mpmath.rf`). + +The parameters lists ``a_s`` and ``b_s`` may contain integers, +real numbers, complex numbers, as well as exact fractions given in +the form of tuples `(p, q)`. :func:`~mpmath.hyper` is optimized to handle +integers and fractions more efficiently than arbitrary +floating-point parameters (since rational parameters are by +far the most common). + +**Examples** + +Verifying that :func:`~mpmath.hyper` gives the sum in the definition, by +comparison with :func:`~mpmath.nsum`:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> a,b,c,d = 2,3,4,5 + >>> x = 0.25 + >>> hyper([a,b],[c,d],x) + 1.078903941164934876086237 + >>> fn = lambda n: rf(a,n)*rf(b,n)/rf(c,n)/rf(d,n)*x**n/fac(n) + >>> nsum(fn, [0, inf]) + 1.078903941164934876086237 + +The parameters can be any combination of integers, fractions, +floats and complex numbers:: + + >>> a, b, c, d, e = 1, (-1,2), pi, 3+4j, (2,3) + >>> x = 0.2j + >>> hyper([a,b],[c,d,e],x) + (0.9923571616434024810831887 - 0.005753848733883879742993122j) + >>> b, e = -0.5, mpf(2)/3 + >>> fn = lambda n: rf(a,n)*rf(b,n)/rf(c,n)/rf(d,n)/rf(e,n)*x**n/fac(n) + >>> nsum(fn, [0, inf]) + (0.9923571616434024810831887 - 0.005753848733883879742993122j) + +The `\,_0F_0` and `\,_1F_0` series are just elementary functions:: + + >>> a, z = sqrt(2), +pi + >>> hyper([],[],z) + 23.14069263277926900572909 + >>> exp(z) + 23.14069263277926900572909 + >>> hyper([a],[],z) + (-0.09069132879922920160334114 + 0.3283224323946162083579656j) + >>> (1-z)**(-a) + (-0.09069132879922920160334114 + 0.3283224323946162083579656j) + +If any `a_k` coefficient is a nonpositive integer, the series terminates +into a finite polynomial:: + + >>> hyper([1,1,1,-3],[2,5],1) + 0.7904761904761904761904762 + >>> identify(_) + '(83/105)' + +If any `b_k` is a nonpositive integer, the function is undefined (unless the +series terminates before the division by zero occurs):: + + >>> hyper([1,1,1,-3],[-2,5],1) + Traceback (most recent call last): + ... + ZeroDivisionError: pole in hypergeometric series + >>> hyper([1,1,1,-1],[-2,5],1) + 1.1 + +Except for polynomial cases, the radius of convergence `R` of the hypergeometric +series is either `R = \infty` (if `p \le q`), `R = 1` (if `p = q+1`), or +`R = 0` (if `p > q+1`). + +The analytic continuations of the functions with `p = q+1`, i.e. `\,_2F_1`, +`\,_3F_2`, `\,_4F_3`, etc, are all implemented and therefore these functions +can be evaluated for `|z| \ge 1`. The shortcuts :func:`~mpmath.hyp2f1`, :func:`~mpmath.hyp3f2` +are available to handle the most common cases (see their documentation), +but functions of higher degree are also supported via :func:`~mpmath.hyper`:: + + >>> hyper([1,2,3,4], [5,6,7], 1) # 4F3 at finite-valued branch point + 1.141783505526870731311423 + >>> hyper([4,5,6,7], [1,2,3], 1) # 4F3 at pole + +inf + >>> hyper([1,2,3,4,5], [6,7,8,9], 10) # 5F4 + (1.543998916527972259717257 - 0.5876309929580408028816365j) + >>> hyper([1,2,3,4,5,6], [7,8,9,10,11], 1j) # 6F5 + (0.9996565821853579063502466 + 0.0129721075905630604445669j) + +Near `z = 1` with noninteger parameters:: + + >>> hyper(['1/3',1,'3/2',2], ['1/5','11/6','41/8'], 1) + 2.219433352235586121250027 + >>> hyper(['1/3',1,'3/2',2], ['1/5','11/6','5/4'], 1) + +inf + >>> eps1 = extradps(6)(lambda: 1 - mpf('1e-6'))() + >>> hyper(['1/3',1,'3/2',2], ['1/5','11/6','5/4'], eps1) + 2923978034.412973409330956 + +Please note that, as currently implemented, evaluation of `\,_pF_{p-1}` +with `p \ge 3` may be slow or inaccurate when `|z-1|` is small, +for some parameter values. + +Evaluation may be aborted if convergence appears to be too slow. +The optional ``maxterms`` (limiting the number of series terms) and ``maxprec`` +(limiting the internal precision) keyword arguments can be used +to control evaluation:: + + >>> hyper([1,2,3], [4,5,6], 10000) # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + NoConvergence: Hypergeometric series converges too slowly. Try increasing maxterms. + >>> hyper([1,2,3], [4,5,6], 10000, maxterms=10**6) + 7.622806053177969474396918e+4310 + +Additional options include ``force_series`` (which forces direct use of +a hypergeometric series even if another evaluation method might work better) +and ``asymp_tol`` which controls the target tolerance for using +asymptotic series. + +When `p > q+1`, ``hyper`` computes the (iterated) Borel sum of the divergent +series. For `\,_2F_0` the Borel sum has an analytic solution and can be +computed efficiently (see :func:`~mpmath.hyp2f0`). For higher degrees, the functions +is evaluated first by attempting to sum it directly as an asymptotic +series (this only works for tiny `|z|`), and then by evaluating the Borel +regularized sum using numerical integration. Except for +special parameter combinations, this can be extremely slow. + + >>> hyper([1,1], [], 0.5) # regularization of 2F0 + (1.340965419580146562086448 + 0.8503366631752726568782447j) + >>> hyper([1,1,1,1], [1], 0.5) # regularization of 4F1 + (1.108287213689475145830699 + 0.5327107430640678181200491j) + +With the following magnitude of argument, the asymptotic series for `\,_3F_1` +gives only a few digits. Using Borel summation, ``hyper`` can produce +a value with full accuracy:: + + >>> mp.dps = 15 + >>> hyper([2,0.5,4], [5.25], '0.08', force_series=True) # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + NoConvergence: Hypergeometric series converges too slowly. Try increasing maxterms. + >>> hyper([2,0.5,4], [5.25], '0.08', asymp_tol=1e-4) + 1.0725535790737 + >>> hyper([2,0.5,4], [5.25], '0.08') + (1.07269542893559 + 5.54668863216891e-5j) + >>> hyper([2,0.5,4], [5.25], '-0.08', asymp_tol=1e-4) + 0.946344925484879 + >>> hyper([2,0.5,4], [5.25], '-0.08') + 0.946312503737771 + >>> mp.dps = 25 + >>> hyper([2,0.5,4], [5.25], '-0.08') + 0.9463125037377662296700858 + +Note that with the positive `z` value, there is a complex part in the +correct result, which falls below the tolerance of the asymptotic series. + +By default, a parameter that appears in both ``a_s`` and ``b_s`` will be removed +unless it is a nonpositive integer. This generally speeds up evaluation +by producing a hypergeometric function of lower order. +This optimization can be disabled by passing ``eliminate=False``. + + >>> hyper([1,2,3], [4,5,3], 10000) + 1.268943190440206905892212e+4321 + >>> hyper([1,2,3], [4,5,3], 10000, eliminate=False) # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + NoConvergence: Hypergeometric series converges too slowly. Try increasing maxterms. + >>> hyper([1,2,3], [4,5,3], 10000, eliminate=False, maxterms=10**6) + 1.268943190440206905892212e+4321 + +If a nonpositive integer `-n` appears in both ``a_s`` and ``b_s``, this parameter +cannot be unambiguously removed since it creates a term 0 / 0. +In this case the hypergeometric series is understood to terminate before +the division by zero occurs. This convention is consistent with Mathematica. +An alternative convention of eliminating the parameters can be toggled +with ``eliminate_all=True``: + + >>> hyper([2,-1], [-1], 3) + 7.0 + >>> hyper([2,-1], [-1], 3, eliminate_all=True) + 0.25 + >>> hyper([2], [], 3) + 0.25 + +""" + +hypercomb = r""" +Computes a weighted combination of hypergeometric functions + +.. math :: + + \sum_{r=1}^N \left[ \prod_{k=1}^{l_r} {w_{r,k}}^{c_{r,k}} + \frac{\prod_{k=1}^{m_r} \Gamma(\alpha_{r,k})}{\prod_{k=1}^{n_r} + \Gamma(\beta_{r,k})} + \,_{p_r}F_{q_r}(a_{r,1},\ldots,a_{r,p}; b_{r,1}, + \ldots, b_{r,q}; z_r)\right]. + +Typically the parameters are linear combinations of a small set of base +parameters; :func:`~mpmath.hypercomb` permits computing a correct value in +the case that some of the `\alpha`, `\beta`, `b` turn out to be +nonpositive integers, or if division by zero occurs for some `w^c`, +assuming that there are opposing singularities that cancel out. +The limit is computed by evaluating the function with the base +parameters perturbed, at a higher working precision. + +The first argument should be a function that takes the perturbable +base parameters ``params`` as input and returns `N` tuples +``(w, c, alpha, beta, a, b, z)``, where the coefficients ``w``, ``c``, +gamma factors ``alpha``, ``beta``, and hypergeometric coefficients +``a``, ``b`` each should be lists of numbers, and ``z`` should be a single +number. + +**Examples** + +The following evaluates + +.. math :: + + (a-1) \frac{\Gamma(a-3)}{\Gamma(a-4)} \,_1F_1(a,a-1,z) = e^z(a-4)(a+z-1) + +with `a=1, z=3`. There is a zero factor, two gamma function poles, and +the 1F1 function is singular; all singularities cancel out to give a finite +value:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> hypercomb(lambda a: [([a-1],[1],[a-3],[a-4],[a],[a-1],3)], [1]) + -180.769832308689 + >>> -9*exp(3) + -180.769832308689 + +""" + +hyp0f1 = r""" +Gives the hypergeometric function `\,_0F_1`, sometimes known as the +confluent limit function, defined as + +.. math :: + + \,_0F_1(a,z) = \sum_{k=0}^{\infty} \frac{1}{(a)_k} \frac{z^k}{k!}. + +This function satisfies the differential equation `z f''(z) + a f'(z) = f(z)`, +and is related to the Bessel function of the first kind (see :func:`~mpmath.besselj`). + +``hyp0f1(a,z)`` is equivalent to ``hyper([],[a],z)``; see documentation for +:func:`~mpmath.hyper` for more information. + +**Examples** + +Evaluation for arbitrary arguments:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> hyp0f1(2, 0.25) + 1.130318207984970054415392 + >>> hyp0f1((1,2), 1234567) + 6.27287187546220705604627e+964 + >>> hyp0f1(3+4j, 1000000j) + (3.905169561300910030267132e+606 + 3.807708544441684513934213e+606j) + +Evaluation is supported for arbitrarily large values of `z`, +using asymptotic expansions:: + + >>> hyp0f1(1, 10**50) + 2.131705322874965310390701e+8685889638065036553022565 + >>> hyp0f1(1, -10**50) + 1.115945364792025420300208e-13 + +Verifying the differential equation:: + + >>> a = 2.5 + >>> f = lambda z: hyp0f1(a,z) + >>> for z in [0, 10, 3+4j]: + ... chop(z*diff(f,z,2) + a*diff(f,z) - f(z)) + ... + 0.0 + 0.0 + 0.0 + +""" + +hyp1f1 = r""" +Gives the confluent hypergeometric function of the first kind, + +.. math :: + + \,_1F_1(a,b,z) = \sum_{k=0}^{\infty} \frac{(a)_k}{(b)_k} \frac{z^k}{k!}, + +also known as Kummer's function and sometimes denoted by `M(a,b,z)`. This +function gives one solution to the confluent (Kummer's) differential equation + +.. math :: + + z f''(z) + (b-z) f'(z) - af(z) = 0. + +A second solution is given by the `U` function; see :func:`~mpmath.hyperu`. +Solutions are also given in an alternate form by the Whittaker +functions (:func:`~mpmath.whitm`, :func:`~mpmath.whitw`). + +``hyp1f1(a,b,z)`` is equivalent +to ``hyper([a],[b],z)``; see documentation for :func:`~mpmath.hyper` for more +information. + +**Examples** + +Evaluation for real and complex values of the argument `z`, with +fixed parameters `a = 2, b = -1/3`:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> hyp1f1(2, (-1,3), 3.25) + -2815.956856924817275640248 + >>> hyp1f1(2, (-1,3), -3.25) + -1.145036502407444445553107 + >>> hyp1f1(2, (-1,3), 1000) + -8.021799872770764149793693e+441 + >>> hyp1f1(2, (-1,3), -1000) + 0.000003131987633006813594535331 + >>> hyp1f1(2, (-1,3), 100+100j) + (-3.189190365227034385898282e+48 - 1.106169926814270418999315e+49j) + +Parameters may be complex:: + + >>> hyp1f1(2+3j, -1+j, 10j) + (261.8977905181045142673351 + 160.8930312845682213562172j) + +Arbitrarily large values of `z` are supported:: + + >>> hyp1f1(3, 4, 10**20) + 3.890569218254486878220752e+43429448190325182745 + >>> hyp1f1(3, 4, -10**20) + 6.0e-60 + >>> hyp1f1(3, 4, 10**20*j) + (-1.935753855797342532571597e-20 - 2.291911213325184901239155e-20j) + +Verifying the differential equation:: + + >>> a, b = 1.5, 2 + >>> f = lambda z: hyp1f1(a,b,z) + >>> for z in [0, -10, 3, 3+4j]: + ... chop(z*diff(f,z,2) + (b-z)*diff(f,z) - a*f(z)) + ... + 0.0 + 0.0 + 0.0 + 0.0 + +An integral representation:: + + >>> a, b = 1.5, 3 + >>> z = 1.5 + >>> hyp1f1(a,b,z) + 2.269381460919952778587441 + >>> g = lambda t: exp(z*t)*t**(a-1)*(1-t)**(b-a-1) + >>> gammaprod([b],[a,b-a])*quad(g, [0,1]) + 2.269381460919952778587441 + + +""" + +hyp1f2 = r""" +Gives the hypergeometric function `\,_1F_2(a_1,a_2;b_1,b_2; z)`. +The call ``hyp1f2(a1,b1,b2,z)`` is equivalent to +``hyper([a1],[b1,b2],z)``. + +Evaluation works for complex and arbitrarily large arguments:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> a, b, c = 1.5, (-1,3), 2.25 + >>> hyp1f2(a, b, c, 10**20) + -1.159388148811981535941434e+8685889639 + >>> hyp1f2(a, b, c, -10**20) + -12.60262607892655945795907 + >>> hyp1f2(a, b, c, 10**20*j) + (4.237220401382240876065501e+6141851464 - 2.950930337531768015892987e+6141851464j) + >>> hyp1f2(2+3j, -2j, 0.5j, 10-20j) + (135881.9905586966432662004 - 86681.95885418079535738828j) + +""" + +hyp2f2 = r""" +Gives the hypergeometric function `\,_2F_2(a_1,a_2;b_1,b_2; z)`. +The call ``hyp2f2(a1,a2,b1,b2,z)`` is equivalent to +``hyper([a1,a2],[b1,b2],z)``. + +Evaluation works for complex and arbitrarily large arguments:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> a, b, c, d = 1.5, (-1,3), 2.25, 4 + >>> hyp2f2(a, b, c, d, 10**20) + -5.275758229007902299823821e+43429448190325182663 + >>> hyp2f2(a, b, c, d, -10**20) + 2561445.079983207701073448 + >>> hyp2f2(a, b, c, d, 10**20*j) + (2218276.509664121194836667 - 1280722.539991603850462856j) + >>> hyp2f2(2+3j, -2j, 0.5j, 4j, 10-20j) + (80500.68321405666957342788 - 20346.82752982813540993502j) + +""" + +hyp2f3 = r""" +Gives the hypergeometric function `\,_2F_3(a_1,a_2;b_1,b_2,b_3; z)`. +The call ``hyp2f3(a1,a2,b1,b2,b3,z)`` is equivalent to +``hyper([a1,a2],[b1,b2,b3],z)``. + +Evaluation works for arbitrarily large arguments:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> a1,a2,b1,b2,b3 = 1.5, (-1,3), 2.25, 4, (1,5) + >>> hyp2f3(a1,a2,b1,b2,b3,10**20) + -4.169178177065714963568963e+8685889590 + >>> hyp2f3(a1,a2,b1,b2,b3,-10**20) + 7064472.587757755088178629 + >>> hyp2f3(a1,a2,b1,b2,b3,10**20*j) + (-5.163368465314934589818543e+6141851415 + 1.783578125755972803440364e+6141851416j) + >>> hyp2f3(2+3j, -2j, 0.5j, 4j, -1-j, 10-20j) + (-2280.938956687033150740228 + 13620.97336609573659199632j) + >>> hyp2f3(2+3j, -2j, 0.5j, 4j, -1-j, 10000000-20000000j) + (4.849835186175096516193e+3504 - 3.365981529122220091353633e+3504j) + +""" + +hyp2f1 = r""" +Gives the Gauss hypergeometric function `\,_2F_1` (often simply referred to as +*the* hypergeometric function), defined for `|z| < 1` as + +.. math :: + + \,_2F_1(a,b,c,z) = \sum_{k=0}^{\infty} + \frac{(a)_k (b)_k}{(c)_k} \frac{z^k}{k!}. + +and for `|z| \ge 1` by analytic continuation, with a branch cut on `(1, \infty)` +when necessary. + +Special cases of this function include many of the orthogonal polynomials as +well as the incomplete beta function and other functions. Properties of the +Gauss hypergeometric function are documented comprehensively in many references, +for example Abramowitz & Stegun, section 15. + +The implementation supports the analytic continuation as well as evaluation +close to the unit circle where `|z| \approx 1`. The syntax ``hyp2f1(a,b,c,z)`` +is equivalent to ``hyper([a,b],[c],z)``. + +**Examples** + +Evaluation with `z` inside, outside and on the unit circle, for +fixed parameters:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> hyp2f1(2, (1,2), 4, 0.75) + 1.303703703703703703703704 + >>> hyp2f1(2, (1,2), 4, -1.75) + 0.7431290566046919177853916 + >>> hyp2f1(2, (1,2), 4, 1.75) + (1.418075801749271137026239 - 1.114976146679907015775102j) + >>> hyp2f1(2, (1,2), 4, 1) + 1.6 + >>> hyp2f1(2, (1,2), 4, -1) + 0.8235498012182875315037882 + >>> hyp2f1(2, (1,2), 4, j) + (0.9144026291433065674259078 + 0.2050415770437884900574923j) + >>> hyp2f1(2, (1,2), 4, 2+j) + (0.9274013540258103029011549 + 0.7455257875808100868984496j) + >>> hyp2f1(2, (1,2), 4, 0.25j) + (0.9931169055799728251931672 + 0.06154836525312066938147793j) + +Evaluation with complex parameter values:: + + >>> hyp2f1(1+j, 0.75, 10j, 1+5j) + (0.8834833319713479923389638 + 0.7053886880648105068343509j) + +Evaluation with `z = 1`:: + + >>> hyp2f1(-2.5, 3.5, 1.5, 1) + 0.0 + >>> hyp2f1(-2.5, 3, 4, 1) + 0.06926406926406926406926407 + >>> hyp2f1(2, 3, 4, 1) + +inf + +Evaluation for huge arguments:: + + >>> hyp2f1((-1,3), 1.75, 4, '1e100') + (7.883714220959876246415651e+32 + 1.365499358305579597618785e+33j) + >>> hyp2f1((-1,3), 1.75, 4, '1e1000000') + (7.883714220959876246415651e+333332 + 1.365499358305579597618785e+333333j) + >>> hyp2f1((-1,3), 1.75, 4, '1e1000000j') + (1.365499358305579597618785e+333333 - 7.883714220959876246415651e+333332j) + +An integral representation:: + + >>> a,b,c,z = -0.5, 1, 2.5, 0.25 + >>> g = lambda t: t**(b-1) * (1-t)**(c-b-1) * (1-t*z)**(-a) + >>> gammaprod([c],[b,c-b]) * quad(g, [0,1]) + 0.9480458814362824478852618 + >>> hyp2f1(a,b,c,z) + 0.9480458814362824478852618 + +Verifying the hypergeometric differential equation:: + + >>> f = lambda z: hyp2f1(a,b,c,z) + >>> chop(z*(1-z)*diff(f,z,2) + (c-(a+b+1)*z)*diff(f,z) - a*b*f(z)) + 0.0 + +""" + +hyp3f2 = r""" +Gives the generalized hypergeometric function `\,_3F_2`, defined for `|z| < 1` +as + +.. math :: + + \,_3F_2(a_1,a_2,a_3,b_1,b_2,z) = \sum_{k=0}^{\infty} + \frac{(a_1)_k (a_2)_k (a_3)_k}{(b_1)_k (b_2)_k} \frac{z^k}{k!}. + +and for `|z| \ge 1` by analytic continuation. The analytic structure of this +function is similar to that of `\,_2F_1`, generally with a singularity at +`z = 1` and a branch cut on `(1, \infty)`. + +Evaluation is supported inside, on, and outside +the circle of convergence `|z| = 1`:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> hyp3f2(1,2,3,4,5,0.25) + 1.083533123380934241548707 + >>> hyp3f2(1,2+2j,3,4,5,-10+10j) + (0.1574651066006004632914361 - 0.03194209021885226400892963j) + >>> hyp3f2(1,2,3,4,5,-10) + 0.3071141169208772603266489 + >>> hyp3f2(1,2,3,4,5,10) + (-0.4857045320523947050581423 - 0.5988311440454888436888028j) + >>> hyp3f2(0.25,1,1,2,1.5,1) + 1.157370995096772047567631 + >>> (8-pi-2*ln2)/3 + 1.157370995096772047567631 + >>> hyp3f2(1+j,0.5j,2,1,-2j,-1) + (1.74518490615029486475959 + 0.1454701525056682297614029j) + >>> hyp3f2(1+j,0.5j,2,1,-2j,sqrt(j)) + (0.9829816481834277511138055 - 0.4059040020276937085081127j) + >>> hyp3f2(-3,2,1,-5,4,1) + 1.41 + >>> hyp3f2(-3,2,1,-5,4,2) + 2.12 + +Evaluation very close to the unit circle:: + + >>> hyp3f2(1,2,3,4,5,'1.0001') + (1.564877796743282766872279 - 3.76821518787438186031973e-11j) + >>> hyp3f2(1,2,3,4,5,'1+0.0001j') + (1.564747153061671573212831 + 0.0001305757570366084557648482j) + >>> hyp3f2(1,2,3,4,5,'0.9999') + 1.564616644881686134983664 + >>> hyp3f2(1,2,3,4,5,'-0.9999') + 0.7823896253461678060196207 + +.. note :: + + Evaluation for `|z-1|` small can currently be inaccurate or slow + for some parameter combinations. + +For various parameter combinations, `\,_3F_2` admits representation in terms +of hypergeometric functions of lower degree, or in terms of +simpler functions:: + + >>> for a, b, z in [(1,2,-1), (2,0.5,1)]: + ... hyp2f1(a,b,a+b+0.5,z)**2 + ... hyp3f2(2*a,a+b,2*b,a+b+0.5,2*a+2*b,z) + ... + 0.4246104461966439006086308 + 0.4246104461966439006086308 + 7.111111111111111111111111 + 7.111111111111111111111111 + + >>> z = 2+3j + >>> hyp3f2(0.5,1,1.5,2,2,z) + (0.7621440939243342419729144 + 0.4249117735058037649915723j) + >>> 4*(pi-2*ellipe(z))/(pi*z) + (0.7621440939243342419729144 + 0.4249117735058037649915723j) + +""" + +hyperu = r""" +Gives the Tricomi confluent hypergeometric function `U`, also known as +the Kummer or confluent hypergeometric function of the second kind. This +function gives a second linearly independent solution to the confluent +hypergeometric differential equation (the first is provided by `\,_1F_1` -- +see :func:`~mpmath.hyp1f1`). + +**Examples** + +Evaluation for arbitrary complex arguments:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> hyperu(2,3,4) + 0.0625 + >>> hyperu(0.25, 5, 1000) + 0.1779949416140579573763523 + >>> hyperu(0.25, 5, -1000) + (0.1256256609322773150118907 - 0.1256256609322773150118907j) + +The `U` function may be singular at `z = 0`:: + + >>> hyperu(1.5, 2, 0) + +inf + >>> hyperu(1.5, -2, 0) + 0.1719434921288400112603671 + +Verifying the differential equation:: + + >>> a, b = 1.5, 2 + >>> f = lambda z: hyperu(a,b,z) + >>> for z in [-10, 3, 3+4j]: + ... chop(z*diff(f,z,2) + (b-z)*diff(f,z) - a*f(z)) + ... + 0.0 + 0.0 + 0.0 + +An integral representation:: + + >>> a,b,z = 2, 3.5, 4.25 + >>> hyperu(a,b,z) + 0.06674960718150520648014567 + >>> quad(lambda t: exp(-z*t)*t**(a-1)*(1+t)**(b-a-1),[0,inf]) / gamma(a) + 0.06674960718150520648014567 + + +[1] http://people.math.sfu.ca/~cbm/aands/page_504.htm +""" + +hyp2f0 = r""" +Gives the hypergeometric function `\,_2F_0`, defined formally by the +series + +.. math :: + + \,_2F_0(a,b;;z) = \sum_{n=0}^{\infty} (a)_n (b)_n \frac{z^n}{n!}. + +This series usually does not converge. For small enough `z`, it can be viewed +as an asymptotic series that may be summed directly with an appropriate +truncation. When this is not the case, :func:`~mpmath.hyp2f0` gives a regularized sum, +or equivalently, it uses a representation in terms of the +hypergeometric U function [1]. The series also converges when either `a` or `b` +is a nonpositive integer, as it then terminates into a polynomial +after `-a` or `-b` terms. + +**Examples** + +Evaluation is supported for arbitrary complex arguments:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> hyp2f0((2,3), 1.25, -100) + 0.07095851870980052763312791 + >>> hyp2f0((2,3), 1.25, 100) + (-0.03254379032170590665041131 + 0.07269254613282301012735797j) + >>> hyp2f0(-0.75, 1-j, 4j) + (-0.3579987031082732264862155 - 3.052951783922142735255881j) + +Even with real arguments, the regularized value of 2F0 is often complex-valued, +but the imaginary part decreases exponentially as `z \to 0`. In the following +example, the first call uses complex evaluation while the second has a small +enough `z` to evaluate using the direct series and thus the returned value +is strictly real (this should be taken to indicate that the imaginary +part is less than ``eps``):: + + >>> mp.dps = 15 + >>> hyp2f0(1.5, 0.5, 0.05) + (1.04166637647907 + 8.34584913683906e-8j) + >>> hyp2f0(1.5, 0.5, 0.0005) + 1.00037535207621 + +The imaginary part can be retrieved by increasing the working precision:: + + >>> mp.dps = 80 + >>> nprint(hyp2f0(1.5, 0.5, 0.009).imag) + 1.23828e-46 + +In the polynomial case (the series terminating), 2F0 can evaluate exactly:: + + >>> mp.dps = 15 + >>> hyp2f0(-6,-6,2) + 291793.0 + >>> identify(hyp2f0(-2,1,0.25)) + '(5/8)' + +The coefficients of the polynomials can be recovered using Taylor expansion:: + + >>> nprint(taylor(lambda x: hyp2f0(-3,0.5,x), 0, 10)) + [1.0, -1.5, 2.25, -1.875, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] + >>> nprint(taylor(lambda x: hyp2f0(-4,0.5,x), 0, 10)) + [1.0, -2.0, 4.5, -7.5, 6.5625, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] + + +[1] http://people.math.sfu.ca/~cbm/aands/page_504.htm +""" + + +gammainc = r""" +``gammainc(z, a=0, b=inf)`` computes the (generalized) incomplete +gamma function with integration limits `[a, b]`: + +.. math :: + + \Gamma(z,a,b) = \int_a^b t^{z-1} e^{-t} \, dt + +The generalized incomplete gamma function reduces to the +following special cases when one or both endpoints are fixed: + +* `\Gamma(z,0,\infty)` is the standard ("complete") + gamma function, `\Gamma(z)` (available directly + as the mpmath function :func:`~mpmath.gamma`) +* `\Gamma(z,a,\infty)` is the "upper" incomplete gamma + function, `\Gamma(z,a)` +* `\Gamma(z,0,b)` is the "lower" incomplete gamma + function, `\gamma(z,b)`. + +Of course, we have +`\Gamma(z,0,x) + \Gamma(z,x,\infty) = \Gamma(z)` +for all `z` and `x`. + +Note however that some authors reverse the order of the +arguments when defining the lower and upper incomplete +gamma function, so one should be careful to get the correct +definition. + +If also given the keyword argument ``regularized=True``, +:func:`~mpmath.gammainc` computes the "regularized" incomplete gamma +function + +.. math :: + + P(z,a,b) = \frac{\Gamma(z,a,b)}{\Gamma(z)}. + +**Examples** + +We can compare with numerical quadrature to verify that +:func:`~mpmath.gammainc` computes the integral in the definition:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> gammainc(2+3j, 4, 10) + (0.00977212668627705160602312 - 0.0770637306312989892451977j) + >>> quad(lambda t: t**(2+3j-1) * exp(-t), [4, 10]) + (0.00977212668627705160602312 - 0.0770637306312989892451977j) + +Argument symmetries follow directly from the integral definition:: + + >>> gammainc(3, 4, 5) + gammainc(3, 5, 4) + 0.0 + >>> gammainc(3,0,2) + gammainc(3,2,4); gammainc(3,0,4) + 1.523793388892911312363331 + 1.523793388892911312363331 + >>> findroot(lambda z: gammainc(2,z,3), 1) + 3.0 + +Evaluation for arbitrarily large arguments:: + + >>> gammainc(10, 100) + 4.083660630910611272288592e-26 + >>> gammainc(10, 10000000000000000) + 5.290402449901174752972486e-4342944819032375 + >>> gammainc(3+4j, 1000000+1000000j) + (-1.257913707524362408877881e-434284 + 2.556691003883483531962095e-434284j) + +Evaluation of a generalized incomplete gamma function automatically chooses +the representation that gives a more accurate result, depending on which +parameter is larger:: + + >>> gammainc(10000000, 3) - gammainc(10000000, 2) # Bad + 0.0 + >>> gammainc(10000000, 2, 3) # Good + 1.755146243738946045873491e+4771204 + >>> gammainc(2, 0, 100000001) - gammainc(2, 0, 100000000) # Bad + 0.0 + >>> gammainc(2, 100000000, 100000001) # Good + 4.078258353474186729184421e-43429441 + +The incomplete gamma functions satisfy simple recurrence +relations:: + + >>> mp.dps = 25 + >>> z, a = mpf(3.5), mpf(2) + >>> gammainc(z+1, a); z*gammainc(z,a) + a**z*exp(-a) + 10.60130296933533459267329 + 10.60130296933533459267329 + >>> gammainc(z+1,0,a); z*gammainc(z,0,a) - a**z*exp(-a) + 1.030425427232114336470932 + 1.030425427232114336470932 + +Evaluation at integers and poles:: + + >>> gammainc(-3, -4, -5) + (-0.2214577048967798566234192 + 0.0j) + >>> gammainc(-3, 0, 5) + +inf + +If `z` is an integer, the recurrence reduces the incomplete gamma +function to `P(a) \exp(-a) + Q(b) \exp(-b)` where `P` and +`Q` are polynomials:: + + >>> gammainc(1, 2); exp(-2) + 0.1353352832366126918939995 + 0.1353352832366126918939995 + >>> mp.dps = 50 + >>> identify(gammainc(6, 1, 2), ['exp(-1)', 'exp(-2)']) + '(326*exp(-1) + (-872)*exp(-2))' + +The incomplete gamma functions reduce to functions such as +the exponential integral Ei and the error function for special +arguments:: + + >>> mp.dps = 25 + >>> gammainc(0, 4); -ei(-4) + 0.00377935240984890647887486 + 0.00377935240984890647887486 + >>> gammainc(0.5, 0, 2); sqrt(pi)*erf(sqrt(2)) + 1.691806732945198336509541 + 1.691806732945198336509541 + +""" + +erf = r""" +Computes the error function, `\mathrm{erf}(x)`. The error +function is the normalized antiderivative of the Gaussian function +`\exp(-t^2)`. More precisely, + +.. math:: + + \mathrm{erf}(x) = \frac{2}{\sqrt \pi} \int_0^x \exp(-t^2) \,dt + +**Basic examples** + +Simple values and limits include:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> erf(0) + 0.0 + >>> erf(1) + 0.842700792949715 + >>> erf(-1) + -0.842700792949715 + >>> erf(inf) + 1.0 + >>> erf(-inf) + -1.0 + +For large real `x`, `\mathrm{erf}(x)` approaches 1 very +rapidly:: + + >>> erf(3) + 0.999977909503001 + >>> erf(5) + 0.999999999998463 + +The error function is an odd function:: + + >>> nprint(chop(taylor(erf, 0, 5))) + [0.0, 1.12838, 0.0, -0.376126, 0.0, 0.112838] + +:func:`~mpmath.erf` implements arbitrary-precision evaluation and +supports complex numbers:: + + >>> mp.dps = 50 + >>> erf(0.5) + 0.52049987781304653768274665389196452873645157575796 + >>> mp.dps = 25 + >>> erf(1+j) + (1.316151281697947644880271 + 0.1904534692378346862841089j) + +Evaluation is supported for large arguments:: + + >>> mp.dps = 25 + >>> erf('1e1000') + 1.0 + >>> erf('-1e1000') + -1.0 + >>> erf('1e-1000') + 1.128379167095512573896159e-1000 + >>> erf('1e7j') + (0.0 + 8.593897639029319267398803e+43429448190317j) + >>> erf('1e7+1e7j') + (0.9999999858172446172631323 + 3.728805278735270407053139e-8j) + +**Related functions** + +See also :func:`~mpmath.erfc`, which is more accurate for large `x`, +and :func:`~mpmath.erfi` which gives the antiderivative of +`\exp(t^2)`. + +The Fresnel integrals :func:`~mpmath.fresnels` and :func:`~mpmath.fresnelc` +are also related to the error function. +""" + +erfc = r""" +Computes the complementary error function, +`\mathrm{erfc}(x) = 1-\mathrm{erf}(x)`. +This function avoids cancellation that occurs when naively +computing the complementary error function as ``1-erf(x)``:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> 1 - erf(10) + 0.0 + >>> erfc(10) + 2.08848758376254e-45 + +:func:`~mpmath.erfc` works accurately even for ludicrously large +arguments:: + + >>> erfc(10**10) + 4.3504398860243e-43429448190325182776 + +Complex arguments are supported:: + + >>> erfc(500+50j) + (1.19739830969552e-107492 + 1.46072418957528e-107491j) + +""" + + +erfi = r""" +Computes the imaginary error function, `\mathrm{erfi}(x)`. +The imaginary error function is defined in analogy with the +error function, but with a positive sign in the integrand: + +.. math :: + + \mathrm{erfi}(x) = \frac{2}{\sqrt \pi} \int_0^x \exp(t^2) \,dt + +Whereas the error function rapidly converges to 1 as `x` grows, +the imaginary error function rapidly diverges to infinity. +The functions are related as +`\mathrm{erfi}(x) = -i\,\mathrm{erf}(ix)` for all complex +numbers `x`. + +**Examples** + +Basic values and limits:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> erfi(0) + 0.0 + >>> erfi(1) + 1.65042575879754 + >>> erfi(-1) + -1.65042575879754 + >>> erfi(inf) + +inf + >>> erfi(-inf) + -inf + +Note the symmetry between erf and erfi:: + + >>> erfi(3j) + (0.0 + 0.999977909503001j) + >>> erf(3) + 0.999977909503001 + >>> erf(1+2j) + (-0.536643565778565 - 5.04914370344703j) + >>> erfi(2+1j) + (-5.04914370344703 - 0.536643565778565j) + +Large arguments are supported:: + + >>> erfi(1000) + 1.71130938718796e+434291 + >>> erfi(10**10) + 7.3167287567024e+43429448190325182754 + >>> erfi(-10**10) + -7.3167287567024e+43429448190325182754 + >>> erfi(1000-500j) + (2.49895233563961e+325717 + 2.6846779342253e+325717j) + >>> erfi(100000j) + (0.0 + 1.0j) + >>> erfi(-100000j) + (0.0 - 1.0j) + + +""" + +erfinv = r""" +Computes the inverse error function, satisfying + +.. math :: + + \mathrm{erf}(\mathrm{erfinv}(x)) = + \mathrm{erfinv}(\mathrm{erf}(x)) = x. + +This function is defined only for `-1 \le x \le 1`. + +**Examples** + +Special values include:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> erfinv(0) + 0.0 + >>> erfinv(1) + +inf + >>> erfinv(-1) + -inf + +The domain is limited to the standard interval:: + + >>> erfinv(2) + Traceback (most recent call last): + ... + ValueError: erfinv(x) is defined only for -1 <= x <= 1 + +It is simple to check that :func:`~mpmath.erfinv` computes inverse values of +:func:`~mpmath.erf` as promised:: + + >>> erf(erfinv(0.75)) + 0.75 + >>> erf(erfinv(-0.995)) + -0.995 + +:func:`~mpmath.erfinv` supports arbitrary-precision evaluation:: + + >>> mp.dps = 50 + >>> x = erf(2) + >>> x + 0.99532226501895273416206925636725292861089179704006 + >>> erfinv(x) + 2.0 + +A definite integral involving the inverse error function:: + + >>> mp.dps = 15 + >>> quad(erfinv, [0, 1]) + 0.564189583547756 + >>> 1/sqrt(pi) + 0.564189583547756 + +The inverse error function can be used to generate random numbers +with a Gaussian distribution (although this is a relatively +inefficient algorithm):: + + >>> nprint([erfinv(2*rand()-1) for n in range(6)]) # doctest: +SKIP + [-0.586747, 1.10233, -0.376796, 0.926037, -0.708142, -0.732012] + +""" + +npdf = r""" +``npdf(x, mu=0, sigma=1)`` evaluates the probability density +function of a normal distribution with mean value `\mu` +and variance `\sigma^2`. + +Elementary properties of the probability distribution can +be verified using numerical integration:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> quad(npdf, [-inf, inf]) + 1.0 + >>> quad(lambda x: npdf(x, 3), [3, inf]) + 0.5 + >>> quad(lambda x: npdf(x, 3, 2), [3, inf]) + 0.5 + +See also :func:`~mpmath.ncdf`, which gives the cumulative +distribution. +""" + +ncdf = r""" +``ncdf(x, mu=0, sigma=1)`` evaluates the cumulative distribution +function of a normal distribution with mean value `\mu` +and variance `\sigma^2`. + +See also :func:`~mpmath.npdf`, which gives the probability density. + +Elementary properties include:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> ncdf(pi, mu=pi) + 0.5 + >>> ncdf(-inf) + 0.0 + >>> ncdf(+inf) + 1.0 + +The cumulative distribution is the integral of the density +function having identical mu and sigma:: + + >>> mp.dps = 15 + >>> diff(ncdf, 2) + 0.053990966513188 + >>> npdf(2) + 0.053990966513188 + >>> diff(lambda x: ncdf(x, 1, 0.5), 0) + 0.107981933026376 + >>> npdf(0, 1, 0.5) + 0.107981933026376 +""" + +expint = r""" +:func:`~mpmath.expint(n,z)` gives the generalized exponential integral +or En-function, + +.. math :: + + \mathrm{E}_n(z) = \int_1^{\infty} \frac{e^{-zt}}{t^n} dt, + +where `n` and `z` may both be complex numbers. The case with `n = 1` is +also given by :func:`~mpmath.e1`. + +**Examples** + +Evaluation at real and complex arguments:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> expint(1, 6.25) + 0.0002704758872637179088496194 + >>> expint(-3, 2+3j) + (0.00299658467335472929656159 + 0.06100816202125885450319632j) + >>> expint(2+3j, 4-5j) + (0.001803529474663565056945248 - 0.002235061547756185403349091j) + +At negative integer values of `n`, `E_n(z)` reduces to a +rational-exponential function:: + + >>> f = lambda n, z: fac(n)*sum(z**k/fac(k-1) for k in range(1,n+2))/\ + ... exp(z)/z**(n+2) + >>> n = 3 + >>> z = 1/pi + >>> expint(-n,z) + 584.2604820613019908668219 + >>> f(n,z) + 584.2604820613019908668219 + >>> n = 5 + >>> expint(-n,z) + 115366.5762594725451811138 + >>> f(n,z) + 115366.5762594725451811138 +""" + +e1 = r""" +Computes the exponential integral `\mathrm{E}_1(z)`, given by + +.. math :: + + \mathrm{E}_1(z) = \int_z^{\infty} \frac{e^{-t}}{t} dt. + +This is equivalent to :func:`~mpmath.expint` with `n = 1`. + +**Examples** + +Two ways to evaluate this function:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> e1(6.25) + 0.0002704758872637179088496194 + >>> expint(1,6.25) + 0.0002704758872637179088496194 + +The E1-function is essentially the same as the Ei-function (:func:`~mpmath.ei`) +with negated argument, except for an imaginary branch cut term:: + + >>> e1(2.5) + 0.02491491787026973549562801 + >>> -ei(-2.5) + 0.02491491787026973549562801 + >>> e1(-2.5) + (-7.073765894578600711923552 - 3.141592653589793238462643j) + >>> -ei(2.5) + -7.073765894578600711923552 + +""" + +ei = r""" +Computes the exponential integral or Ei-function, `\mathrm{Ei}(x)`. +The exponential integral is defined as + +.. math :: + + \mathrm{Ei}(x) = \int_{-\infty\,}^x \frac{e^t}{t} \, dt. + +When the integration range includes `t = 0`, the exponential +integral is interpreted as providing the Cauchy principal value. + +For real `x`, the Ei-function behaves roughly like +`\mathrm{Ei}(x) \approx \exp(x) + \log(|x|)`. + +The Ei-function is related to the more general family of exponential +integral functions denoted by `E_n`, which are available as :func:`~mpmath.expint`. + +**Basic examples** + +Some basic values and limits are:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> ei(0) + -inf + >>> ei(1) + 1.89511781635594 + >>> ei(inf) + +inf + >>> ei(-inf) + 0.0 + +For `x < 0`, the defining integral can be evaluated +numerically as a reference:: + + >>> ei(-4) + -0.00377935240984891 + >>> quad(lambda t: exp(t)/t, [-inf, -4]) + -0.00377935240984891 + +:func:`~mpmath.ei` supports complex arguments and arbitrary +precision evaluation:: + + >>> mp.dps = 50 + >>> ei(pi) + 10.928374389331410348638445906907535171566338835056 + >>> mp.dps = 25 + >>> ei(3+4j) + (-4.154091651642689822535359 + 4.294418620024357476985535j) + +**Related functions** + +The exponential integral is closely related to the logarithmic +integral. See :func:`~mpmath.li` for additional information. + +The exponential integral is related to the hyperbolic +and trigonometric integrals (see :func:`~mpmath.chi`, :func:`~mpmath.shi`, +:func:`~mpmath.ci`, :func:`~mpmath.si`) similarly to how the ordinary +exponential function is related to the hyperbolic and +trigonometric functions:: + + >>> mp.dps = 15 + >>> ei(3) + 9.93383257062542 + >>> chi(3) + shi(3) + 9.93383257062542 + >>> chop(ci(3j) - j*si(3j) - pi*j/2) + 9.93383257062542 + +Beware that logarithmic corrections, as in the last example +above, are required to obtain the correct branch in general. +For details, see [1]. + +The exponential integral is also a special case of the +hypergeometric function `\,_2F_2`:: + + >>> z = 0.6 + >>> z*hyper([1,1],[2,2],z) + (ln(z)-ln(1/z))/2 + euler + 0.769881289937359 + >>> ei(z) + 0.769881289937359 + +**References** + +1. Relations between Ei and other functions: + http://functions.wolfram.com/GammaBetaErf/ExpIntegralEi/27/01/ + +2. Abramowitz & Stegun, section 5: + http://people.math.sfu.ca/~cbm/aands/page_228.htm + +3. Asymptotic expansion for Ei: + http://mathworld.wolfram.com/En-Function.html +""" + +li = r""" +Computes the logarithmic integral or li-function +`\mathrm{li}(x)`, defined by + +.. math :: + + \mathrm{li}(x) = \int_0^x \frac{1}{\log t} \, dt + +The logarithmic integral has a singularity at `x = 1`. + +Alternatively, ``li(x, offset=True)`` computes the offset +logarithmic integral (used in number theory) + +.. math :: + + \mathrm{Li}(x) = \int_2^x \frac{1}{\log t} \, dt. + +These two functions are related via the simple identity +`\mathrm{Li}(x) = \mathrm{li}(x) - \mathrm{li}(2)`. + +The logarithmic integral should also not be confused with +the polylogarithm (also denoted by Li), which is implemented +as :func:`~mpmath.polylog`. + +**Examples** + +Some basic values and limits:: + + >>> from mpmath import * + >>> mp.dps = 30; mp.pretty = True + >>> li(0) + 0.0 + >>> li(1) + -inf + >>> li(1) + -inf + >>> li(2) + 1.04516378011749278484458888919 + >>> findroot(li, 2) + 1.45136923488338105028396848589 + >>> li(inf) + +inf + >>> li(2, offset=True) + 0.0 + >>> li(1, offset=True) + -inf + >>> li(0, offset=True) + -1.04516378011749278484458888919 + >>> li(10, offset=True) + 5.12043572466980515267839286347 + +The logarithmic integral can be evaluated for arbitrary +complex arguments:: + + >>> mp.dps = 20 + >>> li(3+4j) + (3.1343755504645775265 + 2.6769247817778742392j) + +The logarithmic integral is related to the exponential integral:: + + >>> ei(log(3)) + 2.1635885946671919729 + >>> li(3) + 2.1635885946671919729 + +The logarithmic integral grows like `O(x/\log(x))`:: + + >>> mp.dps = 15 + >>> x = 10**100 + >>> x/log(x) + 4.34294481903252e+97 + >>> li(x) + 4.3619719871407e+97 + +The prime number theorem states that the number of primes less +than `x` is asymptotic to `\mathrm{Li}(x)` (equivalently +`\mathrm{li}(x)`). For example, it is known that there are +exactly 1,925,320,391,606,803,968,923 prime numbers less than +`10^{23}` [1]. The logarithmic integral provides a very +accurate estimate:: + + >>> li(10**23, offset=True) + 1.92532039161405e+21 + +A definite integral is:: + + >>> quad(li, [0, 1]) + -0.693147180559945 + >>> -ln(2) + -0.693147180559945 + +**References** + +1. http://mathworld.wolfram.com/PrimeCountingFunction.html + +2. http://mathworld.wolfram.com/LogarithmicIntegral.html + +""" + +ci = r""" +Computes the cosine integral, + +.. math :: + + \mathrm{Ci}(x) = -\int_x^{\infty} \frac{\cos t}{t}\,dt + = \gamma + \log x + \int_0^x \frac{\cos t - 1}{t}\,dt + +**Examples** + +Some values and limits:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> ci(0) + -inf + >>> ci(1) + 0.3374039229009681346626462 + >>> ci(pi) + 0.07366791204642548599010096 + >>> ci(inf) + 0.0 + >>> ci(-inf) + (0.0 + 3.141592653589793238462643j) + >>> ci(2+3j) + (1.408292501520849518759125 - 2.983617742029605093121118j) + +The cosine integral behaves roughly like the sinc function +(see :func:`~mpmath.sinc`) for large real `x`:: + + >>> ci(10**10) + -4.875060251748226537857298e-11 + >>> sinc(10**10) + -4.875060250875106915277943e-11 + >>> chop(limit(ci, inf)) + 0.0 + +It has infinitely many roots on the positive real axis:: + + >>> findroot(ci, 1) + 0.6165054856207162337971104 + >>> findroot(ci, 2) + 3.384180422551186426397851 + +Evaluation is supported for `z` anywhere in the complex plane:: + + >>> ci(10**6*(1+j)) + (4.449410587611035724984376e+434287 + 9.75744874290013526417059e+434287j) + +We can evaluate the defining integral as a reference:: + + >>> mp.dps = 15 + >>> -quadosc(lambda t: cos(t)/t, [5, inf], omega=1) + -0.190029749656644 + >>> ci(5) + -0.190029749656644 + +Some infinite series can be evaluated using the +cosine integral:: + + >>> nsum(lambda k: (-1)**k/(fac(2*k)*(2*k)), [1,inf]) + -0.239811742000565 + >>> ci(1) - euler + -0.239811742000565 + +""" + +si = r""" +Computes the sine integral, + +.. math :: + + \mathrm{Si}(x) = \int_0^x \frac{\sin t}{t}\,dt. + +The sine integral is thus the antiderivative of the sinc +function (see :func:`~mpmath.sinc`). + +**Examples** + +Some values and limits:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> si(0) + 0.0 + >>> si(1) + 0.9460830703671830149413533 + >>> si(-1) + -0.9460830703671830149413533 + >>> si(pi) + 1.851937051982466170361053 + >>> si(inf) + 1.570796326794896619231322 + >>> si(-inf) + -1.570796326794896619231322 + >>> si(2+3j) + (4.547513889562289219853204 + 1.399196580646054789459839j) + +The sine integral approaches `\pi/2` for large real `x`:: + + >>> si(10**10) + 1.570796326707584656968511 + >>> pi/2 + 1.570796326794896619231322 + +Evaluation is supported for `z` anywhere in the complex plane:: + + >>> si(10**6*(1+j)) + (-9.75744874290013526417059e+434287 + 4.449410587611035724984376e+434287j) + +We can evaluate the defining integral as a reference:: + + >>> mp.dps = 15 + >>> quad(sinc, [0, 5]) + 1.54993124494467 + >>> si(5) + 1.54993124494467 + +Some infinite series can be evaluated using the +sine integral:: + + >>> nsum(lambda k: (-1)**k/(fac(2*k+1)*(2*k+1)), [0,inf]) + 0.946083070367183 + >>> si(1) + 0.946083070367183 + +""" + +chi = r""" +Computes the hyperbolic cosine integral, defined +in analogy with the cosine integral (see :func:`~mpmath.ci`) as + +.. math :: + + \mathrm{Chi}(x) = -\int_x^{\infty} \frac{\cosh t}{t}\,dt + = \gamma + \log x + \int_0^x \frac{\cosh t - 1}{t}\,dt + +Some values and limits:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> chi(0) + -inf + >>> chi(1) + 0.8378669409802082408946786 + >>> chi(inf) + +inf + >>> findroot(chi, 0.5) + 0.5238225713898644064509583 + >>> chi(2+3j) + (-0.1683628683277204662429321 + 2.625115880451325002151688j) + +Evaluation is supported for `z` anywhere in the complex plane:: + + >>> chi(10**6*(1+j)) + (4.449410587611035724984376e+434287 - 9.75744874290013526417059e+434287j) + +""" + +shi = r""" +Computes the hyperbolic sine integral, defined +in analogy with the sine integral (see :func:`~mpmath.si`) as + +.. math :: + + \mathrm{Shi}(x) = \int_0^x \frac{\sinh t}{t}\,dt. + +Some values and limits:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> shi(0) + 0.0 + >>> shi(1) + 1.057250875375728514571842 + >>> shi(-1) + -1.057250875375728514571842 + >>> shi(inf) + +inf + >>> shi(2+3j) + (-0.1931890762719198291678095 + 2.645432555362369624818525j) + +Evaluation is supported for `z` anywhere in the complex plane:: + + >>> shi(10**6*(1+j)) + (4.449410587611035724984376e+434287 - 9.75744874290013526417059e+434287j) + +""" + +fresnels = r""" +Computes the Fresnel sine integral + +.. math :: + + S(x) = \int_0^x \sin\left(\frac{\pi t^2}{2}\right) \,dt + +Note that some sources define this function +without the normalization factor `\pi/2`. + +**Examples** + +Some basic values and limits:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> fresnels(0) + 0.0 + >>> fresnels(inf) + 0.5 + >>> fresnels(-inf) + -0.5 + >>> fresnels(1) + 0.4382591473903547660767567 + >>> fresnels(1+2j) + (36.72546488399143842838788 + 15.58775110440458732748279j) + +Comparing with the definition:: + + >>> fresnels(3) + 0.4963129989673750360976123 + >>> quad(lambda t: sin(pi*t**2/2), [0,3]) + 0.4963129989673750360976123 +""" + +fresnelc = r""" +Computes the Fresnel cosine integral + +.. math :: + + C(x) = \int_0^x \cos\left(\frac{\pi t^2}{2}\right) \,dt + +Note that some sources define this function +without the normalization factor `\pi/2`. + +**Examples** + +Some basic values and limits:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> fresnelc(0) + 0.0 + >>> fresnelc(inf) + 0.5 + >>> fresnelc(-inf) + -0.5 + >>> fresnelc(1) + 0.7798934003768228294742064 + >>> fresnelc(1+2j) + (16.08787137412548041729489 - 36.22568799288165021578758j) + +Comparing with the definition:: + + >>> fresnelc(3) + 0.6057207892976856295561611 + >>> quad(lambda t: cos(pi*t**2/2), [0,3]) + 0.6057207892976856295561611 +""" + +airyai = r""" +Computes the Airy function `\operatorname{Ai}(z)`, which is +the solution of the Airy differential equation `f''(z) - z f(z) = 0` +with initial conditions + +.. math :: + + \operatorname{Ai}(0) = + \frac{1}{3^{2/3}\Gamma\left(\frac{2}{3}\right)} + + \operatorname{Ai}'(0) = + -\frac{1}{3^{1/3}\Gamma\left(\frac{1}{3}\right)}. + +Other common ways of defining the Ai-function include +integrals such as + +.. math :: + + \operatorname{Ai}(x) = \frac{1}{\pi} + \int_0^{\infty} \cos\left(\frac{1}{3}t^3+xt\right) dt + \qquad x \in \mathbb{R} + + \operatorname{Ai}(z) = \frac{\sqrt{3}}{2\pi} + \int_0^{\infty} + \exp\left(-\frac{t^3}{3}-\frac{z^3}{3t^3}\right) dt. + +The Ai-function is an entire function with a turning point, +behaving roughly like a slowly decaying sine wave for `z < 0` and +like a rapidly decreasing exponential for `z > 0`. +A second solution of the Airy differential equation +is given by `\operatorname{Bi}(z)` (see :func:`~mpmath.airybi`). + +Optionally, with *derivative=alpha*, :func:`airyai` can compute the +`\alpha`-th order fractional derivative with respect to `z`. +For `\alpha = n = 1,2,3,\ldots` this gives the derivative +`\operatorname{Ai}^{(n)}(z)`, and for `\alpha = -n = -1,-2,-3,\ldots` +this gives the `n`-fold iterated integral + +.. math :: + + f_0(z) = \operatorname{Ai}(z) + + f_n(z) = \int_0^z f_{n-1}(t) dt. + +The Ai-function has infinitely many zeros, all located along the +negative half of the real axis. They can be computed with +:func:`~mpmath.airyaizero`. + +**Plots** + +.. literalinclude :: /plots/ai.py +.. image :: /plots/ai.png +.. literalinclude :: /plots/ai_c.py +.. image :: /plots/ai_c.png + +**Basic examples** + +Limits and values include:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> airyai(0); 1/(power(3,'2/3')*gamma('2/3')) + 0.3550280538878172392600632 + 0.3550280538878172392600632 + >>> airyai(1) + 0.1352924163128814155241474 + >>> airyai(-1) + 0.5355608832923521187995166 + >>> airyai(inf); airyai(-inf) + 0.0 + 0.0 + +Evaluation is supported for large magnitudes of the argument:: + + >>> airyai(-100) + 0.1767533932395528780908311 + >>> airyai(100) + 2.634482152088184489550553e-291 + >>> airyai(50+50j) + (-5.31790195707456404099817e-68 - 1.163588003770709748720107e-67j) + >>> airyai(-50+50j) + (1.041242537363167632587245e+158 + 3.347525544923600321838281e+157j) + +Huge arguments are also fine:: + + >>> airyai(10**10) + 1.162235978298741779953693e-289529654602171 + >>> airyai(-10**10) + 0.0001736206448152818510510181 + >>> w = airyai(10**10*(1+j)) + >>> w.real + 5.711508683721355528322567e-186339621747698 + >>> w.imag + 1.867245506962312577848166e-186339621747697 + +The first root of the Ai-function is:: + + >>> findroot(airyai, -2) + -2.338107410459767038489197 + >>> airyaizero(1) + -2.338107410459767038489197 + +**Properties and relations** + +Verifying the Airy differential equation:: + + >>> for z in [-3.4, 0, 2.5, 1+2j]: + ... chop(airyai(z,2) - z*airyai(z)) + ... + 0.0 + 0.0 + 0.0 + 0.0 + +The first few terms of the Taylor series expansion around `z = 0` +(every third term is zero):: + + >>> nprint(taylor(airyai, 0, 5)) + [0.355028, -0.258819, 0.0, 0.0591713, -0.0215683, 0.0] + +The Airy functions satisfy the Wronskian relation +`\operatorname{Ai}(z) \operatorname{Bi}'(z) - +\operatorname{Ai}'(z) \operatorname{Bi}(z) = 1/\pi`:: + + >>> z = -0.5 + >>> airyai(z)*airybi(z,1) - airyai(z,1)*airybi(z) + 0.3183098861837906715377675 + >>> 1/pi + 0.3183098861837906715377675 + +The Airy functions can be expressed in terms of Bessel +functions of order `\pm 1/3`. For `\Re[z] \le 0`, we have:: + + >>> z = -3 + >>> airyai(z) + -0.3788142936776580743472439 + >>> y = 2*power(-z,'3/2')/3 + >>> (sqrt(-z) * (besselj('1/3',y) + besselj('-1/3',y)))/3 + -0.3788142936776580743472439 + +**Derivatives and integrals** + +Derivatives of the Ai-function (directly and using :func:`~mpmath.diff`):: + + >>> airyai(-3,1); diff(airyai,-3) + 0.3145837692165988136507873 + 0.3145837692165988136507873 + >>> airyai(-3,2); diff(airyai,-3,2) + 1.136442881032974223041732 + 1.136442881032974223041732 + >>> airyai(1000,1); diff(airyai,1000) + -2.943133917910336090459748e-9156 + -2.943133917910336090459748e-9156 + +Several derivatives at `z = 0`:: + + >>> airyai(0,0); airyai(0,1); airyai(0,2) + 0.3550280538878172392600632 + -0.2588194037928067984051836 + 0.0 + >>> airyai(0,3); airyai(0,4); airyai(0,5) + 0.3550280538878172392600632 + -0.5176388075856135968103671 + 0.0 + >>> airyai(0,15); airyai(0,16); airyai(0,17) + 1292.30211615165475090663 + -3188.655054727379756351861 + 0.0 + +The integral of the Ai-function:: + + >>> airyai(3,-1); quad(airyai, [0,3]) + 0.3299203760070217725002701 + 0.3299203760070217725002701 + >>> airyai(-10,-1); quad(airyai, [0,-10]) + -0.765698403134212917425148 + -0.765698403134212917425148 + +Integrals of high or fractional order:: + + >>> airyai(-2,0.5); differint(airyai,-2,0.5,0) + (0.0 + 0.2453596101351438273844725j) + (0.0 + 0.2453596101351438273844725j) + >>> airyai(-2,-4); differint(airyai,-2,-4,0) + 0.2939176441636809580339365 + 0.2939176441636809580339365 + >>> airyai(0,-1); airyai(0,-2); airyai(0,-3) + 0.0 + 0.0 + 0.0 + +Integrals of the Ai-function can be evaluated at limit points:: + + >>> airyai(-1000000,-1); airyai(-inf,-1) + -0.6666843728311539978751512 + -0.6666666666666666666666667 + >>> airyai(10,-1); airyai(+inf,-1) + 0.3333333332991690159427932 + 0.3333333333333333333333333 + >>> airyai(+inf,-2); airyai(+inf,-3) + +inf + +inf + >>> airyai(-1000000,-2); airyai(-inf,-2) + 666666.4078472650651209742 + +inf + >>> airyai(-1000000,-3); airyai(-inf,-3) + -333333074513.7520264995733 + -inf + +**References** + +1. [DLMF]_ Chapter 9: Airy and Related Functions +2. [WolframFunctions]_ section: Bessel-Type Functions + +""" + +airybi = r""" +Computes the Airy function `\operatorname{Bi}(z)`, which is +the solution of the Airy differential equation `f''(z) - z f(z) = 0` +with initial conditions + +.. math :: + + \operatorname{Bi}(0) = + \frac{1}{3^{1/6}\Gamma\left(\frac{2}{3}\right)} + + \operatorname{Bi}'(0) = + \frac{3^{1/6}}{\Gamma\left(\frac{1}{3}\right)}. + +Like the Ai-function (see :func:`~mpmath.airyai`), the Bi-function +is oscillatory for `z < 0`, but it grows rather than decreases +for `z > 0`. + +Optionally, as for :func:`~mpmath.airyai`, derivatives, integrals +and fractional derivatives can be computed with the *derivative* +parameter. + +The Bi-function has infinitely many zeros along the negative +half-axis, as well as complex zeros, which can all be computed +with :func:`~mpmath.airybizero`. + +**Plots** + +.. literalinclude :: /plots/bi.py +.. image :: /plots/bi.png +.. literalinclude :: /plots/bi_c.py +.. image :: /plots/bi_c.png + +**Basic examples** + +Limits and values include:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> airybi(0); 1/(power(3,'1/6')*gamma('2/3')) + 0.6149266274460007351509224 + 0.6149266274460007351509224 + >>> airybi(1) + 1.207423594952871259436379 + >>> airybi(-1) + 0.10399738949694461188869 + >>> airybi(inf); airybi(-inf) + +inf + 0.0 + +Evaluation is supported for large magnitudes of the argument:: + + >>> airybi(-100) + 0.02427388768016013160566747 + >>> airybi(100) + 6.041223996670201399005265e+288 + >>> airybi(50+50j) + (-5.322076267321435669290334e+63 + 1.478450291165243789749427e+65j) + >>> airybi(-50+50j) + (-3.347525544923600321838281e+157 + 1.041242537363167632587245e+158j) + +Huge arguments:: + + >>> airybi(10**10) + 1.369385787943539818688433e+289529654602165 + >>> airybi(-10**10) + 0.001775656141692932747610973 + >>> w = airybi(10**10*(1+j)) + >>> w.real + -6.559955931096196875845858e+186339621747689 + >>> w.imag + -6.822462726981357180929024e+186339621747690 + +The first real root of the Bi-function is:: + + >>> findroot(airybi, -1); airybizero(1) + -1.17371322270912792491998 + -1.17371322270912792491998 + +**Properties and relations** + +Verifying the Airy differential equation:: + + >>> for z in [-3.4, 0, 2.5, 1+2j]: + ... chop(airybi(z,2) - z*airybi(z)) + ... + 0.0 + 0.0 + 0.0 + 0.0 + +The first few terms of the Taylor series expansion around `z = 0` +(every third term is zero):: + + >>> nprint(taylor(airybi, 0, 5)) + [0.614927, 0.448288, 0.0, 0.102488, 0.0373574, 0.0] + +The Airy functions can be expressed in terms of Bessel +functions of order `\pm 1/3`. For `\Re[z] \le 0`, we have:: + + >>> z = -3 + >>> airybi(z) + -0.1982896263749265432206449 + >>> p = 2*power(-z,'3/2')/3 + >>> sqrt(-mpf(z)/3)*(besselj('-1/3',p) - besselj('1/3',p)) + -0.1982896263749265432206449 + +**Derivatives and integrals** + +Derivatives of the Bi-function (directly and using :func:`~mpmath.diff`):: + + >>> airybi(-3,1); diff(airybi,-3) + -0.675611222685258537668032 + -0.675611222685258537668032 + >>> airybi(-3,2); diff(airybi,-3,2) + 0.5948688791247796296619346 + 0.5948688791247796296619346 + >>> airybi(1000,1); diff(airybi,1000) + 1.710055114624614989262335e+9156 + 1.710055114624614989262335e+9156 + +Several derivatives at `z = 0`:: + + >>> airybi(0,0); airybi(0,1); airybi(0,2) + 0.6149266274460007351509224 + 0.4482883573538263579148237 + 0.0 + >>> airybi(0,3); airybi(0,4); airybi(0,5) + 0.6149266274460007351509224 + 0.8965767147076527158296474 + 0.0 + >>> airybi(0,15); airybi(0,16); airybi(0,17) + 2238.332923903442675949357 + 5522.912562599140729510628 + 0.0 + +The integral of the Bi-function:: + + >>> airybi(3,-1); quad(airybi, [0,3]) + 10.06200303130620056316655 + 10.06200303130620056316655 + >>> airybi(-10,-1); quad(airybi, [0,-10]) + -0.01504042480614002045135483 + -0.01504042480614002045135483 + +Integrals of high or fractional order:: + + >>> airybi(-2,0.5); differint(airybi, -2, 0.5, 0) + (0.0 + 0.5019859055341699223453257j) + (0.0 + 0.5019859055341699223453257j) + >>> airybi(-2,-4); differint(airybi,-2,-4,0) + 0.2809314599922447252139092 + 0.2809314599922447252139092 + >>> airybi(0,-1); airybi(0,-2); airybi(0,-3) + 0.0 + 0.0 + 0.0 + +Integrals of the Bi-function can be evaluated at limit points:: + + >>> airybi(-1000000,-1); airybi(-inf,-1) + 0.000002191261128063434047966873 + 0.0 + >>> airybi(10,-1); airybi(+inf,-1) + 147809803.1074067161675853 + +inf + >>> airybi(+inf,-2); airybi(+inf,-3) + +inf + +inf + >>> airybi(-1000000,-2); airybi(-inf,-2) + 0.4482883750599908479851085 + 0.4482883573538263579148237 + >>> gamma('2/3')*power(3,'2/3')/(2*pi) + 0.4482883573538263579148237 + >>> airybi(-100000,-3); airybi(-inf,-3) + -44828.52827206932872493133 + -inf + >>> airybi(-100000,-4); airybi(-inf,-4) + 2241411040.437759489540248 + +inf + +""" + +airyaizero = r""" +Gives the `k`-th zero of the Airy Ai-function, +i.e. the `k`-th number `a_k` ordered by magnitude for which +`\operatorname{Ai}(a_k) = 0`. + +Optionally, with *derivative=1*, the corresponding +zero `a'_k` of the derivative function, i.e. +`\operatorname{Ai}'(a'_k) = 0`, is computed. + +**Examples** + +Some values of `a_k`:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> airyaizero(1) + -2.338107410459767038489197 + >>> airyaizero(2) + -4.087949444130970616636989 + >>> airyaizero(3) + -5.520559828095551059129856 + >>> airyaizero(1000) + -281.0315196125215528353364 + +Some values of `a'_k`:: + + >>> airyaizero(1,1) + -1.018792971647471089017325 + >>> airyaizero(2,1) + -3.248197582179836537875424 + >>> airyaizero(3,1) + -4.820099211178735639400616 + >>> airyaizero(1000,1) + -280.9378080358935070607097 + +Verification:: + + >>> chop(airyai(airyaizero(1))) + 0.0 + >>> chop(airyai(airyaizero(1,1),1)) + 0.0 + +""" + +airybizero = r""" +With *complex=False*, gives the `k`-th real zero of the Airy Bi-function, +i.e. the `k`-th number `b_k` ordered by magnitude for which +`\operatorname{Bi}(b_k) = 0`. + +With *complex=True*, gives the `k`-th complex zero in the upper +half plane `\beta_k`. Also the conjugate `\overline{\beta_k}` +is a zero. + +Optionally, with *derivative=1*, the corresponding +zero `b'_k` or `\beta'_k` of the derivative function, i.e. +`\operatorname{Bi}'(b'_k) = 0` or `\operatorname{Bi}'(\beta'_k) = 0`, +is computed. + +**Examples** + +Some values of `b_k`:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> airybizero(1) + -1.17371322270912792491998 + >>> airybizero(2) + -3.271093302836352715680228 + >>> airybizero(3) + -4.830737841662015932667709 + >>> airybizero(1000) + -280.9378112034152401578834 + +Some values of `b_k`:: + + >>> airybizero(1,1) + -2.294439682614123246622459 + >>> airybizero(2,1) + -4.073155089071828215552369 + >>> airybizero(3,1) + -5.512395729663599496259593 + >>> airybizero(1000,1) + -281.0315164471118527161362 + +Some values of `\beta_k`:: + + >>> airybizero(1,complex=True) + (0.9775448867316206859469927 + 2.141290706038744575749139j) + >>> airybizero(2,complex=True) + (1.896775013895336346627217 + 3.627291764358919410440499j) + >>> airybizero(3,complex=True) + (2.633157739354946595708019 + 4.855468179979844983174628j) + >>> airybizero(1000,complex=True) + (140.4978560578493018899793 + 243.3907724215792121244867j) + +Some values of `\beta'_k`:: + + >>> airybizero(1,1,complex=True) + (0.2149470745374305676088329 + 1.100600143302797880647194j) + >>> airybizero(2,1,complex=True) + (1.458168309223507392028211 + 2.912249367458445419235083j) + >>> airybizero(3,1,complex=True) + (2.273760763013482299792362 + 4.254528549217097862167015j) + >>> airybizero(1000,1,complex=True) + (140.4509972835270559730423 + 243.3096175398562811896208j) + +Verification:: + + >>> chop(airybi(airybizero(1))) + 0.0 + >>> chop(airybi(airybizero(1,1),1)) + 0.0 + >>> u = airybizero(1,complex=True) + >>> chop(airybi(u)) + 0.0 + >>> chop(airybi(conj(u))) + 0.0 + +The complex zeros (in the upper and lower half-planes respectively) +asymptotically approach the rays `z = R \exp(\pm i \pi /3)`:: + + >>> arg(airybizero(1,complex=True)) + 1.142532510286334022305364 + >>> arg(airybizero(1000,complex=True)) + 1.047271114786212061583917 + >>> arg(airybizero(1000000,complex=True)) + 1.047197624741816183341355 + >>> pi/3 + 1.047197551196597746154214 + +""" + + +ellipk = r""" +Evaluates the complete elliptic integral of the first kind, +`K(m)`, defined by + +.. math :: + + K(m) = \int_0^{\pi/2} \frac{dt}{\sqrt{1-m \sin^2 t}} \, = \, + \frac{\pi}{2} \,_2F_1\left(\frac{1}{2}, \frac{1}{2}, 1, m\right). + +Note that the argument is the parameter `m = k^2`, +not the modulus `k` which is sometimes used. + +**Plots** + +.. literalinclude :: /plots/ellipk.py +.. image :: /plots/ellipk.png + +**Examples** + +Values and limits include:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> ellipk(0) + 1.570796326794896619231322 + >>> ellipk(inf) + (0.0 + 0.0j) + >>> ellipk(-inf) + 0.0 + >>> ellipk(1) + +inf + >>> ellipk(-1) + 1.31102877714605990523242 + >>> ellipk(2) + (1.31102877714605990523242 - 1.31102877714605990523242j) + +Verifying the defining integral and hypergeometric +representation:: + + >>> ellipk(0.5) + 1.85407467730137191843385 + >>> quad(lambda t: (1-0.5*sin(t)**2)**-0.5, [0, pi/2]) + 1.85407467730137191843385 + >>> pi/2*hyp2f1(0.5,0.5,1,0.5) + 1.85407467730137191843385 + +Evaluation is supported for arbitrary complex `m`:: + + >>> ellipk(3+4j) + (0.9111955638049650086562171 + 0.6313342832413452438845091j) + +A definite integral:: + + >>> quad(ellipk, [0, 1]) + 2.0 +""" + +agm = r""" +``agm(a, b)`` computes the arithmetic-geometric mean of `a` and +`b`, defined as the limit of the following iteration: + +.. math :: + + a_0 = a + + b_0 = b + + a_{n+1} = \frac{a_n+b_n}{2} + + b_{n+1} = \sqrt{a_n b_n} + +This function can be called with a single argument, computing +`\mathrm{agm}(a,1) = \mathrm{agm}(1,a)`. + +**Examples** + +It is a well-known theorem that the geometric mean of +two distinct positive numbers is less than the arithmetic +mean. It follows that the arithmetic-geometric mean lies +between the two means:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> a = mpf(3) + >>> b = mpf(4) + >>> sqrt(a*b) + 3.46410161513775 + >>> agm(a,b) + 3.48202767635957 + >>> (a+b)/2 + 3.5 + +The arithmetic-geometric mean is scale-invariant:: + + >>> agm(10*e, 10*pi) + 29.261085515723 + >>> 10*agm(e, pi) + 29.261085515723 + +As an order-of-magnitude estimate, `\mathrm{agm}(1,x) \approx x` +for large `x`:: + + >>> agm(10**10) + 643448704.760133 + >>> agm(10**50) + 1.34814309345871e+48 + +For tiny `x`, `\mathrm{agm}(1,x) \approx -\pi/(2 \log(x/4))`:: + + >>> agm('0.01') + 0.262166887202249 + >>> -pi/2/log('0.0025') + 0.262172347753122 + +The arithmetic-geometric mean can also be computed for complex +numbers:: + + >>> agm(3, 2+j) + (2.51055133276184 + 0.547394054060638j) + +The AGM iteration converges very quickly (each step doubles +the number of correct digits), so :func:`~mpmath.agm` supports efficient +high-precision evaluation:: + + >>> mp.dps = 10000 + >>> a = agm(1,2) + >>> str(a)[-10:] + '1679581912' + +**Mathematical relations** + +The arithmetic-geometric mean may be used to evaluate the +following two parametric definite integrals: + +.. math :: + + I_1 = \int_0^{\infty} + \frac{1}{\sqrt{(x^2+a^2)(x^2+b^2)}} \,dx + + I_2 = \int_0^{\pi/2} + \frac{1}{\sqrt{a^2 \cos^2(x) + b^2 \sin^2(x)}} \,dx + +We have:: + + >>> mp.dps = 15 + >>> a = 3 + >>> b = 4 + >>> f1 = lambda x: ((x**2+a**2)*(x**2+b**2))**-0.5 + >>> f2 = lambda x: ((a*cos(x))**2 + (b*sin(x))**2)**-0.5 + >>> quad(f1, [0, inf]) + 0.451115405388492 + >>> quad(f2, [0, pi/2]) + 0.451115405388492 + >>> pi/(2*agm(a,b)) + 0.451115405388492 + +A formula for `\Gamma(1/4)`:: + + >>> gamma(0.25) + 3.62560990822191 + >>> sqrt(2*sqrt(2*pi**3)/agm(1,sqrt(2))) + 3.62560990822191 + +**Possible issues** + +The branch cut chosen for complex `a` and `b` is somewhat +arbitrary. + +""" + +gegenbauer = r""" +Evaluates the Gegenbauer polynomial, or ultraspherical polynomial, + +.. math :: + + C_n^{(a)}(z) = {n+2a-1 \choose n} \,_2F_1\left(-n, n+2a; + a+\frac{1}{2}; \frac{1}{2}(1-z)\right). + +When `n` is a nonnegative integer, this formula gives a polynomial +in `z` of degree `n`, but all parameters are permitted to be +complex numbers. With `a = 1/2`, the Gegenbauer polynomial +reduces to a Legendre polynomial. + +**Examples** + +Evaluation for arbitrary arguments:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> gegenbauer(3, 0.5, -10) + -2485.0 + >>> gegenbauer(1000, 10, 100) + 3.012757178975667428359374e+2322 + >>> gegenbauer(2+3j, -0.75, -1000j) + (-5038991.358609026523401901 + 9414549.285447104177860806j) + +Evaluation at negative integer orders:: + + >>> gegenbauer(-4, 2, 1.75) + -1.0 + >>> gegenbauer(-4, 3, 1.75) + 0.0 + >>> gegenbauer(-4, 2j, 1.75) + 0.0 + >>> gegenbauer(-7, 0.5, 3) + 8989.0 + +The Gegenbauer polynomials solve the differential equation:: + + >>> n, a = 4.5, 1+2j + >>> f = lambda z: gegenbauer(n, a, z) + >>> for z in [0, 0.75, -0.5j]: + ... chop((1-z**2)*diff(f,z,2) - (2*a+1)*z*diff(f,z) + n*(n+2*a)*f(z)) + ... + 0.0 + 0.0 + 0.0 + +The Gegenbauer polynomials have generating function +`(1-2zt+t^2)^{-a}`:: + + >>> a, z = 2.5, 1 + >>> taylor(lambda t: (1-2*z*t+t**2)**(-a), 0, 3) + [1.0, 5.0, 15.0, 35.0] + >>> [gegenbauer(n,a,z) for n in range(4)] + [1.0, 5.0, 15.0, 35.0] + +The Gegenbauer polynomials are orthogonal on `[-1, 1]` with respect +to the weight `(1-z^2)^{a-\frac{1}{2}}`:: + + >>> a, n, m = 2.5, 4, 5 + >>> Cn = lambda z: gegenbauer(n, a, z, zeroprec=1000) + >>> Cm = lambda z: gegenbauer(m, a, z, zeroprec=1000) + >>> chop(quad(lambda z: Cn(z)*Cm(z)*(1-z**2)*(a-0.5), [-1, 1])) + 0.0 +""" + +laguerre = r""" +Gives the generalized (associated) Laguerre polynomial, defined by + +.. math :: + + L_n^a(z) = \frac{\Gamma(n+b+1)}{\Gamma(b+1) \Gamma(n+1)} + \,_1F_1(-n, a+1, z). + +With `a = 0` and `n` a nonnegative integer, this reduces to an ordinary +Laguerre polynomial, the sequence of which begins +`L_0(z) = 1, L_1(z) = 1-z, L_2(z) = z^2-2z+1, \ldots`. + +The Laguerre polynomials are orthogonal with respect to the weight +`z^a e^{-z}` on `[0, \infty)`. + +**Plots** + +.. literalinclude :: /plots/laguerre.py +.. image :: /plots/laguerre.png + +**Examples** + +Evaluation for arbitrary arguments:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> laguerre(5, 0, 0.25) + 0.03726399739583333333333333 + >>> laguerre(1+j, 0.5, 2+3j) + (4.474921610704496808379097 - 11.02058050372068958069241j) + >>> laguerre(2, 0, 10000) + 49980001.0 + >>> laguerre(2.5, 0, 10000) + -9.327764910194842158583189e+4328 + +The first few Laguerre polynomials, normalized to have integer +coefficients:: + + >>> for n in range(7): + ... chop(taylor(lambda z: fac(n)*laguerre(n, 0, z), 0, n)) + ... + [1.0] + [1.0, -1.0] + [2.0, -4.0, 1.0] + [6.0, -18.0, 9.0, -1.0] + [24.0, -96.0, 72.0, -16.0, 1.0] + [120.0, -600.0, 600.0, -200.0, 25.0, -1.0] + [720.0, -4320.0, 5400.0, -2400.0, 450.0, -36.0, 1.0] + +Verifying orthogonality:: + + >>> Lm = lambda t: laguerre(m,a,t) + >>> Ln = lambda t: laguerre(n,a,t) + >>> a, n, m = 2.5, 2, 3 + >>> chop(quad(lambda t: exp(-t)*t**a*Lm(t)*Ln(t), [0,inf])) + 0.0 + + +""" + +hermite = r""" +Evaluates the Hermite polynomial `H_n(z)`, which may be defined using +the recurrence + +.. math :: + + H_0(z) = 1 + + H_1(z) = 2z + + H_{n+1} = 2z H_n(z) - 2n H_{n-1}(z). + +The Hermite polynomials are orthogonal on `(-\infty, \infty)` with +respect to the weight `e^{-z^2}`. More generally, allowing arbitrary complex +values of `n`, the Hermite function `H_n(z)` is defined as + +.. math :: + + H_n(z) = (2z)^n \,_2F_0\left(-\frac{n}{2}, \frac{1-n}{2}, + -\frac{1}{z^2}\right) + +for `\Re{z} > 0`, or generally + +.. math :: + + H_n(z) = 2^n \sqrt{\pi} \left( + \frac{1}{\Gamma\left(\frac{1-n}{2}\right)} + \,_1F_1\left(-\frac{n}{2}, \frac{1}{2}, z^2\right) - + \frac{2z}{\Gamma\left(-\frac{n}{2}\right)} + \,_1F_1\left(\frac{1-n}{2}, \frac{3}{2}, z^2\right) + \right). + +**Plots** + +.. literalinclude :: /plots/hermite.py +.. image :: /plots/hermite.png + +**Examples** + +Evaluation for arbitrary arguments:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> hermite(0, 10) + 1.0 + >>> hermite(1, 10); hermite(2, 10) + 20.0 + 398.0 + >>> hermite(10000, 2) + 4.950440066552087387515653e+19334 + >>> hermite(3, -10**8) + -7999999999999998800000000.0 + >>> hermite(-3, -10**8) + 1.675159751729877682920301e+4342944819032534 + >>> hermite(2+3j, -1+2j) + (-0.07652130602993513389421901 - 0.1084662449961914580276007j) + +Coefficients of the first few Hermite polynomials are:: + + >>> for n in range(7): + ... chop(taylor(lambda z: hermite(n, z), 0, n)) + ... + [1.0] + [0.0, 2.0] + [-2.0, 0.0, 4.0] + [0.0, -12.0, 0.0, 8.0] + [12.0, 0.0, -48.0, 0.0, 16.0] + [0.0, 120.0, 0.0, -160.0, 0.0, 32.0] + [-120.0, 0.0, 720.0, 0.0, -480.0, 0.0, 64.0] + +Values at `z = 0`:: + + >>> for n in range(-5, 9): + ... hermite(n, 0) + ... + 0.02769459142039868792653387 + 0.08333333333333333333333333 + 0.2215567313631895034122709 + 0.5 + 0.8862269254527580136490837 + 1.0 + 0.0 + -2.0 + 0.0 + 12.0 + 0.0 + -120.0 + 0.0 + 1680.0 + +Hermite functions satisfy the differential equation:: + + >>> n = 4 + >>> f = lambda z: hermite(n, z) + >>> z = 1.5 + >>> chop(diff(f,z,2) - 2*z*diff(f,z) + 2*n*f(z)) + 0.0 + +Verifying orthogonality:: + + >>> chop(quad(lambda t: hermite(2,t)*hermite(4,t)*exp(-t**2), [-inf,inf])) + 0.0 + +""" + +jacobi = r""" +``jacobi(n, a, b, x)`` evaluates the Jacobi polynomial +`P_n^{(a,b)}(x)`. The Jacobi polynomials are a special +case of the hypergeometric function `\,_2F_1` given by: + +.. math :: + + P_n^{(a,b)}(x) = {n+a \choose n} + \,_2F_1\left(-n,1+a+b+n,a+1,\frac{1-x}{2}\right). + +Note that this definition generalizes to nonintegral values +of `n`. When `n` is an integer, the hypergeometric series +terminates after a finite number of terms, giving +a polynomial in `x`. + +**Evaluation of Jacobi polynomials** + +A special evaluation is `P_n^{(a,b)}(1) = {n+a \choose n}`:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> jacobi(4, 0.5, 0.25, 1) + 2.4609375 + >>> binomial(4+0.5, 4) + 2.4609375 + +A Jacobi polynomial of degree `n` is equal to its +Taylor polynomial of degree `n`. The explicit +coefficients of Jacobi polynomials can therefore +be recovered easily using :func:`~mpmath.taylor`:: + + >>> for n in range(5): + ... nprint(taylor(lambda x: jacobi(n,1,2,x), 0, n)) + ... + [1.0] + [-0.5, 2.5] + [-0.75, -1.5, 5.25] + [0.5, -3.5, -3.5, 10.5] + [0.625, 2.5, -11.25, -7.5, 20.625] + +For nonintegral `n`, the Jacobi "polynomial" is no longer +a polynomial:: + + >>> nprint(taylor(lambda x: jacobi(0.5,1,2,x), 0, 4)) + [0.309983, 1.84119, -1.26933, 1.26699, -1.34808] + +**Orthogonality** + +The Jacobi polynomials are orthogonal on the interval +`[-1, 1]` with respect to the weight function +`w(x) = (1-x)^a (1+x)^b`. That is, +`w(x) P_n^{(a,b)}(x) P_m^{(a,b)}(x)` integrates to +zero if `m \ne n` and to a nonzero number if `m = n`. + +The orthogonality is easy to verify using numerical +quadrature:: + + >>> P = jacobi + >>> f = lambda x: (1-x)**a * (1+x)**b * P(m,a,b,x) * P(n,a,b,x) + >>> a = 2 + >>> b = 3 + >>> m, n = 3, 4 + >>> chop(quad(f, [-1, 1]), 1) + 0.0 + >>> m, n = 4, 4 + >>> quad(f, [-1, 1]) + 1.9047619047619 + +**Differential equation** + +The Jacobi polynomials are solutions of the differential +equation + +.. math :: + + (1-x^2) y'' + (b-a-(a+b+2)x) y' + n (n+a+b+1) y = 0. + +We can verify that :func:`~mpmath.jacobi` approximately satisfies +this equation:: + + >>> from mpmath import * + >>> mp.dps = 15 + >>> a = 2.5 + >>> b = 4 + >>> n = 3 + >>> y = lambda x: jacobi(n,a,b,x) + >>> x = pi + >>> A0 = n*(n+a+b+1)*y(x) + >>> A1 = (b-a-(a+b+2)*x)*diff(y,x) + >>> A2 = (1-x**2)*diff(y,x,2) + >>> nprint(A2 + A1 + A0, 1) + 4.0e-12 + +The difference of order `10^{-12}` is as close to zero as +it could be at 15-digit working precision, since the terms +are large:: + + >>> A0, A1, A2 + (26560.2328981879, -21503.7641037294, -5056.46879445852) + +""" + +legendre = r""" +``legendre(n, x)`` evaluates the Legendre polynomial `P_n(x)`. +The Legendre polynomials are given by the formula + +.. math :: + + P_n(x) = \frac{1}{2^n n!} \frac{d^n}{dx^n} (x^2 -1)^n. + +Alternatively, they can be computed recursively using + +.. math :: + + P_0(x) = 1 + + P_1(x) = x + + (n+1) P_{n+1}(x) = (2n+1) x P_n(x) - n P_{n-1}(x). + +A third definition is in terms of the hypergeometric function +`\,_2F_1`, whereby they can be generalized to arbitrary `n`: + +.. math :: + + P_n(x) = \,_2F_1\left(-n, n+1, 1, \frac{1-x}{2}\right) + +**Plots** + +.. literalinclude :: /plots/legendre.py +.. image :: /plots/legendre.png + +**Basic evaluation** + +The Legendre polynomials assume fixed values at the points +`x = -1` and `x = 1`:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> nprint([legendre(n, 1) for n in range(6)]) + [1.0, 1.0, 1.0, 1.0, 1.0, 1.0] + >>> nprint([legendre(n, -1) for n in range(6)]) + [1.0, -1.0, 1.0, -1.0, 1.0, -1.0] + +The coefficients of Legendre polynomials can be recovered +using degree-`n` Taylor expansion:: + + >>> for n in range(5): + ... nprint(chop(taylor(lambda x: legendre(n, x), 0, n))) + ... + [1.0] + [0.0, 1.0] + [-0.5, 0.0, 1.5] + [0.0, -1.5, 0.0, 2.5] + [0.375, 0.0, -3.75, 0.0, 4.375] + +The roots of Legendre polynomials are located symmetrically +on the interval `[-1, 1]`:: + + >>> for n in range(5): + ... nprint(polyroots(taylor(lambda x: legendre(n, x), 0, n)[::-1])) + ... + [] + [0.0] + [-0.57735, 0.57735] + [-0.774597, 0.0, 0.774597] + [-0.861136, -0.339981, 0.339981, 0.861136] + +An example of an evaluation for arbitrary `n`:: + + >>> legendre(0.75, 2+4j) + (1.94952805264875 + 2.1071073099422j) + +**Orthogonality** + +The Legendre polynomials are orthogonal on `[-1, 1]` with respect +to the trivial weight `w(x) = 1`. That is, `P_m(x) P_n(x)` +integrates to zero if `m \ne n` and to `2/(2n+1)` if `m = n`:: + + >>> m, n = 3, 4 + >>> quad(lambda x: legendre(m,x)*legendre(n,x), [-1, 1]) + 0.0 + >>> m, n = 4, 4 + >>> quad(lambda x: legendre(m,x)*legendre(n,x), [-1, 1]) + 0.222222222222222 + +**Differential equation** + +The Legendre polynomials satisfy the differential equation + +.. math :: + + ((1-x^2) y')' + n(n+1) y' = 0. + +We can verify this numerically:: + + >>> n = 3.6 + >>> x = 0.73 + >>> P = legendre + >>> A = diff(lambda t: (1-t**2)*diff(lambda u: P(n,u), t), x) + >>> B = n*(n+1)*P(n,x) + >>> nprint(A+B,1) + 9.0e-16 + +""" + + +legenp = r""" +Calculates the (associated) Legendre function of the first kind of +degree *n* and order *m*, `P_n^m(z)`. Taking `m = 0` gives the ordinary +Legendre function of the first kind, `P_n(z)`. The parameters may be +complex numbers. + +In terms of the Gauss hypergeometric function, the (associated) Legendre +function is defined as + +.. math :: + + P_n^m(z) = \frac{1}{\Gamma(1-m)} \frac{(1+z)^{m/2}}{(1-z)^{m/2}} + \,_2F_1\left(-n, n+1, 1-m, \frac{1-z}{2}\right). + +With *type=3* instead of *type=2*, the alternative +definition + +.. math :: + + \hat{P}_n^m(z) = \frac{1}{\Gamma(1-m)} \frac{(z+1)^{m/2}}{(z-1)^{m/2}} + \,_2F_1\left(-n, n+1, 1-m, \frac{1-z}{2}\right). + +is used. These functions correspond respectively to ``LegendreP[n,m,2,z]`` +and ``LegendreP[n,m,3,z]`` in Mathematica. + +The general solution of the (associated) Legendre differential equation + +.. math :: + + (1-z^2) f''(z) - 2zf'(z) + \left(n(n+1)-\frac{m^2}{1-z^2}\right)f(z) = 0 + +is given by `C_1 P_n^m(z) + C_2 Q_n^m(z)` for arbitrary constants +`C_1`, `C_2`, where `Q_n^m(z)` is a Legendre function of the +second kind as implemented by :func:`~mpmath.legenq`. + +**Examples** + +Evaluation for arbitrary parameters and arguments:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> legenp(2, 0, 10); legendre(2, 10) + 149.5 + 149.5 + >>> legenp(-2, 0.5, 2.5) + (1.972260393822275434196053 - 1.972260393822275434196053j) + >>> legenp(2+3j, 1-j, -0.5+4j) + (-3.335677248386698208736542 - 5.663270217461022307645625j) + >>> chop(legenp(3, 2, -1.5, type=2)) + 28.125 + >>> chop(legenp(3, 2, -1.5, type=3)) + -28.125 + +Verifying the associated Legendre differential equation:: + + >>> n, m = 2, -0.5 + >>> C1, C2 = 1, -3 + >>> f = lambda z: C1*legenp(n,m,z) + C2*legenq(n,m,z) + >>> deq = lambda z: (1-z**2)*diff(f,z,2) - 2*z*diff(f,z) + \ + ... (n*(n+1)-m**2/(1-z**2))*f(z) + >>> for z in [0, 2, -1.5, 0.5+2j]: + ... chop(deq(mpmathify(z))) + ... + 0.0 + 0.0 + 0.0 + 0.0 +""" + +legenq = r""" +Calculates the (associated) Legendre function of the second kind of +degree *n* and order *m*, `Q_n^m(z)`. Taking `m = 0` gives the ordinary +Legendre function of the second kind, `Q_n(z)`. The parameters may be +complex numbers. + +The Legendre functions of the second kind give a second set of +solutions to the (associated) Legendre differential equation. +(See :func:`~mpmath.legenp`.) +Unlike the Legendre functions of the first kind, they are not +polynomials of `z` for integer `n`, `m` but rational or logarithmic +functions with poles at `z = \pm 1`. + +There are various ways to define Legendre functions of +the second kind, giving rise to different complex structure. +A version can be selected using the *type* keyword argument. +The *type=2* and *type=3* functions are given respectively by + +.. math :: + + Q_n^m(z) = \frac{\pi}{2 \sin(\pi m)} + \left( \cos(\pi m) P_n^m(z) - + \frac{\Gamma(1+m+n)}{\Gamma(1-m+n)} P_n^{-m}(z)\right) + + \hat{Q}_n^m(z) = \frac{\pi}{2 \sin(\pi m)} e^{\pi i m} + \left( \hat{P}_n^m(z) - + \frac{\Gamma(1+m+n)}{\Gamma(1-m+n)} \hat{P}_n^{-m}(z)\right) + +where `P` and `\hat{P}` are the *type=2* and *type=3* Legendre functions +of the first kind. The formulas above should be understood as limits +when `m` is an integer. + +These functions correspond to ``LegendreQ[n,m,2,z]`` (or ``LegendreQ[n,m,z]``) +and ``LegendreQ[n,m,3,z]`` in Mathematica. The *type=3* function +is essentially the same as the function defined in +Abramowitz & Stegun (eq. 8.1.3) but with `(z+1)^{m/2}(z-1)^{m/2}` instead +of `(z^2-1)^{m/2}`, giving slightly different branches. + +**Examples** + +Evaluation for arbitrary parameters and arguments:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> legenq(2, 0, 0.5) + -0.8186632680417568557122028 + >>> legenq(-1.5, -2, 2.5) + (0.6655964618250228714288277 + 0.3937692045497259717762649j) + >>> legenq(2-j, 3+4j, -6+5j) + (-10001.95256487468541686564 - 6011.691337610097577791134j) + +Different versions of the function:: + + >>> legenq(2, 1, 0.5) + 0.7298060598018049369381857 + >>> legenq(2, 1, 1.5) + (-7.902916572420817192300921 + 0.1998650072605976600724502j) + >>> legenq(2, 1, 0.5, type=3) + (2.040524284763495081918338 - 0.7298060598018049369381857j) + >>> chop(legenq(2, 1, 1.5, type=3)) + -0.1998650072605976600724502 + +""" + +chebyt = r""" +``chebyt(n, x)`` evaluates the Chebyshev polynomial of the first +kind `T_n(x)`, defined by the identity + +.. math :: + + T_n(\cos x) = \cos(n x). + +The Chebyshev polynomials of the first kind are a special +case of the Jacobi polynomials, and by extension of the +hypergeometric function `\,_2F_1`. They can thus also be +evaluated for nonintegral `n`. + +**Plots** + +.. literalinclude :: /plots/chebyt.py +.. image :: /plots/chebyt.png + +**Basic evaluation** + +The coefficients of the `n`-th polynomial can be recovered +using using degree-`n` Taylor expansion:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> for n in range(5): + ... nprint(chop(taylor(lambda x: chebyt(n, x), 0, n))) + ... + [1.0] + [0.0, 1.0] + [-1.0, 0.0, 2.0] + [0.0, -3.0, 0.0, 4.0] + [1.0, 0.0, -8.0, 0.0, 8.0] + +**Orthogonality** + +The Chebyshev polynomials of the first kind are orthogonal +on the interval `[-1, 1]` with respect to the weight +function `w(x) = 1/\sqrt{1-x^2}`:: + + >>> f = lambda x: chebyt(m,x)*chebyt(n,x)/sqrt(1-x**2) + >>> m, n = 3, 4 + >>> nprint(quad(f, [-1, 1]),1) + 0.0 + >>> m, n = 4, 4 + >>> quad(f, [-1, 1]) + 1.57079632596448 + +""" + +chebyu = r""" +``chebyu(n, x)`` evaluates the Chebyshev polynomial of the second +kind `U_n(x)`, defined by the identity + +.. math :: + + U_n(\cos x) = \frac{\sin((n+1)x)}{\sin(x)}. + +The Chebyshev polynomials of the second kind are a special +case of the Jacobi polynomials, and by extension of the +hypergeometric function `\,_2F_1`. They can thus also be +evaluated for nonintegral `n`. + +**Plots** + +.. literalinclude :: /plots/chebyu.py +.. image :: /plots/chebyu.png + +**Basic evaluation** + +The coefficients of the `n`-th polynomial can be recovered +using using degree-`n` Taylor expansion:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> for n in range(5): + ... nprint(chop(taylor(lambda x: chebyu(n, x), 0, n))) + ... + [1.0] + [0.0, 2.0] + [-1.0, 0.0, 4.0] + [0.0, -4.0, 0.0, 8.0] + [1.0, 0.0, -12.0, 0.0, 16.0] + +**Orthogonality** + +The Chebyshev polynomials of the second kind are orthogonal +on the interval `[-1, 1]` with respect to the weight +function `w(x) = \sqrt{1-x^2}`:: + + >>> f = lambda x: chebyu(m,x)*chebyu(n,x)*sqrt(1-x**2) + >>> m, n = 3, 4 + >>> quad(f, [-1, 1]) + 0.0 + >>> m, n = 4, 4 + >>> quad(f, [-1, 1]) + 1.5707963267949 +""" + +besselj = r""" +``besselj(n, x, derivative=0)`` gives the Bessel function of the first kind +`J_n(x)`. Bessel functions of the first kind are defined as +solutions of the differential equation + +.. math :: + + x^2 y'' + x y' + (x^2 - n^2) y = 0 + +which appears, among other things, when solving the radial +part of Laplace's equation in cylindrical coordinates. This +equation has two solutions for given `n`, where the +`J_n`-function is the solution that is nonsingular at `x = 0`. +For positive integer `n`, `J_n(x)` behaves roughly like a sine +(odd `n`) or cosine (even `n`) multiplied by a magnitude factor +that decays slowly as `x \to \pm\infty`. + +Generally, `J_n` is a special case of the hypergeometric +function `\,_0F_1`: + +.. math :: + + J_n(x) = \frac{x^n}{2^n \Gamma(n+1)} + \,_0F_1\left(n+1,-\frac{x^2}{4}\right) + +With *derivative* = `m \ne 0`, the `m`-th derivative + +.. math :: + + \frac{d^m}{dx^m} J_n(x) + +is computed. + +**Plots** + +.. literalinclude :: /plots/besselj.py +.. image :: /plots/besselj.png +.. literalinclude :: /plots/besselj_c.py +.. image :: /plots/besselj_c.png + +**Examples** + +Evaluation is supported for arbitrary arguments, and at +arbitrary precision:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> besselj(2, 1000) + -0.024777229528606 + >>> besselj(4, 0.75) + 0.000801070086542314 + >>> besselj(2, 1000j) + (-2.48071721019185e+432 + 6.41567059811949e-437j) + >>> mp.dps = 25 + >>> besselj(0.75j, 3+4j) + (-2.778118364828153309919653 - 1.5863603889018621585533j) + >>> mp.dps = 50 + >>> besselj(1, pi) + 0.28461534317975275734531059968613140570981118184947 + +Arguments may be large:: + + >>> mp.dps = 25 + >>> besselj(0, 10000) + -0.007096160353388801477265164 + >>> besselj(0, 10**10) + 0.000002175591750246891726859055 + >>> besselj(2, 10**100) + 7.337048736538615712436929e-51 + >>> besselj(2, 10**5*j) + (-3.540725411970948860173735e+43426 + 4.4949812409615803110051e-43433j) + +The Bessel functions of the first kind satisfy simple +symmetries around `x = 0`:: + + >>> mp.dps = 15 + >>> nprint([besselj(n,0) for n in range(5)]) + [1.0, 0.0, 0.0, 0.0, 0.0] + >>> nprint([besselj(n,pi) for n in range(5)]) + [-0.304242, 0.284615, 0.485434, 0.333458, 0.151425] + >>> nprint([besselj(n,-pi) for n in range(5)]) + [-0.304242, -0.284615, 0.485434, -0.333458, 0.151425] + +Roots of Bessel functions are often used:: + + >>> nprint([findroot(j0, k) for k in [2, 5, 8, 11, 14]]) + [2.40483, 5.52008, 8.65373, 11.7915, 14.9309] + >>> nprint([findroot(j1, k) for k in [3, 7, 10, 13, 16]]) + [3.83171, 7.01559, 10.1735, 13.3237, 16.4706] + +The roots are not periodic, but the distance between successive +roots asymptotically approaches `2 \pi`. Bessel functions of +the first kind have the following normalization:: + + >>> quadosc(j0, [0, inf], period=2*pi) + 1.0 + >>> quadosc(j1, [0, inf], period=2*pi) + 1.0 + +For `n = 1/2` or `n = -1/2`, the Bessel function reduces to a +trigonometric function:: + + >>> x = 10 + >>> besselj(0.5, x), sqrt(2/(pi*x))*sin(x) + (-0.13726373575505, -0.13726373575505) + >>> besselj(-0.5, x), sqrt(2/(pi*x))*cos(x) + (-0.211708866331398, -0.211708866331398) + +Derivatives of any order can be computed (negative orders +correspond to integration):: + + >>> mp.dps = 25 + >>> besselj(0, 7.5, 1) + -0.1352484275797055051822405 + >>> diff(lambda x: besselj(0,x), 7.5) + -0.1352484275797055051822405 + >>> besselj(0, 7.5, 10) + -0.1377811164763244890135677 + >>> diff(lambda x: besselj(0,x), 7.5, 10) + -0.1377811164763244890135677 + >>> besselj(0,7.5,-1) - besselj(0,3.5,-1) + -0.1241343240399987693521378 + >>> quad(j0, [3.5, 7.5]) + -0.1241343240399987693521378 + +Differentiation with a noninteger order gives the fractional derivative +in the sense of the Riemann-Liouville differintegral, as computed by +:func:`~mpmath.differint`:: + + >>> mp.dps = 15 + >>> besselj(1, 3.5, 0.75) + -0.385977722939384 + >>> differint(lambda x: besselj(1, x), 3.5, 0.75) + -0.385977722939384 + +""" + +besseli = r""" +``besseli(n, x, derivative=0)`` gives the modified Bessel function of the +first kind, + +.. math :: + + I_n(x) = i^{-n} J_n(ix). + +With *derivative* = `m \ne 0`, the `m`-th derivative + +.. math :: + + \frac{d^m}{dx^m} I_n(x) + +is computed. + +**Plots** + +.. literalinclude :: /plots/besseli.py +.. image :: /plots/besseli.png +.. literalinclude :: /plots/besseli_c.py +.. image :: /plots/besseli_c.png + +**Examples** + +Some values of `I_n(x)`:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> besseli(0,0) + 1.0 + >>> besseli(1,0) + 0.0 + >>> besseli(0,1) + 1.266065877752008335598245 + >>> besseli(3.5, 2+3j) + (-0.2904369752642538144289025 - 0.4469098397654815837307006j) + +Arguments may be large:: + + >>> besseli(2, 1000) + 2.480717210191852440616782e+432 + >>> besseli(2, 10**10) + 4.299602851624027900335391e+4342944813 + >>> besseli(2, 6000+10000j) + (-2.114650753239580827144204e+2603 + 4.385040221241629041351886e+2602j) + +For integers `n`, the following integral representation holds:: + + >>> mp.dps = 15 + >>> n = 3 + >>> x = 2.3 + >>> quad(lambda t: exp(x*cos(t))*cos(n*t), [0,pi])/pi + 0.349223221159309 + >>> besseli(n,x) + 0.349223221159309 + +Derivatives and antiderivatives of any order can be computed:: + + >>> mp.dps = 25 + >>> besseli(2, 7.5, 1) + 195.8229038931399062565883 + >>> diff(lambda x: besseli(2,x), 7.5) + 195.8229038931399062565883 + >>> besseli(2, 7.5, 10) + 153.3296508971734525525176 + >>> diff(lambda x: besseli(2,x), 7.5, 10) + 153.3296508971734525525176 + >>> besseli(2,7.5,-1) - besseli(2,3.5,-1) + 202.5043900051930141956876 + >>> quad(lambda x: besseli(2,x), [3.5, 7.5]) + 202.5043900051930141956876 + +""" + +bessely = r""" +``bessely(n, x, derivative=0)`` gives the Bessel function of the second kind, + +.. math :: + + Y_n(x) = \frac{J_n(x) \cos(\pi n) - J_{-n}(x)}{\sin(\pi n)}. + +For `n` an integer, this formula should be understood as a +limit. With *derivative* = `m \ne 0`, the `m`-th derivative + +.. math :: + + \frac{d^m}{dx^m} Y_n(x) + +is computed. + +**Plots** + +.. literalinclude :: /plots/bessely.py +.. image :: /plots/bessely.png +.. literalinclude :: /plots/bessely_c.py +.. image :: /plots/bessely_c.png + +**Examples** + +Some values of `Y_n(x)`:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> bessely(0,0), bessely(1,0), bessely(2,0) + (-inf, -inf, -inf) + >>> bessely(1, pi) + 0.3588729167767189594679827 + >>> bessely(0.5, 3+4j) + (9.242861436961450520325216 - 3.085042824915332562522402j) + +Arguments may be large:: + + >>> bessely(0, 10000) + 0.00364780555898660588668872 + >>> bessely(2.5, 10**50) + -4.8952500412050989295774e-26 + >>> bessely(2.5, -10**50) + (0.0 + 4.8952500412050989295774e-26j) + +Derivatives and antiderivatives of any order can be computed:: + + >>> bessely(2, 3.5, 1) + 0.3842618820422660066089231 + >>> diff(lambda x: bessely(2, x), 3.5) + 0.3842618820422660066089231 + >>> bessely(0.5, 3.5, 1) + -0.2066598304156764337900417 + >>> diff(lambda x: bessely(0.5, x), 3.5) + -0.2066598304156764337900417 + >>> diff(lambda x: bessely(2, x), 0.5, 10) + -208173867409.5547350101511 + >>> bessely(2, 0.5, 10) + -208173867409.5547350101511 + >>> bessely(2, 100.5, 100) + 0.02668487547301372334849043 + >>> quad(lambda x: bessely(2,x), [1,3]) + -1.377046859093181969213262 + >>> bessely(2,3,-1) - bessely(2,1,-1) + -1.377046859093181969213262 + +""" + +besselk = r""" +``besselk(n, x)`` gives the modified Bessel function of the +second kind, + +.. math :: + + K_n(x) = \frac{\pi}{2} \frac{I_{-n}(x)-I_{n}(x)}{\sin(\pi n)} + +For `n` an integer, this formula should be understood as a +limit. + +**Plots** + +.. literalinclude :: /plots/besselk.py +.. image :: /plots/besselk.png +.. literalinclude :: /plots/besselk_c.py +.. image :: /plots/besselk_c.png + +**Examples** + +Evaluation is supported for arbitrary complex arguments:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> besselk(0,1) + 0.4210244382407083333356274 + >>> besselk(0, -1) + (0.4210244382407083333356274 - 3.97746326050642263725661j) + >>> besselk(3.5, 2+3j) + (-0.02090732889633760668464128 + 0.2464022641351420167819697j) + >>> besselk(2+3j, 0.5) + (0.9615816021726349402626083 + 0.1918250181801757416908224j) + +Arguments may be large:: + + >>> besselk(0, 100) + 4.656628229175902018939005e-45 + >>> besselk(1, 10**6) + 4.131967049321725588398296e-434298 + >>> besselk(1, 10**6*j) + (0.001140348428252385844876706 - 0.0005200017201681152909000961j) + >>> besselk(4.5, fmul(10**50, j, exact=True)) + (1.561034538142413947789221e-26 + 1.243554598118700063281496e-25j) + +The point `x = 0` is a singularity (logarithmic if `n = 0`):: + + >>> besselk(0,0) + +inf + >>> besselk(1,0) + +inf + >>> for n in range(-4, 5): + ... print(besselk(n, '1e-1000')) + ... + 4.8e+4001 + 8.0e+3000 + 2.0e+2000 + 1.0e+1000 + 2302.701024509704096466802 + 1.0e+1000 + 2.0e+2000 + 8.0e+3000 + 4.8e+4001 + +""" + +hankel1 = r""" +``hankel1(n,x)`` computes the Hankel function of the first kind, +which is the complex combination of Bessel functions given by + +.. math :: + + H_n^{(1)}(x) = J_n(x) + i Y_n(x). + +**Plots** + +.. literalinclude :: /plots/hankel1.py +.. image :: /plots/hankel1.png +.. literalinclude :: /plots/hankel1_c.py +.. image :: /plots/hankel1_c.png + +**Examples** + +The Hankel function is generally complex-valued:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> hankel1(2, pi) + (0.4854339326315091097054957 - 0.0999007139290278787734903j) + >>> hankel1(3.5, pi) + (0.2340002029630507922628888 - 0.6419643823412927142424049j) +""" + +hankel2 = r""" +``hankel2(n,x)`` computes the Hankel function of the second kind, +which is the complex combination of Bessel functions given by + +.. math :: + + H_n^{(2)}(x) = J_n(x) - i Y_n(x). + +**Plots** + +.. literalinclude :: /plots/hankel2.py +.. image :: /plots/hankel2.png +.. literalinclude :: /plots/hankel2_c.py +.. image :: /plots/hankel2_c.png + +**Examples** + +The Hankel function is generally complex-valued:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> hankel2(2, pi) + (0.4854339326315091097054957 + 0.0999007139290278787734903j) + >>> hankel2(3.5, pi) + (0.2340002029630507922628888 + 0.6419643823412927142424049j) +""" + +lambertw = r""" +The Lambert W function `W(z)` is defined as the inverse function +of `w \exp(w)`. In other words, the value of `W(z)` is such that +`z = W(z) \exp(W(z))` for any complex number `z`. + +The Lambert W function is a multivalued function with infinitely +many branches `W_k(z)`, indexed by `k \in \mathbb{Z}`. Each branch +gives a different solution `w` of the equation `z = w \exp(w)`. +All branches are supported by :func:`~mpmath.lambertw`: + +* ``lambertw(z)`` gives the principal solution (branch 0) + +* ``lambertw(z, k)`` gives the solution on branch `k` + +The Lambert W function has two partially real branches: the +principal branch (`k = 0`) is real for real `z > -1/e`, and the +`k = -1` branch is real for `-1/e < z < 0`. All branches except +`k = 0` have a logarithmic singularity at `z = 0`. + +The definition, implementation and choice of branches +is based on [Corless]_. + +**Plots** + +.. literalinclude :: /plots/lambertw.py +.. image :: /plots/lambertw.png +.. literalinclude :: /plots/lambertw_c.py +.. image :: /plots/lambertw_c.png + +**Basic examples** + +The Lambert W function is the inverse of `w \exp(w)`:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> w = lambertw(1) + >>> w + 0.5671432904097838729999687 + >>> w*exp(w) + 1.0 + +Any branch gives a valid inverse:: + + >>> w = lambertw(1, k=3) + >>> w + (-2.853581755409037807206819 + 17.11353553941214591260783j) + >>> w = lambertw(1, k=25) + >>> w + (-5.047020464221569709378686 + 155.4763860949415867162066j) + >>> chop(w*exp(w)) + 1.0 + +**Applications to equation-solving** + +The Lambert W function may be used to solve various kinds of +equations, such as finding the value of the infinite power +tower `z^{z^{z^{\ldots}}}`:: + + >>> def tower(z, n): + ... if n == 0: + ... return z + ... return z ** tower(z, n-1) + ... + >>> tower(mpf(0.5), 100) + 0.6411857445049859844862005 + >>> -lambertw(-log(0.5))/log(0.5) + 0.6411857445049859844862005 + +**Properties** + +The Lambert W function grows roughly like the natural logarithm +for large arguments:: + + >>> lambertw(1000); log(1000) + 5.249602852401596227126056 + 6.907755278982137052053974 + >>> lambertw(10**100); log(10**100) + 224.8431064451185015393731 + 230.2585092994045684017991 + +The principal branch of the Lambert W function has a rational +Taylor series expansion around `z = 0`:: + + >>> nprint(taylor(lambertw, 0, 6), 10) + [0.0, 1.0, -1.0, 1.5, -2.666666667, 5.208333333, -10.8] + +Some special values and limits are:: + + >>> lambertw(0) + 0.0 + >>> lambertw(1) + 0.5671432904097838729999687 + >>> lambertw(e) + 1.0 + >>> lambertw(inf) + +inf + >>> lambertw(0, k=-1) + -inf + >>> lambertw(0, k=3) + -inf + >>> lambertw(inf, k=2) + (+inf + 12.56637061435917295385057j) + >>> lambertw(inf, k=3) + (+inf + 18.84955592153875943077586j) + >>> lambertw(-inf, k=3) + (+inf + 21.9911485751285526692385j) + +The `k = 0` and `k = -1` branches join at `z = -1/e` where +`W(z) = -1` for both branches. Since `-1/e` can only be represented +approximately with binary floating-point numbers, evaluating the +Lambert W function at this point only gives `-1` approximately:: + + >>> lambertw(-1/e, 0) + -0.9999999999998371330228251 + >>> lambertw(-1/e, -1) + -1.000000000000162866977175 + +If `-1/e` happens to round in the negative direction, there might be +a small imaginary part:: + + >>> mp.dps = 15 + >>> lambertw(-1/e) + (-1.0 + 8.22007971483662e-9j) + >>> lambertw(-1/e+eps) + -0.999999966242188 + +**References** + +1. [Corless]_ +""" + +barnesg = r""" +Evaluates the Barnes G-function, which generalizes the +superfactorial (:func:`~mpmath.superfac`) and by extension also the +hyperfactorial (:func:`~mpmath.hyperfac`) to the complex numbers +in an analogous way to how the gamma function generalizes +the ordinary factorial. + +The Barnes G-function may be defined in terms of a Weierstrass +product: + +.. math :: + + G(z+1) = (2\pi)^{z/2} e^{-[z(z+1)+\gamma z^2]/2} + \prod_{n=1}^\infty + \left[\left(1+\frac{z}{n}\right)^ne^{-z+z^2/(2n)}\right] + +For positive integers `n`, we have have relation to superfactorials +`G(n) = \mathrm{sf}(n-2) = 0! \cdot 1! \cdots (n-2)!`. + +**Examples** + +Some elementary values and limits of the Barnes G-function:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> barnesg(1), barnesg(2), barnesg(3) + (1.0, 1.0, 1.0) + >>> barnesg(4) + 2.0 + >>> barnesg(5) + 12.0 + >>> barnesg(6) + 288.0 + >>> barnesg(7) + 34560.0 + >>> barnesg(8) + 24883200.0 + >>> barnesg(inf) + +inf + >>> barnesg(0), barnesg(-1), barnesg(-2) + (0.0, 0.0, 0.0) + +Closed-form values are known for some rational arguments:: + + >>> barnesg('1/2') + 0.603244281209446 + >>> sqrt(exp(0.25+log(2)/12)/sqrt(pi)/glaisher**3) + 0.603244281209446 + >>> barnesg('1/4') + 0.29375596533861 + >>> nthroot(exp('3/8')/exp(catalan/pi)/ + ... gamma(0.25)**3/sqrt(glaisher)**9, 4) + 0.29375596533861 + +The Barnes G-function satisfies the functional equation +`G(z+1) = \Gamma(z) G(z)`:: + + >>> z = pi + >>> barnesg(z+1) + 2.39292119327948 + >>> gamma(z)*barnesg(z) + 2.39292119327948 + +The asymptotic growth rate of the Barnes G-function is related to +the Glaisher-Kinkelin constant:: + + >>> limit(lambda n: barnesg(n+1)/(n**(n**2/2-mpf(1)/12)* + ... (2*pi)**(n/2)*exp(-3*n**2/4)), inf) + 0.847536694177301 + >>> exp('1/12')/glaisher + 0.847536694177301 + +The Barnes G-function can be differentiated in closed form:: + + >>> z = 3 + >>> diff(barnesg, z) + 0.264507203401607 + >>> barnesg(z)*((z-1)*psi(0,z)-z+(log(2*pi)+1)/2) + 0.264507203401607 + +Evaluation is supported for arbitrary arguments and at arbitrary +precision:: + + >>> barnesg(6.5) + 2548.7457695685 + >>> barnesg(-pi) + 0.00535976768353037 + >>> barnesg(3+4j) + (-0.000676375932234244 - 4.42236140124728e-5j) + >>> mp.dps = 50 + >>> barnesg(1/sqrt(2)) + 0.81305501090451340843586085064413533788206204124732 + >>> q = barnesg(10j) + >>> q.real + 0.000000000021852360840356557241543036724799812371995850552234 + >>> q.imag + -0.00000000000070035335320062304849020654215545839053210041457588 + >>> mp.dps = 15 + >>> barnesg(100) + 3.10361006263698e+6626 + >>> barnesg(-101) + 0.0 + >>> barnesg(-10.5) + 5.94463017605008e+25 + >>> barnesg(-10000.5) + -6.14322868174828e+167480422 + >>> barnesg(1000j) + (5.21133054865546e-1173597 + 4.27461836811016e-1173597j) + >>> barnesg(-1000+1000j) + (2.43114569750291e+1026623 + 2.24851410674842e+1026623j) + + +**References** + +1. Whittaker & Watson, *A Course of Modern Analysis*, + Cambridge University Press, 4th edition (1927), p.264 +2. http://en.wikipedia.org/wiki/Barnes_G-function +3. http://mathworld.wolfram.com/BarnesG-Function.html + +""" + +superfac = r""" +Computes the superfactorial, defined as the product of +consecutive factorials + +.. math :: + + \mathrm{sf}(n) = \prod_{k=1}^n k! + +For general complex `z`, `\mathrm{sf}(z)` is defined +in terms of the Barnes G-function (see :func:`~mpmath.barnesg`). + +**Examples** + +The first few superfactorials are (OEIS A000178):: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> for n in range(10): + ... print("%s %s" % (n, superfac(n))) + ... + 0 1.0 + 1 1.0 + 2 2.0 + 3 12.0 + 4 288.0 + 5 34560.0 + 6 24883200.0 + 7 125411328000.0 + 8 5.05658474496e+15 + 9 1.83493347225108e+21 + +Superfactorials grow very rapidly:: + + >>> superfac(1000) + 3.24570818422368e+1177245 + >>> superfac(10**10) + 2.61398543581249e+467427913956904067453 + +Evaluation is supported for arbitrary arguments:: + + >>> mp.dps = 25 + >>> superfac(pi) + 17.20051550121297985285333 + >>> superfac(2+3j) + (-0.005915485633199789627466468 + 0.008156449464604044948738263j) + >>> diff(superfac, 1) + 0.2645072034016070205673056 + +**References** + +1. http://oeis.org/A000178 + +""" + + +hyperfac = r""" +Computes the hyperfactorial, defined for integers as the product + +.. math :: + + H(n) = \prod_{k=1}^n k^k. + + +The hyperfactorial satisfies the recurrence formula `H(z) = z^z H(z-1)`. +It can be defined more generally in terms of the Barnes G-function (see +:func:`~mpmath.barnesg`) and the gamma function by the formula + +.. math :: + + H(z) = \frac{\Gamma(z+1)^z}{G(z)}. + +The extension to complex numbers can also be done via +the integral representation + +.. math :: + + H(z) = (2\pi)^{-z/2} \exp \left[ + {z+1 \choose 2} + \int_0^z \log(t!)\,dt + \right]. + +**Examples** + +The rapidly-growing sequence of hyperfactorials begins +(OEIS A002109):: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> for n in range(10): + ... print("%s %s" % (n, hyperfac(n))) + ... + 0 1.0 + 1 1.0 + 2 4.0 + 3 108.0 + 4 27648.0 + 5 86400000.0 + 6 4031078400000.0 + 7 3.3197663987712e+18 + 8 5.56964379417266e+25 + 9 2.15779412229419e+34 + +Some even larger hyperfactorials are:: + + >>> hyperfac(1000) + 5.46458120882585e+1392926 + >>> hyperfac(10**10) + 4.60408207642219e+489142638002418704309 + +The hyperfactorial can be evaluated for arbitrary arguments:: + + >>> hyperfac(0.5) + 0.880449235173423 + >>> diff(hyperfac, 1) + 0.581061466795327 + >>> hyperfac(pi) + 205.211134637462 + >>> hyperfac(-10+1j) + (3.01144471378225e+46 - 2.45285242480185e+46j) + +The recurrence property of the hyperfactorial holds +generally:: + + >>> z = 3-4*j + >>> hyperfac(z) + (-4.49795891462086e-7 - 6.33262283196162e-7j) + >>> z**z * hyperfac(z-1) + (-4.49795891462086e-7 - 6.33262283196162e-7j) + >>> z = mpf(-0.6) + >>> chop(z**z * hyperfac(z-1)) + 1.28170142849352 + >>> hyperfac(z) + 1.28170142849352 + +The hyperfactorial may also be computed using the integral +definition:: + + >>> z = 2.5 + >>> hyperfac(z) + 15.9842119922237 + >>> (2*pi)**(-z/2)*exp(binomial(z+1,2) + + ... quad(lambda t: loggamma(t+1), [0, z])) + 15.9842119922237 + +:func:`~mpmath.hyperfac` supports arbitrary-precision evaluation:: + + >>> mp.dps = 50 + >>> hyperfac(10) + 215779412229418562091680268288000000000000000.0 + >>> hyperfac(1/sqrt(2)) + 0.89404818005227001975423476035729076375705084390942 + +**References** + +1. http://oeis.org/A002109 +2. http://mathworld.wolfram.com/Hyperfactorial.html + +""" + +rgamma = r""" +Computes the reciprocal of the gamma function, `1/\Gamma(z)`. This +function evaluates to zero at the poles +of the gamma function, `z = 0, -1, -2, \ldots`. + +**Examples** + +Basic examples:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> rgamma(1) + 1.0 + >>> rgamma(4) + 0.1666666666666666666666667 + >>> rgamma(0); rgamma(-1) + 0.0 + 0.0 + >>> rgamma(1000) + 2.485168143266784862783596e-2565 + >>> rgamma(inf) + 0.0 + +A definite integral that can be evaluated in terms of elementary +integrals:: + + >>> quad(rgamma, [0,inf]) + 2.807770242028519365221501 + >>> e + quad(lambda t: exp(-t)/(pi**2+log(t)**2), [0,inf]) + 2.807770242028519365221501 +""" + +loggamma = r""" +Computes the principal branch of the log-gamma function, +`\ln \Gamma(z)`. Unlike `\ln(\Gamma(z))`, which has infinitely many +complex branch cuts, the principal log-gamma function only has a single +branch cut along the negative half-axis. The principal branch +continuously matches the asymptotic Stirling expansion + +.. math :: + + \ln \Gamma(z) \sim \frac{\ln(2 \pi)}{2} + + \left(z-\frac{1}{2}\right) \ln(z) - z + O(z^{-1}). + +The real parts of both functions agree, but their imaginary +parts generally differ by `2 n \pi` for some `n \in \mathbb{Z}`. +They coincide for `z \in \mathbb{R}, z > 0`. + +Computationally, it is advantageous to use :func:`~mpmath.loggamma` +instead of :func:`~mpmath.gamma` for extremely large arguments. + +**Examples** + +Comparing with `\ln(\Gamma(z))`:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> loggamma('13.2'); log(gamma('13.2')) + 20.49400419456603678498394 + 20.49400419456603678498394 + >>> loggamma(3+4j) + (-1.756626784603784110530604 + 4.742664438034657928194889j) + >>> log(gamma(3+4j)) + (-1.756626784603784110530604 - 1.540520869144928548730397j) + >>> log(gamma(3+4j)) + 2*pi*j + (-1.756626784603784110530604 + 4.742664438034657928194889j) + +Note the imaginary parts for negative arguments:: + + >>> loggamma(-0.5); loggamma(-1.5); loggamma(-2.5) + (1.265512123484645396488946 - 3.141592653589793238462643j) + (0.8600470153764810145109327 - 6.283185307179586476925287j) + (-0.05624371649767405067259453 - 9.42477796076937971538793j) + +Some special values:: + + >>> loggamma(1); loggamma(2) + 0.0 + 0.0 + >>> loggamma(3); +ln2 + 0.6931471805599453094172321 + 0.6931471805599453094172321 + >>> loggamma(3.5); log(15*sqrt(pi)/8) + 1.200973602347074224816022 + 1.200973602347074224816022 + >>> loggamma(inf) + +inf + +Huge arguments are permitted:: + + >>> loggamma('1e30') + 6.807755278982137052053974e+31 + >>> loggamma('1e300') + 6.897755278982137052053974e+302 + >>> loggamma('1e3000') + 6.906755278982137052053974e+3003 + >>> loggamma('1e100000000000000000000') + 2.302585092994045684007991e+100000000000000000020 + >>> loggamma('1e30j') + (-1.570796326794896619231322e+30 + 6.807755278982137052053974e+31j) + >>> loggamma('1e300j') + (-1.570796326794896619231322e+300 + 6.897755278982137052053974e+302j) + >>> loggamma('1e3000j') + (-1.570796326794896619231322e+3000 + 6.906755278982137052053974e+3003j) + +The log-gamma function can be integrated analytically +on any interval of unit length:: + + >>> z = 0 + >>> quad(loggamma, [z,z+1]); log(2*pi)/2 + 0.9189385332046727417803297 + 0.9189385332046727417803297 + >>> z = 3+4j + >>> quad(loggamma, [z,z+1]); (log(z)-1)*z + log(2*pi)/2 + (-0.9619286014994750641314421 + 5.219637303741238195688575j) + (-0.9619286014994750641314421 + 5.219637303741238195688575j) + +The derivatives of the log-gamma function are given by the +polygamma function (:func:`~mpmath.psi`):: + + >>> diff(loggamma, -4+3j); psi(0, -4+3j) + (1.688493531222971393607153 + 2.554898911356806978892748j) + (1.688493531222971393607153 + 2.554898911356806978892748j) + >>> diff(loggamma, -4+3j, 2); psi(1, -4+3j) + (-0.1539414829219882371561038 - 0.1020485197430267719746479j) + (-0.1539414829219882371561038 - 0.1020485197430267719746479j) + +The log-gamma function satisfies an additive form of the +recurrence relation for the ordinary gamma function:: + + >>> z = 2+3j + >>> loggamma(z); loggamma(z+1) - log(z) + (-2.092851753092733349564189 + 2.302396543466867626153708j) + (-2.092851753092733349564189 + 2.302396543466867626153708j) + +""" + +siegeltheta = r""" +Computes the Riemann-Siegel theta function, + +.. math :: + + \theta(t) = \frac{ + \log\Gamma\left(\frac{1+2it}{4}\right) - + \log\Gamma\left(\frac{1-2it}{4}\right) + }{2i} - \frac{\log \pi}{2} t. + +The Riemann-Siegel theta function is important in +providing the phase factor for the Z-function +(see :func:`~mpmath.siegelz`). Evaluation is supported for real and +complex arguments:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> siegeltheta(0) + 0.0 + >>> siegeltheta(inf) + +inf + >>> siegeltheta(-inf) + -inf + >>> siegeltheta(1) + -1.767547952812290388302216 + >>> siegeltheta(10+0.25j) + (-3.068638039426838572528867 + 0.05804937947429712998395177j) + +Arbitrary derivatives may be computed with derivative = k + + >>> siegeltheta(1234, derivative=2) + 0.0004051864079114053109473741 + >>> diff(siegeltheta, 1234, n=2) + 0.0004051864079114053109473741 + + +The Riemann-Siegel theta function has odd symmetry around `t = 0`, +two local extreme points and three real roots including 0 (located +symmetrically):: + + >>> nprint(chop(taylor(siegeltheta, 0, 5))) + [0.0, -2.68609, 0.0, 2.69433, 0.0, -6.40218] + >>> findroot(diffun(siegeltheta), 7) + 6.28983598883690277966509 + >>> findroot(siegeltheta, 20) + 17.84559954041086081682634 + +For large `t`, there is a famous asymptotic formula +for `\theta(t)`, to first order given by:: + + >>> t = mpf(10**6) + >>> siegeltheta(t) + 5488816.353078403444882823 + >>> -t*log(2*pi/t)/2-t/2 + 5488816.745777464310273645 +""" + +grampoint = r""" +Gives the `n`-th Gram point `g_n`, defined as the solution +to the equation `\theta(g_n) = \pi n` where `\theta(t)` +is the Riemann-Siegel theta function (:func:`~mpmath.siegeltheta`). + +The first few Gram points are:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> grampoint(0) + 17.84559954041086081682634 + >>> grampoint(1) + 23.17028270124630927899664 + >>> grampoint(2) + 27.67018221781633796093849 + >>> grampoint(3) + 31.71797995476405317955149 + +Checking the definition:: + + >>> siegeltheta(grampoint(3)) + 9.42477796076937971538793 + >>> 3*pi + 9.42477796076937971538793 + +A large Gram point:: + + >>> grampoint(10**10) + 3293531632.728335454561153 + +Gram points are useful when studying the Z-function +(:func:`~mpmath.siegelz`). See the documentation of that function +for additional examples. + +:func:`~mpmath.grampoint` can solve the defining equation for +nonintegral `n`. There is a fixed point where `g(x) = x`:: + + >>> findroot(lambda x: grampoint(x) - x, 10000) + 9146.698193171459265866198 + +**References** + +1. http://mathworld.wolfram.com/GramPoint.html + +""" + +siegelz = r""" +Computes the Z-function, also known as the Riemann-Siegel Z function, + +.. math :: + + Z(t) = e^{i \theta(t)} \zeta(1/2+it) + +where `\zeta(s)` is the Riemann zeta function (:func:`~mpmath.zeta`) +and where `\theta(t)` denotes the Riemann-Siegel theta function +(see :func:`~mpmath.siegeltheta`). + +Evaluation is supported for real and complex arguments:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> siegelz(1) + -0.7363054628673177346778998 + >>> siegelz(3+4j) + (-0.1852895764366314976003936 - 0.2773099198055652246992479j) + +The first four derivatives are supported, using the +optional *derivative* keyword argument:: + + >>> siegelz(1234567, derivative=3) + 56.89689348495089294249178 + >>> diff(siegelz, 1234567, n=3) + 56.89689348495089294249178 + + +The Z-function has a Maclaurin expansion:: + + >>> nprint(chop(taylor(siegelz, 0, 4))) + [-1.46035, 0.0, 2.73588, 0.0, -8.39357] + +The Z-function `Z(t)` is equal to `\pm |\zeta(s)|` on the +critical line `s = 1/2+it` (i.e. for real arguments `t` +to `Z`). Its zeros coincide with those of the Riemann zeta +function:: + + >>> findroot(siegelz, 14) + 14.13472514173469379045725 + >>> findroot(siegelz, 20) + 21.02203963877155499262848 + >>> findroot(zeta, 0.5+14j) + (0.5 + 14.13472514173469379045725j) + >>> findroot(zeta, 0.5+20j) + (0.5 + 21.02203963877155499262848j) + +Since the Z-function is real-valued on the critical line +(and unlike `|\zeta(s)|` analytic), it is useful for +investigating the zeros of the Riemann zeta function. +For example, one can use a root-finding algorithm based +on sign changes:: + + >>> findroot(siegelz, [100, 200], solver='bisect') + 176.4414342977104188888926 + +To locate roots, Gram points `g_n` which can be computed +by :func:`~mpmath.grampoint` are useful. If `(-1)^n Z(g_n)` is +positive for two consecutive `n`, then `Z(t)` must have +a zero between those points:: + + >>> g10 = grampoint(10) + >>> g11 = grampoint(11) + >>> (-1)**10 * siegelz(g10) > 0 + True + >>> (-1)**11 * siegelz(g11) > 0 + True + >>> findroot(siegelz, [g10, g11], solver='bisect') + 56.44624769706339480436776 + >>> g10, g11 + (54.67523744685325626632663, 57.54516517954725443703014) + +""" + +riemannr = r""" +Evaluates the Riemann R function, a smooth approximation of the +prime counting function `\pi(x)` (see :func:`~mpmath.primepi`). The Riemann +R function gives a fast numerical approximation useful e.g. to +roughly estimate the number of primes in a given interval. + +The Riemann R function is computed using the rapidly convergent Gram +series, + +.. math :: + + R(x) = 1 + \sum_{k=1}^{\infty} + \frac{\log^k x}{k k! \zeta(k+1)}. + +From the Gram series, one sees that the Riemann R function is a +well-defined analytic function (except for a branch cut along +the negative real half-axis); it can be evaluated for arbitrary +real or complex arguments. + +The Riemann R function gives a very accurate approximation +of the prime counting function. For example, it is wrong by at +most 2 for `x < 1000`, and for `x = 10^9` differs from the exact +value of `\pi(x)` by 79, or less than two parts in a million. +It is about 10 times more accurate than the logarithmic integral +estimate (see :func:`~mpmath.li`), which however is even faster to evaluate. +It is orders of magnitude more accurate than the extremely +fast `x/\log x` estimate. + +**Examples** + +For small arguments, the Riemann R function almost exactly +gives the prime counting function if rounded to the nearest +integer:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> primepi(50), riemannr(50) + (15, 14.9757023241462) + >>> max(abs(primepi(n)-int(round(riemannr(n)))) for n in range(100)) + 1 + >>> max(abs(primepi(n)-int(round(riemannr(n)))) for n in range(300)) + 2 + +The Riemann R function can be evaluated for arguments far too large +for exact determination of `\pi(x)` to be computationally +feasible with any presently known algorithm:: + + >>> riemannr(10**30) + 1.46923988977204e+28 + >>> riemannr(10**100) + 4.3619719871407e+97 + >>> riemannr(10**1000) + 4.3448325764012e+996 + +A comparison of the Riemann R function and logarithmic integral estimates +for `\pi(x)` using exact values of `\pi(10^n)` up to `n = 9`. +The fractional error is shown in parentheses:: + + >>> exact = [4,25,168,1229,9592,78498,664579,5761455,50847534] + >>> for n, p in enumerate(exact): + ... n += 1 + ... r, l = riemannr(10**n), li(10**n) + ... rerr, lerr = nstr((r-p)/p,3), nstr((l-p)/p,3) + ... print("%i %i %s(%s) %s(%s)" % (n, p, r, rerr, l, lerr)) + ... + 1 4 4.56458314100509(0.141) 6.1655995047873(0.541) + 2 25 25.6616332669242(0.0265) 30.1261415840796(0.205) + 3 168 168.359446281167(0.00214) 177.609657990152(0.0572) + 4 1229 1226.93121834343(-0.00168) 1246.13721589939(0.0139) + 5 9592 9587.43173884197(-0.000476) 9629.8090010508(0.00394) + 6 78498 78527.3994291277(0.000375) 78627.5491594622(0.00165) + 7 664579 664667.447564748(0.000133) 664918.405048569(0.000511) + 8 5761455 5761551.86732017(1.68e-5) 5762209.37544803(0.000131) + 9 50847534 50847455.4277214(-1.55e-6) 50849234.9570018(3.35e-5) + +The derivative of the Riemann R function gives the approximate +probability for a number of magnitude `x` to be prime:: + + >>> diff(riemannr, 1000) + 0.141903028110784 + >>> mpf(primepi(1050) - primepi(950)) / 100 + 0.15 + +Evaluation is supported for arbitrary arguments and at arbitrary +precision:: + + >>> mp.dps = 30 + >>> riemannr(7.5) + 3.72934743264966261918857135136 + >>> riemannr(-4+2j) + (-0.551002208155486427591793957644 + 2.16966398138119450043195899746j) + +""" + +primepi = r""" +Evaluates the prime counting function, `\pi(x)`, which gives +the number of primes less than or equal to `x`. The argument +`x` may be fractional. + +The prime counting function is very expensive to evaluate +precisely for large `x`, and the present implementation is +not optimized in any way. For numerical approximation of the +prime counting function, it is better to use :func:`~mpmath.primepi2` +or :func:`~mpmath.riemannr`. + +Some values of the prime counting function:: + + >>> from mpmath import * + >>> [primepi(k) for k in range(20)] + [0, 0, 1, 2, 2, 3, 3, 4, 4, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 8] + >>> primepi(3.5) + 2 + >>> primepi(100000) + 9592 + +""" + +primepi2 = r""" +Returns an interval (as an ``mpi`` instance) providing bounds +for the value of the prime counting function `\pi(x)`. For small +`x`, :func:`~mpmath.primepi2` returns an exact interval based on +the output of :func:`~mpmath.primepi`. For `x > 2656`, a loose interval +based on Schoenfeld's inequality + +.. math :: + + |\pi(x) - \mathrm{li}(x)| < \frac{\sqrt x \log x}{8 \pi} + +is returned. This estimate is rigorous assuming the truth of +the Riemann hypothesis, and can be computed very quickly. + +**Examples** + +Exact values of the prime counting function for small `x`:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> iv.dps = 15; iv.pretty = True + >>> primepi2(10) + [4.0, 4.0] + >>> primepi2(100) + [25.0, 25.0] + >>> primepi2(1000) + [168.0, 168.0] + +Loose intervals are generated for moderately large `x`: + + >>> primepi2(10000), primepi(10000) + ([1209.0, 1283.0], 1229) + >>> primepi2(50000), primepi(50000) + ([5070.0, 5263.0], 5133) + +As `x` increases, the absolute error gets worse while the relative +error improves. The exact value of `\pi(10^{23})` is +1925320391606803968923, and :func:`~mpmath.primepi2` gives 9 significant +digits:: + + >>> p = primepi2(10**23) + >>> p + [1.9253203909477020467e+21, 1.925320392280406229e+21] + >>> mpf(p.delta) / mpf(p.a) + 6.9219865355293e-10 + +A more precise, nonrigorous estimate for `\pi(x)` can be +obtained using the Riemann R function (:func:`~mpmath.riemannr`). +For large enough `x`, the value returned by :func:`~mpmath.primepi2` +essentially amounts to a small perturbation of the value returned by +:func:`~mpmath.riemannr`:: + + >>> primepi2(10**100) + [4.3619719871407024816e+97, 4.3619719871407032404e+97] + >>> riemannr(10**100) + 4.3619719871407e+97 +""" + +primezeta = r""" +Computes the prime zeta function, which is defined +in analogy with the Riemann zeta function (:func:`~mpmath.zeta`) +as + +.. math :: + + P(s) = \sum_p \frac{1}{p^s} + +where the sum is taken over all prime numbers `p`. Although +this sum only converges for `\mathrm{Re}(s) > 1`, the +function is defined by analytic continuation in the +half-plane `\mathrm{Re}(s) > 0`. + +**Examples** + +Arbitrary-precision evaluation for real and complex arguments is +supported:: + + >>> from mpmath import * + >>> mp.dps = 30; mp.pretty = True + >>> primezeta(2) + 0.452247420041065498506543364832 + >>> primezeta(pi) + 0.15483752698840284272036497397 + >>> mp.dps = 50 + >>> primezeta(3) + 0.17476263929944353642311331466570670097541212192615 + >>> mp.dps = 20 + >>> primezeta(3+4j) + (-0.12085382601645763295 - 0.013370403397787023602j) + +The prime zeta function has a logarithmic pole at `s = 1`, +with residue equal to the difference of the Mertens and +Euler constants:: + + >>> primezeta(1) + +inf + >>> extradps(25)(lambda x: primezeta(1+x)+log(x))(+eps) + -0.31571845205389007685 + >>> mertens-euler + -0.31571845205389007685 + +The analytic continuation to `0 < \mathrm{Re}(s) \le 1` +is implemented. In this strip the function exhibits +very complex behavior; on the unit interval, it has poles at +`1/n` for every squarefree integer `n`:: + + >>> primezeta(0.5) # Pole at s = 1/2 + (-inf + 3.1415926535897932385j) + >>> primezeta(0.25) + (-1.0416106801757269036 + 0.52359877559829887308j) + >>> primezeta(0.5+10j) + (0.54892423556409790529 + 0.45626803423487934264j) + +Although evaluation works in principle for any `\mathrm{Re}(s) > 0`, +it should be noted that the evaluation time increases exponentially +as `s` approaches the imaginary axis. + +For large `\mathrm{Re}(s)`, `P(s)` is asymptotic to `2^{-s}`:: + + >>> primezeta(inf) + 0.0 + >>> primezeta(10), mpf(2)**-10 + (0.00099360357443698021786, 0.0009765625) + >>> primezeta(1000) + 9.3326361850321887899e-302 + >>> primezeta(1000+1000j) + (-3.8565440833654995949e-302 - 8.4985390447553234305e-302j) + +**References** + +Carl-Erik Froberg, "On the prime zeta function", +BIT 8 (1968), pp. 187-202. + +""" + +bernpoly = r""" +Evaluates the Bernoulli polynomial `B_n(z)`. + +The first few Bernoulli polynomials are:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> for n in range(6): + ... nprint(chop(taylor(lambda x: bernpoly(n,x), 0, n))) + ... + [1.0] + [-0.5, 1.0] + [0.166667, -1.0, 1.0] + [0.0, 0.5, -1.5, 1.0] + [-0.0333333, 0.0, 1.0, -2.0, 1.0] + [0.0, -0.166667, 0.0, 1.66667, -2.5, 1.0] + +At `z = 0`, the Bernoulli polynomial evaluates to a +Bernoulli number (see :func:`~mpmath.bernoulli`):: + + >>> bernpoly(12, 0), bernoulli(12) + (-0.253113553113553, -0.253113553113553) + >>> bernpoly(13, 0), bernoulli(13) + (0.0, 0.0) + +Evaluation is accurate for large `n` and small `z`:: + + >>> mp.dps = 25 + >>> bernpoly(100, 0.5) + 2.838224957069370695926416e+78 + >>> bernpoly(1000, 10.5) + 5.318704469415522036482914e+1769 + +""" + +polylog = r""" +Computes the polylogarithm, defined by the sum + +.. math :: + + \mathrm{Li}_s(z) = \sum_{k=1}^{\infty} \frac{z^k}{k^s}. + +This series is convergent only for `|z| < 1`, so elsewhere +the analytic continuation is implied. + +The polylogarithm should not be confused with the logarithmic +integral (also denoted by Li or li), which is implemented +as :func:`~mpmath.li`. + +**Examples** + +The polylogarithm satisfies a huge number of functional identities. +A sample of polylogarithm evaluations is shown below:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> polylog(1,0.5), log(2) + (0.693147180559945, 0.693147180559945) + >>> polylog(2,0.5), (pi**2-6*log(2)**2)/12 + (0.582240526465012, 0.582240526465012) + >>> polylog(2,-phi), -log(phi)**2-pi**2/10 + (-1.21852526068613, -1.21852526068613) + >>> polylog(3,0.5), 7*zeta(3)/8-pi**2*log(2)/12+log(2)**3/6 + (0.53721319360804, 0.53721319360804) + +:func:`~mpmath.polylog` can evaluate the analytic continuation of the +polylogarithm when `s` is an integer:: + + >>> polylog(2, 10) + (0.536301287357863 - 7.23378441241546j) + >>> polylog(2, -10) + -4.1982778868581 + >>> polylog(2, 10j) + (-3.05968879432873 + 3.71678149306807j) + >>> polylog(-2, 10) + -0.150891632373114 + >>> polylog(-2, -10) + 0.067618332081142 + >>> polylog(-2, 10j) + (0.0384353698579347 + 0.0912451798066779j) + +Some more examples, with arguments on the unit circle (note that +the series definition cannot be used for computation here):: + + >>> polylog(2,j) + (-0.205616758356028 + 0.915965594177219j) + >>> j*catalan-pi**2/48 + (-0.205616758356028 + 0.915965594177219j) + >>> polylog(3,exp(2*pi*j/3)) + (-0.534247512515375 + 0.765587078525922j) + >>> -4*zeta(3)/9 + 2*j*pi**3/81 + (-0.534247512515375 + 0.765587078525921j) + +Polylogarithms of different order are related by integration +and differentiation:: + + >>> s, z = 3, 0.5 + >>> polylog(s+1, z) + 0.517479061673899 + >>> quad(lambda t: polylog(s,t)/t, [0, z]) + 0.517479061673899 + >>> z*diff(lambda t: polylog(s+2,t), z) + 0.517479061673899 + +Taylor series expansions around `z = 0` are:: + + >>> for n in range(-3, 4): + ... nprint(taylor(lambda x: polylog(n,x), 0, 5)) + ... + [0.0, 1.0, 8.0, 27.0, 64.0, 125.0] + [0.0, 1.0, 4.0, 9.0, 16.0, 25.0] + [0.0, 1.0, 2.0, 3.0, 4.0, 5.0] + [0.0, 1.0, 1.0, 1.0, 1.0, 1.0] + [0.0, 1.0, 0.5, 0.333333, 0.25, 0.2] + [0.0, 1.0, 0.25, 0.111111, 0.0625, 0.04] + [0.0, 1.0, 0.125, 0.037037, 0.015625, 0.008] + +The series defining the polylogarithm is simultaneously +a Taylor series and an L-series. For certain values of `z`, the +polylogarithm reduces to a pure zeta function:: + + >>> polylog(pi, 1), zeta(pi) + (1.17624173838258, 1.17624173838258) + >>> polylog(pi, -1), -altzeta(pi) + (-0.909670702980385, -0.909670702980385) + +Evaluation for arbitrary, nonintegral `s` is supported +for `z` within the unit circle: + + >>> polylog(3+4j, 0.25) + (0.24258605789446 - 0.00222938275488344j) + >>> nsum(lambda k: 0.25**k / k**(3+4j), [1,inf]) + (0.24258605789446 - 0.00222938275488344j) + +It is also supported outside of the unit circle:: + + >>> polylog(1+j, 20+40j) + (-7.1421172179728 - 3.92726697721369j) + >>> polylog(1+j, 200+400j) + (-5.41934747194626 - 9.94037752563927j) + +**References** + +1. Richard Crandall, "Note on fast polylogarithm computation" + http://www.reed.edu/physics/faculty/crandall/papers/Polylog.pdf +2. http://en.wikipedia.org/wiki/Polylogarithm +3. http://mathworld.wolfram.com/Polylogarithm.html + +""" + +bell = r""" +For `n` a nonnegative integer, ``bell(n,x)`` evaluates the Bell +polynomial `B_n(x)`, the first few of which are + +.. math :: + + B_0(x) = 1 + + B_1(x) = x + + B_2(x) = x^2+x + + B_3(x) = x^3+3x^2+x + +If `x = 1` or :func:`~mpmath.bell` is called with only one argument, it +gives the `n`-th Bell number `B_n`, which is the number of +partitions of a set with `n` elements. By setting the precision to +at least `\log_{10} B_n` digits, :func:`~mpmath.bell` provides fast +calculation of exact Bell numbers. + +In general, :func:`~mpmath.bell` computes + +.. math :: + + B_n(x) = e^{-x} \left(\mathrm{sinc}(\pi n) + E_n(x)\right) + +where `E_n(x)` is the generalized exponential function implemented +by :func:`~mpmath.polyexp`. This is an extension of Dobinski's formula [1], +where the modification is the sinc term ensuring that `B_n(x)` is +continuous in `n`; :func:`~mpmath.bell` can thus be evaluated, +differentiated, etc for arbitrary complex arguments. + +**Examples** + +Simple evaluations:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> bell(0, 2.5) + 1.0 + >>> bell(1, 2.5) + 2.5 + >>> bell(2, 2.5) + 8.75 + +Evaluation for arbitrary complex arguments:: + + >>> bell(5.75+1j, 2-3j) + (-10767.71345136587098445143 - 15449.55065599872579097221j) + +The first few Bell polynomials:: + + >>> for k in range(7): + ... nprint(taylor(lambda x: bell(k,x), 0, k)) + ... + [1.0] + [0.0, 1.0] + [0.0, 1.0, 1.0] + [0.0, 1.0, 3.0, 1.0] + [0.0, 1.0, 7.0, 6.0, 1.0] + [0.0, 1.0, 15.0, 25.0, 10.0, 1.0] + [0.0, 1.0, 31.0, 90.0, 65.0, 15.0, 1.0] + +The first few Bell numbers and complementary Bell numbers:: + + >>> [int(bell(k)) for k in range(10)] + [1, 1, 2, 5, 15, 52, 203, 877, 4140, 21147] + >>> [int(bell(k,-1)) for k in range(10)] + [1, -1, 0, 1, 1, -2, -9, -9, 50, 267] + +Large Bell numbers:: + + >>> mp.dps = 50 + >>> bell(50) + 185724268771078270438257767181908917499221852770.0 + >>> bell(50,-1) + -29113173035759403920216141265491160286912.0 + +Some even larger values:: + + >>> mp.dps = 25 + >>> bell(1000,-1) + -1.237132026969293954162816e+1869 + >>> bell(1000) + 2.989901335682408421480422e+1927 + >>> bell(1000,2) + 6.591553486811969380442171e+1987 + >>> bell(1000,100.5) + 9.101014101401543575679639e+2529 + +A determinant identity satisfied by Bell numbers:: + + >>> mp.dps = 15 + >>> N = 8 + >>> det([[bell(k+j) for j in range(N)] for k in range(N)]) + 125411328000.0 + >>> superfac(N-1) + 125411328000.0 + +**References** + +1. http://mathworld.wolfram.com/DobinskisFormula.html + +""" + +polyexp = r""" +Evaluates the polyexponential function, defined for arbitrary +complex `s`, `z` by the series + +.. math :: + + E_s(z) = \sum_{k=1}^{\infty} \frac{k^s}{k!} z^k. + +`E_s(z)` is constructed from the exponential function analogously +to how the polylogarithm is constructed from the ordinary +logarithm; as a function of `s` (with `z` fixed), `E_s` is an L-series +It is an entire function of both `s` and `z`. + +The polyexponential function provides a generalization of the +Bell polynomials `B_n(x)` (see :func:`~mpmath.bell`) to noninteger orders `n`. +In terms of the Bell polynomials, + +.. math :: + + E_s(z) = e^z B_s(z) - \mathrm{sinc}(\pi s). + +Note that `B_n(x)` and `e^{-x} E_n(x)` are identical if `n` +is a nonzero integer, but not otherwise. In particular, they differ +at `n = 0`. + +**Examples** + +Evaluating a series:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> nsum(lambda k: sqrt(k)/fac(k), [1,inf]) + 2.101755547733791780315904 + >>> polyexp(0.5,1) + 2.101755547733791780315904 + +Evaluation for arbitrary arguments:: + + >>> polyexp(-3-4j, 2.5+2j) + (2.351660261190434618268706 + 1.202966666673054671364215j) + +Evaluation is accurate for tiny function values:: + + >>> polyexp(4, -100) + 3.499471750566824369520223e-36 + +If `n` is a nonpositive integer, `E_n` reduces to a special +instance of the hypergeometric function `\,_pF_q`:: + + >>> n = 3 + >>> x = pi + >>> polyexp(-n,x) + 4.042192318847986561771779 + >>> x*hyper([1]*(n+1), [2]*(n+1), x) + 4.042192318847986561771779 + +""" + +cyclotomic = r""" +Evaluates the cyclotomic polynomial `\Phi_n(x)`, defined by + +.. math :: + + \Phi_n(x) = \prod_{\zeta} (x - \zeta) + +where `\zeta` ranges over all primitive `n`-th roots of unity +(see :func:`~mpmath.unitroots`). An equivalent representation, used +for computation, is + +.. math :: + + \Phi_n(x) = \prod_{d\mid n}(x^d-1)^{\mu(n/d)} = \Phi_n(x) + +where `\mu(m)` denotes the Moebius function. The cyclotomic +polynomials are integer polynomials, the first of which can be +written explicitly as + +.. math :: + + \Phi_0(x) = 1 + + \Phi_1(x) = x - 1 + + \Phi_2(x) = x + 1 + + \Phi_3(x) = x^3 + x^2 + 1 + + \Phi_4(x) = x^2 + 1 + + \Phi_5(x) = x^4 + x^3 + x^2 + x + 1 + + \Phi_6(x) = x^2 - x + 1 + +**Examples** + +The coefficients of low-order cyclotomic polynomials can be recovered +using Taylor expansion:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> for n in range(9): + ... p = chop(taylor(lambda x: cyclotomic(n,x), 0, 10)) + ... print("%s %s" % (n, nstr(p[:10+1-p[::-1].index(1)]))) + ... + 0 [1.0] + 1 [-1.0, 1.0] + 2 [1.0, 1.0] + 3 [1.0, 1.0, 1.0] + 4 [1.0, 0.0, 1.0] + 5 [1.0, 1.0, 1.0, 1.0, 1.0] + 6 [1.0, -1.0, 1.0] + 7 [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0] + 8 [1.0, 0.0, 0.0, 0.0, 1.0] + +The definition as a product over primitive roots may be checked +by computing the product explicitly (for a real argument, this +method will generally introduce numerical noise in the imaginary +part):: + + >>> mp.dps = 25 + >>> z = 3+4j + >>> cyclotomic(10, z) + (-419.0 - 360.0j) + >>> fprod(z-r for r in unitroots(10, primitive=True)) + (-419.0 - 360.0j) + >>> z = 3 + >>> cyclotomic(10, z) + 61.0 + >>> fprod(z-r for r in unitroots(10, primitive=True)) + (61.0 - 3.146045605088568607055454e-25j) + +Up to permutation, the roots of a given cyclotomic polynomial +can be checked to agree with the list of primitive roots:: + + >>> p = taylor(lambda x: cyclotomic(6,x), 0, 6)[:3] + >>> for r in polyroots(p[::-1]): + ... print(r) + ... + (0.5 - 0.8660254037844386467637232j) + (0.5 + 0.8660254037844386467637232j) + >>> + >>> for r in unitroots(6, primitive=True): + ... print(r) + ... + (0.5 + 0.8660254037844386467637232j) + (0.5 - 0.8660254037844386467637232j) + +""" + +meijerg = r""" +Evaluates the Meijer G-function, defined as + +.. math :: + + G^{m,n}_{p,q} \left( \left. \begin{matrix} + a_1, \dots, a_n ; a_{n+1} \dots a_p \\ + b_1, \dots, b_m ; b_{m+1} \dots b_q + \end{matrix}\; \right| \; z ; r \right) = + \frac{1}{2 \pi i} \int_L + \frac{\prod_{j=1}^m \Gamma(b_j+s) \prod_{j=1}^n\Gamma(1-a_j-s)} + {\prod_{j=n+1}^{p}\Gamma(a_j+s) \prod_{j=m+1}^q \Gamma(1-b_j-s)} + z^{-s/r} ds + +for an appropriate choice of the contour `L` (see references). + +There are `p` elements `a_j`. +The argument *a_s* should be a pair of lists, the first containing the +`n` elements `a_1, \ldots, a_n` and the second containing +the `p-n` elements `a_{n+1}, \ldots a_p`. + +There are `q` elements `b_j`. +The argument *b_s* should be a pair of lists, the first containing the +`m` elements `b_1, \ldots, b_m` and the second containing +the `q-m` elements `b_{m+1}, \ldots b_q`. + +The implicit tuple `(m, n, p, q)` constitutes the order or degree of the +Meijer G-function, and is determined by the lengths of the coefficient +vectors. Confusingly, the indices in this tuple appear in a different order +from the coefficients, but this notation is standard. The many examples +given below should hopefully clear up any potential confusion. + +**Algorithm** + +The Meijer G-function is evaluated as a combination of hypergeometric series. +There are two versions of the function, which can be selected with +the optional *series* argument. + +*series=1* uses a sum of `m` `\,_pF_{q-1}` functions of `z` + +*series=2* uses a sum of `n` `\,_qF_{p-1}` functions of `1/z` + +The default series is chosen based on the degree and `|z|` in order +to be consistent with Mathematica's. This definition of the Meijer G-function +has a discontinuity at `|z| = 1` for some orders, which can +be avoided by explicitly specifying a series. + +Keyword arguments are forwarded to :func:`~mpmath.hypercomb`. + +**Examples** + +Many standard functions are special cases of the Meijer G-function +(possibly rescaled and/or with branch cut corrections). We define +some test parameters:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> a = mpf(0.75) + >>> b = mpf(1.5) + >>> z = mpf(2.25) + +The exponential function: +`e^z = G^{1,0}_{0,1} \left( \left. \begin{matrix} - \\ 0 \end{matrix} \; +\right| \; -z \right)` + + >>> meijerg([[],[]], [[0],[]], -z) + 9.487735836358525720550369 + >>> exp(z) + 9.487735836358525720550369 + +The natural logarithm: +`\log(1+z) = G^{1,2}_{2,2} \left( \left. \begin{matrix} 1, 1 \\ 1, 0 +\end{matrix} \; \right| \; -z \right)` + + >>> meijerg([[1,1],[]], [[1],[0]], z) + 1.178654996341646117219023 + >>> log(1+z) + 1.178654996341646117219023 + +A rational function: +`\frac{z}{z+1} = G^{1,2}_{2,2} \left( \left. \begin{matrix} 1, 1 \\ 1, 1 +\end{matrix} \; \right| \; z \right)` + + >>> meijerg([[1,1],[]], [[1],[1]], z) + 0.6923076923076923076923077 + >>> z/(z+1) + 0.6923076923076923076923077 + +The sine and cosine functions: + +`\frac{1}{\sqrt \pi} \sin(2 \sqrt z) = G^{1,0}_{0,2} \left( \left. \begin{matrix} +- \\ \frac{1}{2}, 0 \end{matrix} \; \right| \; z \right)` + +`\frac{1}{\sqrt \pi} \cos(2 \sqrt z) = G^{1,0}_{0,2} \left( \left. \begin{matrix} +- \\ 0, \frac{1}{2} \end{matrix} \; \right| \; z \right)` + + >>> meijerg([[],[]], [[0.5],[0]], (z/2)**2) + 0.4389807929218676682296453 + >>> sin(z)/sqrt(pi) + 0.4389807929218676682296453 + >>> meijerg([[],[]], [[0],[0.5]], (z/2)**2) + -0.3544090145996275423331762 + >>> cos(z)/sqrt(pi) + -0.3544090145996275423331762 + +Bessel functions: + +`J_a(2 \sqrt z) = G^{1,0}_{0,2} \left( \left. +\begin{matrix} - \\ \frac{a}{2}, -\frac{a}{2} +\end{matrix} \; \right| \; z \right)` + +`Y_a(2 \sqrt z) = G^{2,0}_{1,3} \left( \left. +\begin{matrix} \frac{-a-1}{2} \\ \frac{a}{2}, -\frac{a}{2}, \frac{-a-1}{2} +\end{matrix} \; \right| \; z \right)` + +`(-z)^{a/2} z^{-a/2} I_a(2 \sqrt z) = G^{1,0}_{0,2} \left( \left. +\begin{matrix} - \\ \frac{a}{2}, -\frac{a}{2} +\end{matrix} \; \right| \; -z \right)` + +`2 K_a(2 \sqrt z) = G^{2,0}_{0,2} \left( \left. +\begin{matrix} - \\ \frac{a}{2}, -\frac{a}{2} +\end{matrix} \; \right| \; z \right)` + +As the example with the Bessel *I* function shows, a branch +factor is required for some arguments when inverting the square root. + + >>> meijerg([[],[]], [[a/2],[-a/2]], (z/2)**2) + 0.5059425789597154858527264 + >>> besselj(a,z) + 0.5059425789597154858527264 + >>> meijerg([[],[(-a-1)/2]], [[a/2,-a/2],[(-a-1)/2]], (z/2)**2) + 0.1853868950066556941442559 + >>> bessely(a, z) + 0.1853868950066556941442559 + >>> meijerg([[],[]], [[a/2],[-a/2]], -(z/2)**2) + (0.8685913322427653875717476 + 2.096964974460199200551738j) + >>> (-z)**(a/2) / z**(a/2) * besseli(a, z) + (0.8685913322427653875717476 + 2.096964974460199200551738j) + >>> 0.5*meijerg([[],[]], [[a/2,-a/2],[]], (z/2)**2) + 0.09334163695597828403796071 + >>> besselk(a,z) + 0.09334163695597828403796071 + +Error functions: + +`\sqrt{\pi} z^{2(a-1)} \mathrm{erfc}(z) = G^{2,0}_{1,2} \left( \left. +\begin{matrix} a \\ a-1, a-\frac{1}{2} +\end{matrix} \; \right| \; z, \frac{1}{2} \right)` + + >>> meijerg([[],[a]], [[a-1,a-0.5],[]], z, 0.5) + 0.00172839843123091957468712 + >>> sqrt(pi) * z**(2*a-2) * erfc(z) + 0.00172839843123091957468712 + +A Meijer G-function of higher degree, (1,1,2,3): + + >>> meijerg([[a],[b]], [[a],[b,a-1]], z) + 1.55984467443050210115617 + >>> sin((b-a)*pi)/pi*(exp(z)-1)*z**(a-1) + 1.55984467443050210115617 + +A Meijer G-function of still higher degree, (4,1,2,4), that can +be expanded as a messy combination of exponential integrals: + + >>> meijerg([[a],[2*b-a]], [[b,a,b-0.5,-1-a+2*b],[]], z) + 0.3323667133658557271898061 + >>> chop(4**(a-b+1)*sqrt(pi)*gamma(2*b-2*a)*z**a*\ + ... expint(2*b-2*a, -2*sqrt(-z))*expint(2*b-2*a, 2*sqrt(-z))) + 0.3323667133658557271898061 + +In the following case, different series give different values:: + + >>> chop(meijerg([[1],[0.25]],[[3],[0.5]],-2)) + -0.06417628097442437076207337 + >>> meijerg([[1],[0.25]],[[3],[0.5]],-2,series=1) + 0.1428699426155117511873047 + >>> chop(meijerg([[1],[0.25]],[[3],[0.5]],-2,series=2)) + -0.06417628097442437076207337 + +**References** + +1. http://en.wikipedia.org/wiki/Meijer_G-function + +2. http://mathworld.wolfram.com/MeijerG-Function.html + +3. http://functions.wolfram.com/HypergeometricFunctions/MeijerG/ + +4. http://functions.wolfram.com/HypergeometricFunctions/MeijerG1/ + +""" + +clsin = r""" +Computes the Clausen sine function, defined formally by the series + +.. math :: + + \mathrm{Cl}_s(z) = \sum_{k=1}^{\infty} \frac{\sin(kz)}{k^s}. + +The special case `\mathrm{Cl}_2(z)` (i.e. ``clsin(2,z)``) is the classical +"Clausen function". More generally, the Clausen function is defined for +complex `s` and `z`, even when the series does not converge. The +Clausen function is related to the polylogarithm (:func:`~mpmath.polylog`) as + +.. math :: + + \mathrm{Cl}_s(z) = \frac{1}{2i}\left(\mathrm{Li}_s\left(e^{iz}\right) - + \mathrm{Li}_s\left(e^{-iz}\right)\right) + + = \mathrm{Im}\left[\mathrm{Li}_s(e^{iz})\right] \quad (s, z \in \mathbb{R}), + +and this representation can be taken to provide the analytic continuation of the +series. The complementary function :func:`~mpmath.clcos` gives the corresponding +cosine sum. + +**Examples** + +Evaluation for arbitrarily chosen `s` and `z`:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> s, z = 3, 4 + >>> clsin(s, z); nsum(lambda k: sin(z*k)/k**s, [1,inf]) + -0.6533010136329338746275795 + -0.6533010136329338746275795 + +Using `z + \pi` instead of `z` gives an alternating series:: + + >>> clsin(s, z+pi) + 0.8860032351260589402871624 + >>> nsum(lambda k: (-1)**k*sin(z*k)/k**s, [1,inf]) + 0.8860032351260589402871624 + +With `s = 1`, the sum can be expressed in closed form +using elementary functions:: + + >>> z = 1 + sqrt(3) + >>> clsin(1, z) + 0.2047709230104579724675985 + >>> chop((log(1-exp(-j*z)) - log(1-exp(j*z)))/(2*j)) + 0.2047709230104579724675985 + >>> nsum(lambda k: sin(k*z)/k, [1,inf]) + 0.2047709230104579724675985 + +The classical Clausen function `\mathrm{Cl}_2(\theta)` gives the +value of the integral `\int_0^{\theta} -\ln(2\sin(x/2)) dx` for +`0 < \theta < 2 \pi`:: + + >>> cl2 = lambda t: clsin(2, t) + >>> cl2(3.5) + -0.2465045302347694216534255 + >>> -quad(lambda x: ln(2*sin(0.5*x)), [0, 3.5]) + -0.2465045302347694216534255 + +This function is symmetric about `\theta = \pi` with zeros and extreme +points:: + + >>> cl2(0); cl2(pi/3); chop(cl2(pi)); cl2(5*pi/3); chop(cl2(2*pi)) + 0.0 + 1.014941606409653625021203 + 0.0 + -1.014941606409653625021203 + 0.0 + +Catalan's constant is a special value:: + + >>> cl2(pi/2) + 0.9159655941772190150546035 + >>> +catalan + 0.9159655941772190150546035 + +The Clausen sine function can be expressed in closed form when +`s` is an odd integer (becoming zero when `s` < 0):: + + >>> z = 1 + sqrt(2) + >>> clsin(1, z); (pi-z)/2 + 0.3636895456083490948304773 + 0.3636895456083490948304773 + >>> clsin(3, z); pi**2/6*z - pi*z**2/4 + z**3/12 + 0.5661751584451144991707161 + 0.5661751584451144991707161 + >>> clsin(-1, z) + 0.0 + >>> clsin(-3, z) + 0.0 + +It can also be expressed in closed form for even integer `s \le 0`, +providing a finite sum for series such as +`\sin(z) + \sin(2z) + \sin(3z) + \ldots`:: + + >>> z = 1 + sqrt(2) + >>> clsin(0, z) + 0.1903105029507513881275865 + >>> cot(z/2)/2 + 0.1903105029507513881275865 + >>> clsin(-2, z) + -0.1089406163841548817581392 + >>> -cot(z/2)*csc(z/2)**2/4 + -0.1089406163841548817581392 + +Call with ``pi=True`` to multiply `z` by `\pi` exactly:: + + >>> clsin(3, 3*pi) + -8.892316224968072424732898e-26 + >>> clsin(3, 3, pi=True) + 0.0 + +Evaluation for complex `s`, `z` in a nonconvergent case:: + + >>> s, z = -1-j, 1+2j + >>> clsin(s, z) + (-0.593079480117379002516034 + 0.9038644233367868273362446j) + >>> extraprec(20)(nsum)(lambda k: sin(k*z)/k**s, [1,inf]) + (-0.593079480117379002516034 + 0.9038644233367868273362446j) + +""" + +clcos = r""" +Computes the Clausen cosine function, defined formally by the series + +.. math :: + + \mathrm{\widetilde{Cl}}_s(z) = \sum_{k=1}^{\infty} \frac{\cos(kz)}{k^s}. + +This function is complementary to the Clausen sine function +:func:`~mpmath.clsin`. In terms of the polylogarithm, + +.. math :: + + \mathrm{\widetilde{Cl}}_s(z) = + \frac{1}{2}\left(\mathrm{Li}_s\left(e^{iz}\right) + + \mathrm{Li}_s\left(e^{-iz}\right)\right) + + = \mathrm{Re}\left[\mathrm{Li}_s(e^{iz})\right] \quad (s, z \in \mathbb{R}). + +**Examples** + +Evaluation for arbitrarily chosen `s` and `z`:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> s, z = 3, 4 + >>> clcos(s, z); nsum(lambda k: cos(z*k)/k**s, [1,inf]) + -0.6518926267198991308332759 + -0.6518926267198991308332759 + +Using `z + \pi` instead of `z` gives an alternating series:: + + >>> s, z = 3, 0.5 + >>> clcos(s, z+pi) + -0.8155530586502260817855618 + >>> nsum(lambda k: (-1)**k*cos(z*k)/k**s, [1,inf]) + -0.8155530586502260817855618 + +With `s = 1`, the sum can be expressed in closed form +using elementary functions:: + + >>> z = 1 + sqrt(3) + >>> clcos(1, z) + -0.6720334373369714849797918 + >>> chop(-0.5*(log(1-exp(j*z))+log(1-exp(-j*z)))) + -0.6720334373369714849797918 + >>> -log(abs(2*sin(0.5*z))) # Equivalent to above when z is real + -0.6720334373369714849797918 + >>> nsum(lambda k: cos(k*z)/k, [1,inf]) + -0.6720334373369714849797918 + +It can also be expressed in closed form when `s` is an even integer. +For example, + + >>> clcos(2,z) + -0.7805359025135583118863007 + >>> pi**2/6 - pi*z/2 + z**2/4 + -0.7805359025135583118863007 + +The case `s = 0` gives the renormalized sum of +`\cos(z) + \cos(2z) + \cos(3z) + \ldots` (which happens to be the same for +any value of `z`):: + + >>> clcos(0, z) + -0.5 + >>> nsum(lambda k: cos(k*z), [1,inf]) + -0.5 + +Also the sums + +.. math :: + + \cos(z) + 2\cos(2z) + 3\cos(3z) + \ldots + +and + +.. math :: + + \cos(z) + 2^n \cos(2z) + 3^n \cos(3z) + \ldots + +for higher integer powers `n = -s` can be done in closed form. They are zero +when `n` is positive and even (`s` negative and even):: + + >>> clcos(-1, z); 1/(2*cos(z)-2) + -0.2607829375240542480694126 + -0.2607829375240542480694126 + >>> clcos(-3, z); (2+cos(z))*csc(z/2)**4/8 + 0.1472635054979944390848006 + 0.1472635054979944390848006 + >>> clcos(-2, z); clcos(-4, z); clcos(-6, z) + 0.0 + 0.0 + 0.0 + +With `z = \pi`, the series reduces to that of the Riemann zeta function +(more generally, if `z = p \pi/q`, it is a finite sum over Hurwitz zeta +function values):: + + >>> clcos(2.5, 0); zeta(2.5) + 1.34148725725091717975677 + 1.34148725725091717975677 + >>> clcos(2.5, pi); -altzeta(2.5) + -0.8671998890121841381913472 + -0.8671998890121841381913472 + +Call with ``pi=True`` to multiply `z` by `\pi` exactly:: + + >>> clcos(-3, 2*pi) + 2.997921055881167659267063e+102 + >>> clcos(-3, 2, pi=True) + 0.008333333333333333333333333 + +Evaluation for complex `s`, `z` in a nonconvergent case:: + + >>> s, z = -1-j, 1+2j + >>> clcos(s, z) + (0.9407430121562251476136807 + 0.715826296033590204557054j) + >>> extraprec(20)(nsum)(lambda k: cos(k*z)/k**s, [1,inf]) + (0.9407430121562251476136807 + 0.715826296033590204557054j) + +""" + +whitm = r""" +Evaluates the Whittaker function `M(k,m,z)`, which gives a solution +to the Whittaker differential equation + +.. math :: + + \frac{d^2f}{dz^2} + \left(-\frac{1}{4}+\frac{k}{z}+ + \frac{(\frac{1}{4}-m^2)}{z^2}\right) f = 0. + +A second solution is given by :func:`~mpmath.whitw`. + +The Whittaker functions are defined in Abramowitz & Stegun, section 13.1. +They are alternate forms of the confluent hypergeometric functions +`\,_1F_1` and `U`: + +.. math :: + + M(k,m,z) = e^{-\frac{1}{2}z} z^{\frac{1}{2}+m} + \,_1F_1(\tfrac{1}{2}+m-k, 1+2m, z) + + W(k,m,z) = e^{-\frac{1}{2}z} z^{\frac{1}{2}+m} + U(\tfrac{1}{2}+m-k, 1+2m, z). + +**Examples** + +Evaluation for arbitrary real and complex arguments is supported:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> whitm(1, 1, 1) + 0.7302596799460411820509668 + >>> whitm(1, 1, -1) + (0.0 - 1.417977827655098025684246j) + >>> whitm(j, j/2, 2+3j) + (3.245477713363581112736478 - 0.822879187542699127327782j) + >>> whitm(2, 3, 100000) + 4.303985255686378497193063e+21707 + +Evaluation at zero:: + + >>> whitm(1,-1,0); whitm(1,-0.5,0); whitm(1,0,0) + +inf + nan + 0.0 + +We can verify that :func:`~mpmath.whitm` numerically satisfies the +differential equation for arbitrarily chosen values:: + + >>> k = mpf(0.25) + >>> m = mpf(1.5) + >>> f = lambda z: whitm(k,m,z) + >>> for z in [-1, 2.5, 3, 1+2j]: + ... chop(diff(f,z,2) + (-0.25 + k/z + (0.25-m**2)/z**2)*f(z)) + ... + 0.0 + 0.0 + 0.0 + 0.0 + +An integral involving both :func:`~mpmath.whitm` and :func:`~mpmath.whitw`, +verifying evaluation along the real axis:: + + >>> quad(lambda x: exp(-x)*whitm(3,2,x)*whitw(1,-2,x), [0,inf]) + 3.438869842576800225207341 + >>> 128/(21*sqrt(pi)) + 3.438869842576800225207341 + +""" + +whitw = r""" +Evaluates the Whittaker function `W(k,m,z)`, which gives a second +solution to the Whittaker differential equation. (See :func:`~mpmath.whitm`.) + +**Examples** + +Evaluation for arbitrary real and complex arguments is supported:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> whitw(1, 1, 1) + 1.19532063107581155661012 + >>> whitw(1, 1, -1) + (-0.9424875979222187313924639 - 0.2607738054097702293308689j) + >>> whitw(j, j/2, 2+3j) + (0.1782899315111033879430369 - 0.01609578360403649340169406j) + >>> whitw(2, 3, 100000) + 1.887705114889527446891274e-21705 + >>> whitw(-1, -1, 100) + 1.905250692824046162462058e-24 + +Evaluation at zero:: + + >>> for m in [-1, -0.5, 0, 0.5, 1]: + ... whitw(1, m, 0) + ... + +inf + nan + 0.0 + nan + +inf + +We can verify that :func:`~mpmath.whitw` numerically satisfies the +differential equation for arbitrarily chosen values:: + + >>> k = mpf(0.25) + >>> m = mpf(1.5) + >>> f = lambda z: whitw(k,m,z) + >>> for z in [-1, 2.5, 3, 1+2j]: + ... chop(diff(f,z,2) + (-0.25 + k/z + (0.25-m**2)/z**2)*f(z)) + ... + 0.0 + 0.0 + 0.0 + 0.0 + +""" + +ber = r""" +Computes the Kelvin function ber, which for real arguments gives the real part +of the Bessel J function of a rotated argument + +.. math :: + + J_n\left(x e^{3\pi i/4}\right) = \mathrm{ber}_n(x) + i \mathrm{bei}_n(x). + +The imaginary part is given by :func:`~mpmath.bei`. + +**Plots** + +.. literalinclude :: /plots/ber.py +.. image :: /plots/ber.png + +**Examples** + +Verifying the defining relation:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> n, x = 2, 3.5 + >>> ber(n,x) + 1.442338852571888752631129 + >>> bei(n,x) + -0.948359035324558320217678 + >>> besselj(n, x*root(1,8,3)) + (1.442338852571888752631129 - 0.948359035324558320217678j) + +The ber and bei functions are also defined by analytic continuation +for complex arguments:: + + >>> ber(1+j, 2+3j) + (4.675445984756614424069563 - 15.84901771719130765656316j) + >>> bei(1+j, 2+3j) + (15.83886679193707699364398 + 4.684053288183046528703611j) + +""" + +bei = r""" +Computes the Kelvin function bei, which for real arguments gives the +imaginary part of the Bessel J function of a rotated argument. +See :func:`~mpmath.ber`. +""" + +ker = r""" +Computes the Kelvin function ker, which for real arguments gives the real part +of the (rescaled) Bessel K function of a rotated argument + +.. math :: + + e^{-\pi i/2} K_n\left(x e^{3\pi i/4}\right) = \mathrm{ker}_n(x) + i \mathrm{kei}_n(x). + +The imaginary part is given by :func:`~mpmath.kei`. + +**Plots** + +.. literalinclude :: /plots/ker.py +.. image :: /plots/ker.png + +**Examples** + +Verifying the defining relation:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> n, x = 2, 4.5 + >>> ker(n,x) + 0.02542895201906369640249801 + >>> kei(n,x) + -0.02074960467222823237055351 + >>> exp(-n*pi*j/2) * besselk(n, x*root(1,8,1)) + (0.02542895201906369640249801 - 0.02074960467222823237055351j) + +The ker and kei functions are also defined by analytic continuation +for complex arguments:: + + >>> ker(1+j, 3+4j) + (1.586084268115490421090533 - 2.939717517906339193598719j) + >>> kei(1+j, 3+4j) + (-2.940403256319453402690132 - 1.585621643835618941044855j) + +""" + +kei = r""" +Computes the Kelvin function kei, which for real arguments gives the +imaginary part of the (rescaled) Bessel K function of a rotated argument. +See :func:`~mpmath.ker`. +""" + +struveh = r""" +Gives the Struve function + +.. math :: + + \,\mathbf{H}_n(z) = + \sum_{k=0}^\infty \frac{(-1)^k}{\Gamma(k+\frac{3}{2}) + \Gamma(k+n+\frac{3}{2})} {\left({\frac{z}{2}}\right)}^{2k+n+1} + +which is a solution to the Struve differential equation + +.. math :: + + z^2 f''(z) + z f'(z) + (z^2-n^2) f(z) = \frac{2 z^{n+1}}{\pi (2n-1)!!}. + +**Examples** + +Evaluation for arbitrary real and complex arguments:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> struveh(0, 3.5) + 0.3608207733778295024977797 + >>> struveh(-1, 10) + -0.255212719726956768034732 + >>> struveh(1, -100.5) + 0.5819566816797362287502246 + >>> struveh(2.5, 10000000000000) + 3153915652525200060.308937 + >>> struveh(2.5, -10000000000000) + (0.0 - 3153915652525200060.308937j) + >>> struveh(1+j, 1000000+4000000j) + (-3.066421087689197632388731e+1737173 - 1.596619701076529803290973e+1737173j) + +A Struve function of half-integer order is elementary; for example: + + >>> z = 3 + >>> struveh(0.5, 3) + 0.9167076867564138178671595 + >>> sqrt(2/(pi*z))*(1-cos(z)) + 0.9167076867564138178671595 + +Numerically verifying the differential equation:: + + >>> z = mpf(4.5) + >>> n = 3 + >>> f = lambda z: struveh(n,z) + >>> lhs = z**2*diff(f,z,2) + z*diff(f,z) + (z**2-n**2)*f(z) + >>> rhs = 2*z**(n+1)/fac2(2*n-1)/pi + >>> lhs + 17.40359302709875496632744 + >>> rhs + 17.40359302709875496632744 + +""" + +struvel = r""" +Gives the modified Struve function + +.. math :: + + \,\mathbf{L}_n(z) = -i e^{-n\pi i/2} \mathbf{H}_n(i z) + +which solves to the modified Struve differential equation + +.. math :: + + z^2 f''(z) + z f'(z) - (z^2+n^2) f(z) = \frac{2 z^{n+1}}{\pi (2n-1)!!}. + +**Examples** + +Evaluation for arbitrary real and complex arguments:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> struvel(0, 3.5) + 7.180846515103737996249972 + >>> struvel(-1, 10) + 2670.994904980850550721511 + >>> struvel(1, -100.5) + 1.757089288053346261497686e+42 + >>> struvel(2.5, 10000000000000) + 4.160893281017115450519948e+4342944819025 + >>> struvel(2.5, -10000000000000) + (0.0 - 4.160893281017115450519948e+4342944819025j) + >>> struvel(1+j, 700j) + (-0.1721150049480079451246076 + 0.1240770953126831093464055j) + >>> struvel(1+j, 1000000+4000000j) + (-2.973341637511505389128708e+434290 - 5.164633059729968297147448e+434290j) + +Numerically verifying the differential equation:: + + >>> z = mpf(3.5) + >>> n = 3 + >>> f = lambda z: struvel(n,z) + >>> lhs = z**2*diff(f,z,2) + z*diff(f,z) - (z**2+n**2)*f(z) + >>> rhs = 2*z**(n+1)/fac2(2*n-1)/pi + >>> lhs + 6.368850306060678353018165 + >>> rhs + 6.368850306060678353018165 +""" + +appellf1 = r""" +Gives the Appell F1 hypergeometric function of two variables, + +.. math :: + + F_1(a,b_1,b_2,c,x,y) = \sum_{m=0}^{\infty} \sum_{n=0}^{\infty} + \frac{(a)_{m+n} (b_1)_m (b_2)_n}{(c)_{m+n}} + \frac{x^m y^n}{m! n!}. + +This series is only generally convergent when `|x| < 1` and `|y| < 1`, +although :func:`~mpmath.appellf1` can evaluate an analytic continuation +with respecto to either variable, and sometimes both. + +**Examples** + +Evaluation is supported for real and complex parameters:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> appellf1(1,0,0.5,1,0.5,0.25) + 1.154700538379251529018298 + >>> appellf1(1,1+j,0.5,1,0.5,0.5j) + (1.138403860350148085179415 + 1.510544741058517621110615j) + +For some integer parameters, the F1 series reduces to a polynomial:: + + >>> appellf1(2,-4,-3,1,2,5) + -816.0 + >>> appellf1(-5,1,2,1,4,5) + -20528.0 + +The analytic continuation with respect to either `x` or `y`, +and sometimes with respect to both, can be evaluated:: + + >>> appellf1(2,3,4,5,100,0.5) + (0.0006231042714165329279738662 + 0.0000005769149277148425774499857j) + >>> appellf1('1.1', '0.3', '0.2+2j', '0.4', '0.2', 1.5+3j) + (-0.1782604566893954897128702 + 0.002472407104546216117161499j) + >>> appellf1(1,2,3,4,10,12) + -0.07122993830066776374929313 + +For certain arguments, F1 reduces to an ordinary hypergeometric function:: + + >>> appellf1(1,2,3,5,0.5,0.25) + 1.547902270302684019335555 + >>> 4*hyp2f1(1,2,5,'1/3')/3 + 1.547902270302684019335555 + >>> appellf1(1,2,3,4,0,1.5) + (-1.717202506168937502740238 - 2.792526803190927323077905j) + >>> hyp2f1(1,3,4,1.5) + (-1.717202506168937502740238 - 2.792526803190927323077905j) + +The F1 function satisfies a system of partial differential equations:: + + >>> a,b1,b2,c,x,y = map(mpf, [1,0.5,0.25,1.125,0.25,-0.25]) + >>> F = lambda x,y: appellf1(a,b1,b2,c,x,y) + >>> chop(x*(1-x)*diff(F,(x,y),(2,0)) + + ... y*(1-x)*diff(F,(x,y),(1,1)) + + ... (c-(a+b1+1)*x)*diff(F,(x,y),(1,0)) - + ... b1*y*diff(F,(x,y),(0,1)) - + ... a*b1*F(x,y)) + 0.0 + >>> + >>> chop(y*(1-y)*diff(F,(x,y),(0,2)) + + ... x*(1-y)*diff(F,(x,y),(1,1)) + + ... (c-(a+b2+1)*y)*diff(F,(x,y),(0,1)) - + ... b2*x*diff(F,(x,y),(1,0)) - + ... a*b2*F(x,y)) + 0.0 + +The Appell F1 function allows for closed-form evaluation of various +integrals, such as any integral of the form +`\int x^r (x+a)^p (x+b)^q dx`:: + + >>> def integral(a,b,p,q,r,x1,x2): + ... a,b,p,q,r,x1,x2 = map(mpmathify, [a,b,p,q,r,x1,x2]) + ... f = lambda x: x**r * (x+a)**p * (x+b)**q + ... def F(x): + ... v = x**(r+1)/(r+1) * (a+x)**p * (b+x)**q + ... v *= (1+x/a)**(-p) + ... v *= (1+x/b)**(-q) + ... v *= appellf1(r+1,-p,-q,2+r,-x/a,-x/b) + ... return v + ... print("Num. quad: %s" % quad(f, [x1,x2])) + ... print("Appell F1: %s" % (F(x2)-F(x1))) + ... + >>> integral('1/5','4/3','-2','3','1/2',0,1) + Num. quad: 9.073335358785776206576981 + Appell F1: 9.073335358785776206576981 + >>> integral('3/2','4/3','-2','3','1/2',0,1) + Num. quad: 1.092829171999626454344678 + Appell F1: 1.092829171999626454344678 + >>> integral('3/2','4/3','-2','3','1/2',12,25) + Num. quad: 1106.323225040235116498927 + Appell F1: 1106.323225040235116498927 + +Also incomplete elliptic integrals fall into this category [1]:: + + >>> def E(z, m): + ... if (pi/2).ae(z): + ... return ellipe(m) + ... return 2*round(re(z)/pi)*ellipe(m) + mpf(-1)**round(re(z)/pi)*\ + ... sin(z)*appellf1(0.5,0.5,-0.5,1.5,sin(z)**2,m*sin(z)**2) + ... + >>> z, m = 1, 0.5 + >>> E(z,m); quad(lambda t: sqrt(1-m*sin(t)**2), [0,pi/4,3*pi/4,z]) + 0.9273298836244400669659042 + 0.9273298836244400669659042 + >>> z, m = 3, 2 + >>> E(z,m); quad(lambda t: sqrt(1-m*sin(t)**2), [0,pi/4,3*pi/4,z]) + (1.057495752337234229715836 + 1.198140234735592207439922j) + (1.057495752337234229715836 + 1.198140234735592207439922j) + +**References** + +1. [WolframFunctions]_ http://functions.wolfram.com/EllipticIntegrals/EllipticE2/26/01/ +2. [SrivastavaKarlsson]_ +3. [CabralRosetti]_ +4. [Vidunas]_ +5. [Slater]_ + +""" + +angerj = r""" +Gives the Anger function + +.. math :: + + \mathbf{J}_{\nu}(z) = \frac{1}{\pi} + \int_0^{\pi} \cos(\nu t - z \sin t) dt + +which is an entire function of both the parameter `\nu` and +the argument `z`. It solves the inhomogeneous Bessel differential +equation + +.. math :: + + f''(z) + \frac{1}{z}f'(z) + \left(1-\frac{\nu^2}{z^2}\right) f(z) + = \frac{(z-\nu)}{\pi z^2} \sin(\pi \nu). + +**Examples** + +Evaluation for real and complex parameter and argument:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> angerj(2,3) + 0.4860912605858910769078311 + >>> angerj(-3+4j, 2+5j) + (-5033.358320403384472395612 + 585.8011892476145118551756j) + >>> angerj(3.25, 1e6j) + (4.630743639715893346570743e+434290 - 1.117960409887505906848456e+434291j) + >>> angerj(-1.5, 1e6) + 0.0002795719747073879393087011 + +The Anger function coincides with the Bessel J-function when `\nu` +is an integer:: + + >>> angerj(1,3); besselj(1,3) + 0.3390589585259364589255146 + 0.3390589585259364589255146 + >>> angerj(1.5,3); besselj(1.5,3) + 0.4088969848691080859328847 + 0.4777182150870917715515015 + +Verifying the differential equation:: + + >>> v,z = mpf(2.25), 0.75 + >>> f = lambda z: angerj(v,z) + >>> diff(f,z,2) + diff(f,z)/z + (1-(v/z)**2)*f(z) + -0.6002108774380707130367995 + >>> (z-v)/(pi*z**2) * sinpi(v) + -0.6002108774380707130367995 + +Verifying the integral representation:: + + >>> angerj(v,z) + 0.1145380759919333180900501 + >>> quad(lambda t: cos(v*t-z*sin(t))/pi, [0,pi]) + 0.1145380759919333180900501 + +**References** + +1. [DLMF]_ section 11.10: Anger-Weber Functions +""" + +webere = r""" +Gives the Weber function + +.. math :: + + \mathbf{E}_{\nu}(z) = \frac{1}{\pi} + \int_0^{\pi} \sin(\nu t - z \sin t) dt + +which is an entire function of both the parameter `\nu` and +the argument `z`. It solves the inhomogeneous Bessel differential +equation + +.. math :: + + f''(z) + \frac{1}{z}f'(z) + \left(1-\frac{\nu^2}{z^2}\right) f(z) + = -\frac{1}{\pi z^2} (z+\nu+(z-\nu)\cos(\pi \nu)). + +**Examples** + +Evaluation for real and complex parameter and argument:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> webere(2,3) + -0.1057668973099018425662646 + >>> webere(-3+4j, 2+5j) + (-585.8081418209852019290498 - 5033.314488899926921597203j) + >>> webere(3.25, 1e6j) + (-1.117960409887505906848456e+434291 - 4.630743639715893346570743e+434290j) + >>> webere(3.25, 1e6) + -0.00002812518265894315604914453 + +Up to addition of a rational function of `z`, the Weber function coincides +with the Struve H-function when `\nu` is an integer:: + + >>> webere(1,3); 2/pi-struveh(1,3) + -0.3834897968188690177372881 + -0.3834897968188690177372881 + >>> webere(5,3); 26/(35*pi)-struveh(5,3) + 0.2009680659308154011878075 + 0.2009680659308154011878075 + +Verifying the differential equation:: + + >>> v,z = mpf(2.25), 0.75 + >>> f = lambda z: webere(v,z) + >>> diff(f,z,2) + diff(f,z)/z + (1-(v/z)**2)*f(z) + -1.097441848875479535164627 + >>> -(z+v+(z-v)*cospi(v))/(pi*z**2) + -1.097441848875479535164627 + +Verifying the integral representation:: + + >>> webere(v,z) + 0.1486507351534283744485421 + >>> quad(lambda t: sin(v*t-z*sin(t))/pi, [0,pi]) + 0.1486507351534283744485421 + +**References** + +1. [DLMF]_ section 11.10: Anger-Weber Functions +""" + +lommels1 = r""" +Gives the Lommel function `s_{\mu,\nu}` or `s^{(1)}_{\mu,\nu}` + +.. math :: + + s_{\mu,\nu}(z) = \frac{z^{\mu+1}}{(\mu-\nu+1)(\mu+\nu+1)} + \,_1F_2\left(1; \frac{\mu-\nu+3}{2}, \frac{\mu+\nu+3}{2}; + -\frac{z^2}{4} \right) + +which solves the inhomogeneous Bessel equation + +.. math :: + + z^2 f''(z) + z f'(z) + (z^2-\nu^2) f(z) = z^{\mu+1}. + +A second solution is given by :func:`~mpmath.lommels2`. + +**Plots** + +.. literalinclude :: /plots/lommels1.py +.. image :: /plots/lommels1.png + +**Examples** + +An integral representation:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> u,v,z = 0.25, 0.125, mpf(0.75) + >>> lommels1(u,v,z) + 0.4276243877565150372999126 + >>> (bessely(v,z)*quad(lambda t: t**u*besselj(v,t), [0,z]) - \ + ... besselj(v,z)*quad(lambda t: t**u*bessely(v,t), [0,z]))*(pi/2) + 0.4276243877565150372999126 + +A special value:: + + >>> lommels1(v,v,z) + 0.5461221367746048054932553 + >>> gamma(v+0.5)*sqrt(pi)*power(2,v-1)*struveh(v,z) + 0.5461221367746048054932553 + +Verifying the differential equation:: + + >>> f = lambda z: lommels1(u,v,z) + >>> z**2*diff(f,z,2) + z*diff(f,z) + (z**2-v**2)*f(z) + 0.6979536443265746992059141 + >>> z**(u+1) + 0.6979536443265746992059141 + +**References** + +1. [GradshteynRyzhik]_ +2. [Weisstein]_ http://mathworld.wolfram.com/LommelFunction.html +""" + +lommels2 = r""" +Gives the second Lommel function `S_{\mu,\nu}` or `s^{(2)}_{\mu,\nu}` + +.. math :: + + S_{\mu,\nu}(z) = s_{\mu,\nu}(z) + 2^{\mu-1} + \Gamma\left(\tfrac{1}{2}(\mu-\nu+1)\right) + \Gamma\left(\tfrac{1}{2}(\mu+\nu+1)\right) \times + + \left[\sin(\tfrac{1}{2}(\mu-\nu)\pi) J_{\nu}(z) - + \cos(\tfrac{1}{2}(\mu-\nu)\pi) Y_{\nu}(z) + \right] + +which solves the same differential equation as +:func:`~mpmath.lommels1`. + +**Plots** + +.. literalinclude :: /plots/lommels2.py +.. image :: /plots/lommels2.png + +**Examples** + +For large `|z|`, `S_{\mu,\nu} \sim z^{\mu-1}`:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> lommels2(10,2,30000) + 1.968299831601008419949804e+40 + >>> power(30000,9) + 1.9683e+40 + +A special value:: + + >>> u,v,z = 0.5, 0.125, mpf(0.75) + >>> lommels2(v,v,z) + 0.9589683199624672099969765 + >>> (struveh(v,z)-bessely(v,z))*power(2,v-1)*sqrt(pi)*gamma(v+0.5) + 0.9589683199624672099969765 + +Verifying the differential equation:: + + >>> f = lambda z: lommels2(u,v,z) + >>> z**2*diff(f,z,2) + z*diff(f,z) + (z**2-v**2)*f(z) + 0.6495190528383289850727924 + >>> z**(u+1) + 0.6495190528383289850727924 + +**References** + +1. [GradshteynRyzhik]_ +2. [Weisstein]_ http://mathworld.wolfram.com/LommelFunction.html +""" + +appellf2 = r""" +Gives the Appell F2 hypergeometric function of two variables + +.. math :: + + F_2(a,b_1,b_2,c_1,c_2,x,y) = \sum_{m=0}^{\infty} \sum_{n=0}^{\infty} + \frac{(a)_{m+n} (b_1)_m (b_2)_n}{(c_1)_m (c_2)_n} + \frac{x^m y^n}{m! n!}. + +The series is generally absolutely convergent for `|x| + |y| < 1`. + +**Examples** + +Evaluation for real and complex arguments:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> appellf2(1,2,3,4,5,0.25,0.125) + 1.257417193533135344785602 + >>> appellf2(1,-3,-4,2,3,2,3) + -42.8 + >>> appellf2(0.5,0.25,-0.25,2,3,0.25j,0.25) + (0.9880539519421899867041719 + 0.01497616165031102661476978j) + >>> chop(appellf2(1,1+j,1-j,3j,-3j,0.25,0.25)) + 1.201311219287411337955192 + >>> appellf2(1,1,1,4,6,0.125,16) + (-0.09455532250274744282125152 - 0.7647282253046207836769297j) + +A transformation formula:: + + >>> a,b1,b2,c1,c2,x,y = map(mpf, [1,2,0.5,0.25,1.625,-0.125,0.125]) + >>> appellf2(a,b1,b2,c1,c2,x,y) + 0.2299211717841180783309688 + >>> (1-x)**(-a)*appellf2(a,c1-b1,b2,c1,c2,x/(x-1),y/(1-x)) + 0.2299211717841180783309688 + +A system of partial differential equations satisfied by F2:: + + >>> a,b1,b2,c1,c2,x,y = map(mpf, [1,0.5,0.25,1.125,1.5,0.0625,-0.0625]) + >>> F = lambda x,y: appellf2(a,b1,b2,c1,c2,x,y) + >>> chop(x*(1-x)*diff(F,(x,y),(2,0)) - + ... x*y*diff(F,(x,y),(1,1)) + + ... (c1-(a+b1+1)*x)*diff(F,(x,y),(1,0)) - + ... b1*y*diff(F,(x,y),(0,1)) - + ... a*b1*F(x,y)) + 0.0 + >>> chop(y*(1-y)*diff(F,(x,y),(0,2)) - + ... x*y*diff(F,(x,y),(1,1)) + + ... (c2-(a+b2+1)*y)*diff(F,(x,y),(0,1)) - + ... b2*x*diff(F,(x,y),(1,0)) - + ... a*b2*F(x,y)) + 0.0 + +**References** + +See references for :func:`~mpmath.appellf1`. +""" + +appellf3 = r""" +Gives the Appell F3 hypergeometric function of two variables + +.. math :: + + F_3(a_1,a_2,b_1,b_2,c,x,y) = \sum_{m=0}^{\infty} \sum_{n=0}^{\infty} + \frac{(a_1)_m (a_2)_n (b_1)_m (b_2)_n}{(c)_{m+n}} + \frac{x^m y^n}{m! n!}. + +The series is generally absolutely convergent for `|x| < 1, |y| < 1`. + +**Examples** + +Evaluation for various parameters and variables:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> appellf3(1,2,3,4,5,0.5,0.25) + 2.221557778107438938158705 + >>> appellf3(1,2,3,4,5,6,0); hyp2f1(1,3,5,6) + (-0.5189554589089861284537389 - 0.1454441043328607980769742j) + (-0.5189554589089861284537389 - 0.1454441043328607980769742j) + >>> appellf3(1,-2,-3,1,1,4,6) + -17.4 + >>> appellf3(1,2,-3,1,1,4,6) + (17.7876136773677356641825 + 19.54768762233649126154534j) + >>> appellf3(1,2,-3,1,1,6,4) + (85.02054175067929402953645 + 148.4402528821177305173599j) + >>> chop(appellf3(1+j,2,1-j,2,3,0.25,0.25)) + 1.719992169545200286696007 + +Many transformations and evaluations for special combinations +of the parameters are possible, e.g.: + + >>> a,b,c,x,y = map(mpf, [0.5,0.25,0.125,0.125,-0.125]) + >>> appellf3(a,c-a,b,c-b,c,x,y) + 1.093432340896087107444363 + >>> (1-y)**(a+b-c)*hyp2f1(a,b,c,x+y-x*y) + 1.093432340896087107444363 + >>> x**2*appellf3(1,1,1,1,3,x,-x) + 0.01568646277445385390945083 + >>> polylog(2,x**2) + 0.01568646277445385390945083 + >>> a1,a2,b1,b2,c,x = map(mpf, [0.5,0.25,0.125,0.5,4.25,0.125]) + >>> appellf3(a1,a2,b1,b2,c,x,1) + 1.03947361709111140096947 + >>> gammaprod([c,c-a2-b2],[c-a2,c-b2])*hyp3f2(a1,b1,c-a2-b2,c-a2,c-b2,x) + 1.03947361709111140096947 + +The Appell F3 function satisfies a pair of partial +differential equations:: + + >>> a1,a2,b1,b2,c,x,y = map(mpf, [0.5,0.25,0.125,0.5,0.625,0.0625,-0.0625]) + >>> F = lambda x,y: appellf3(a1,a2,b1,b2,c,x,y) + >>> chop(x*(1-x)*diff(F,(x,y),(2,0)) + + ... y*diff(F,(x,y),(1,1)) + + ... (c-(a1+b1+1)*x)*diff(F,(x,y),(1,0)) - + ... a1*b1*F(x,y)) + 0.0 + >>> chop(y*(1-y)*diff(F,(x,y),(0,2)) + + ... x*diff(F,(x,y),(1,1)) + + ... (c-(a2+b2+1)*y)*diff(F,(x,y),(0,1)) - + ... a2*b2*F(x,y)) + 0.0 + +**References** + +See references for :func:`~mpmath.appellf1`. +""" + +appellf4 = r""" +Gives the Appell F4 hypergeometric function of two variables + +.. math :: + + F_4(a,b,c_1,c_2,x,y) = \sum_{m=0}^{\infty} \sum_{n=0}^{\infty} + \frac{(a)_{m+n} (b)_{m+n}}{(c_1)_m (c_2)_n} + \frac{x^m y^n}{m! n!}. + +The series is generally absolutely convergent for +`\sqrt{|x|} + \sqrt{|y|} < 1`. + +**Examples** + +Evaluation for various parameters and arguments:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> appellf4(1,1,2,2,0.25,0.125) + 1.286182069079718313546608 + >>> appellf4(-2,-3,4,5,4,5) + 34.8 + >>> appellf4(5,4,2,3,0.25j,-0.125j) + (-0.2585967215437846642163352 + 2.436102233553582711818743j) + +Reduction to `\,_2F_1` in a special case:: + + >>> a,b,c,x,y = map(mpf, [0.5,0.25,0.125,0.125,-0.125]) + >>> appellf4(a,b,c,a+b-c+1,x*(1-y),y*(1-x)) + 1.129143488466850868248364 + >>> hyp2f1(a,b,c,x)*hyp2f1(a,b,a+b-c+1,y) + 1.129143488466850868248364 + +A system of partial differential equations satisfied by F4:: + + >>> a,b,c1,c2,x,y = map(mpf, [1,0.5,0.25,1.125,0.0625,-0.0625]) + >>> F = lambda x,y: appellf4(a,b,c1,c2,x,y) + >>> chop(x*(1-x)*diff(F,(x,y),(2,0)) - + ... y**2*diff(F,(x,y),(0,2)) - + ... 2*x*y*diff(F,(x,y),(1,1)) + + ... (c1-(a+b+1)*x)*diff(F,(x,y),(1,0)) - + ... ((a+b+1)*y)*diff(F,(x,y),(0,1)) - + ... a*b*F(x,y)) + 0.0 + >>> chop(y*(1-y)*diff(F,(x,y),(0,2)) - + ... x**2*diff(F,(x,y),(2,0)) - + ... 2*x*y*diff(F,(x,y),(1,1)) + + ... (c2-(a+b+1)*y)*diff(F,(x,y),(0,1)) - + ... ((a+b+1)*x)*diff(F,(x,y),(1,0)) - + ... a*b*F(x,y)) + 0.0 + +**References** + +See references for :func:`~mpmath.appellf1`. +""" + +zeta = r""" +Computes the Riemann zeta function + +.. math :: + + \zeta(s) = 1+\frac{1}{2^s}+\frac{1}{3^s}+\frac{1}{4^s}+\ldots + +or, with `a \ne 1`, the more general Hurwitz zeta function + +.. math :: + + \zeta(s,a) = \sum_{k=0}^\infty \frac{1}{(a+k)^s}. + +Optionally, ``zeta(s, a, n)`` computes the `n`-th derivative with +respect to `s`, + +.. math :: + + \zeta^{(n)}(s,a) = (-1)^n \sum_{k=0}^\infty \frac{\log^n(a+k)}{(a+k)^s}. + +Although these series only converge for `\Re(s) > 1`, the Riemann and Hurwitz +zeta functions are defined through analytic continuation for arbitrary +complex `s \ne 1` (`s = 1` is a pole). + +The implementation uses three algorithms: the Borwein algorithm for +the Riemann zeta function when `s` is close to the real line; +the Riemann-Siegel formula for the Riemann zeta function when `s` is +large imaginary, and Euler-Maclaurin summation in all other cases. +The reflection formula for `\Re(s) < 0` is implemented in some cases. +The algorithm can be chosen with ``method = 'borwein'``, +``method='riemann-siegel'`` or ``method = 'euler-maclaurin'``. + +The parameter `a` is usually a rational number `a = p/q`, and may be specified +as such by passing an integer tuple `(p, q)`. Evaluation is supported for +arbitrary complex `a`, but may be slow and/or inaccurate when `\Re(s) < 0` for +nonrational `a` or when computing derivatives. + +**Examples** + +Some values of the Riemann zeta function:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> zeta(2); pi**2 / 6 + 1.644934066848226436472415 + 1.644934066848226436472415 + >>> zeta(0) + -0.5 + >>> zeta(-1) + -0.08333333333333333333333333 + >>> zeta(-2) + 0.0 + +For large positive `s`, `\zeta(s)` rapidly approaches 1:: + + >>> zeta(50) + 1.000000000000000888178421 + >>> zeta(100) + 1.0 + >>> zeta(inf) + 1.0 + >>> 1-sum((zeta(k)-1)/k for k in range(2,85)); +euler + 0.5772156649015328606065121 + 0.5772156649015328606065121 + >>> nsum(lambda k: zeta(k)-1, [2, inf]) + 1.0 + +Evaluation is supported for complex `s` and `a`: + + >>> zeta(-3+4j) + (-0.03373057338827757067584698 + 0.2774499251557093745297677j) + >>> zeta(2+3j, -1+j) + (389.6841230140842816370741 + 295.2674610150305334025962j) + +The Riemann zeta function has so-called nontrivial zeros on +the critical line `s = 1/2 + it`:: + + >>> findroot(zeta, 0.5+14j); zetazero(1) + (0.5 + 14.13472514173469379045725j) + (0.5 + 14.13472514173469379045725j) + >>> findroot(zeta, 0.5+21j); zetazero(2) + (0.5 + 21.02203963877155499262848j) + (0.5 + 21.02203963877155499262848j) + >>> findroot(zeta, 0.5+25j); zetazero(3) + (0.5 + 25.01085758014568876321379j) + (0.5 + 25.01085758014568876321379j) + >>> chop(zeta(zetazero(10))) + 0.0 + +Evaluation on and near the critical line is supported for large +heights `t` by means of the Riemann-Siegel formula (currently +for `a = 1`, `n \le 4`):: + + >>> zeta(0.5+100000j) + (1.073032014857753132114076 + 5.780848544363503984261041j) + >>> zeta(0.75+1000000j) + (0.9535316058375145020351559 + 0.9525945894834273060175651j) + >>> zeta(0.5+10000000j) + (11.45804061057709254500227 - 8.643437226836021723818215j) + >>> zeta(0.5+100000000j, derivative=1) + (51.12433106710194942681869 + 43.87221167872304520599418j) + >>> zeta(0.5+100000000j, derivative=2) + (-444.2760822795430400549229 - 896.3789978119185981665403j) + >>> zeta(0.5+100000000j, derivative=3) + (3230.72682687670422215339 + 14374.36950073615897616781j) + >>> zeta(0.5+100000000j, derivative=4) + (-11967.35573095046402130602 - 218945.7817789262839266148j) + >>> zeta(1+10000000j) # off the line + (2.859846483332530337008882 + 0.491808047480981808903986j) + >>> zeta(1+10000000j, derivative=1) + (-4.333835494679647915673205 - 0.08405337962602933636096103j) + >>> zeta(1+10000000j, derivative=4) + (453.2764822702057701894278 - 581.963625832768189140995j) + +For investigation of the zeta function zeros, the Riemann-Siegel +Z-function is often more convenient than working with the Riemann +zeta function directly (see :func:`~mpmath.siegelz`). + +Some values of the Hurwitz zeta function:: + + >>> zeta(2, 3); -5./4 + pi**2/6 + 0.3949340668482264364724152 + 0.3949340668482264364724152 + >>> zeta(2, (3,4)); pi**2 - 8*catalan + 2.541879647671606498397663 + 2.541879647671606498397663 + +For positive integer values of `s`, the Hurwitz zeta function is +equivalent to a polygamma function (except for a normalizing factor):: + + >>> zeta(4, (1,5)); psi(3, '1/5')/6 + 625.5408324774542966919938 + 625.5408324774542966919938 + +Evaluation of derivatives:: + + >>> zeta(0, 3+4j, 1); loggamma(3+4j) - ln(2*pi)/2 + (-2.675565317808456852310934 + 4.742664438034657928194889j) + (-2.675565317808456852310934 + 4.742664438034657928194889j) + >>> zeta(2, 1, 20) + 2432902008176640000.000242 + >>> zeta(3+4j, 5.5+2j, 4) + (-0.140075548947797130681075 - 0.3109263360275413251313634j) + >>> zeta(0.5+100000j, 1, 4) + (-10407.16081931495861539236 + 13777.78669862804508537384j) + >>> zeta(-100+0.5j, (1,3), derivative=4) + (4.007180821099823942702249e+79 + 4.916117957092593868321778e+78j) + +Generating a Taylor series at `s = 2` using derivatives:: + + >>> for k in range(11): print("%s * (s-2)^%i" % (zeta(2,1,k)/fac(k), k)) + ... + 1.644934066848226436472415 * (s-2)^0 + -0.9375482543158437537025741 * (s-2)^1 + 0.9946401171494505117104293 * (s-2)^2 + -1.000024300473840810940657 * (s-2)^3 + 1.000061933072352565457512 * (s-2)^4 + -1.000006869443931806408941 * (s-2)^5 + 1.000000173233769531820592 * (s-2)^6 + -0.9999999569989868493432399 * (s-2)^7 + 0.9999999937218844508684206 * (s-2)^8 + -0.9999999996355013916608284 * (s-2)^9 + 1.000000000004610645020747 * (s-2)^10 + +Evaluation at zero and for negative integer `s`:: + + >>> zeta(0, 10) + -9.5 + >>> zeta(-2, (2,3)); mpf(1)/81 + 0.01234567901234567901234568 + 0.01234567901234567901234568 + >>> zeta(-3+4j, (5,4)) + (0.2899236037682695182085988 + 0.06561206166091757973112783j) + >>> zeta(-3.25, 1/pi) + -0.0005117269627574430494396877 + >>> zeta(-3.5, pi, 1) + 11.156360390440003294709 + >>> zeta(-100.5, (8,3)) + -4.68162300487989766727122e+77 + >>> zeta(-10.5, (-8,3)) + (-0.01521913704446246609237979 + 29907.72510874248161608216j) + >>> zeta(-1000.5, (-8,3)) + (1.031911949062334538202567e+1770 + 1.519555750556794218804724e+426j) + >>> zeta(-1+j, 3+4j) + (-16.32988355630802510888631 - 22.17706465801374033261383j) + >>> zeta(-1+j, 3+4j, 2) + (32.48985276392056641594055 - 51.11604466157397267043655j) + >>> diff(lambda s: zeta(s, 3+4j), -1+j, 2) + (32.48985276392056641594055 - 51.11604466157397267043655j) + +**References** + +1. http://mathworld.wolfram.com/RiemannZetaFunction.html + +2. http://mathworld.wolfram.com/HurwitzZetaFunction.html + +3. [BorweinZeta]_ + +""" + +dirichlet = r""" +Evaluates the Dirichlet L-function + +.. math :: + + L(s,\chi) = \sum_{k=1}^\infty \frac{\chi(k)}{k^s}. + +where `\chi` is a periodic sequence of length `q` which should be supplied +in the form of a list `[\chi(0), \chi(1), \ldots, \chi(q-1)]`. +Strictly, `\chi` should be a Dirichlet character, but any periodic +sequence will work. + +For example, ``dirichlet(s, [1])`` gives the ordinary +Riemann zeta function and ``dirichlet(s, [-1,1])`` gives +the alternating zeta function (Dirichlet eta function). + +Also the derivative with respect to `s` (currently only a first +derivative) can be evaluated. + +**Examples** + +The ordinary Riemann zeta function:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> dirichlet(3, [1]); zeta(3) + 1.202056903159594285399738 + 1.202056903159594285399738 + >>> dirichlet(1, [1]) + +inf + +The alternating zeta function:: + + >>> dirichlet(1, [-1,1]); ln(2) + 0.6931471805599453094172321 + 0.6931471805599453094172321 + +The following defines the Dirichlet beta function +`\beta(s) = \sum_{k=0}^\infty \frac{(-1)^k}{(2k+1)^s}` and verifies +several values of this function:: + + >>> B = lambda s, d=0: dirichlet(s, [0, 1, 0, -1], d) + >>> B(0); 1./2 + 0.5 + 0.5 + >>> B(1); pi/4 + 0.7853981633974483096156609 + 0.7853981633974483096156609 + >>> B(2); +catalan + 0.9159655941772190150546035 + 0.9159655941772190150546035 + >>> B(2,1); diff(B, 2) + 0.08158073611659279510291217 + 0.08158073611659279510291217 + >>> B(-1,1); 2*catalan/pi + 0.5831218080616375602767689 + 0.5831218080616375602767689 + >>> B(0,1); log(gamma(0.25)**2/(2*pi*sqrt(2))) + 0.3915943927068367764719453 + 0.3915943927068367764719454 + >>> B(1,1); 0.25*pi*(euler+2*ln2+3*ln(pi)-4*ln(gamma(0.25))) + 0.1929013167969124293631898 + 0.1929013167969124293631898 + +A custom L-series of period 3:: + + >>> dirichlet(2, [2,0,1]) + 0.7059715047839078092146831 + >>> 2*nsum(lambda k: (3*k)**-2, [1,inf]) + \ + ... nsum(lambda k: (3*k+2)**-2, [0,inf]) + 0.7059715047839078092146831 + +""" + +coulombf = r""" +Calculates the regular Coulomb wave function + +.. math :: + + F_l(\eta,z) = C_l(\eta) z^{l+1} e^{-iz} \,_1F_1(l+1-i\eta, 2l+2, 2iz) + +where the normalization constant `C_l(\eta)` is as calculated by +:func:`~mpmath.coulombc`. This function solves the differential equation + +.. math :: + + f''(z) + \left(1-\frac{2\eta}{z}-\frac{l(l+1)}{z^2}\right) f(z) = 0. + +A second linearly independent solution is given by the irregular +Coulomb wave function `G_l(\eta,z)` (see :func:`~mpmath.coulombg`) +and thus the general solution is +`f(z) = C_1 F_l(\eta,z) + C_2 G_l(\eta,z)` for arbitrary +constants `C_1`, `C_2`. +Physically, the Coulomb wave functions give the radial solution +to the Schrodinger equation for a point particle in a `1/z` potential; `z` is +then the radius and `l`, `\eta` are quantum numbers. + +The Coulomb wave functions with real parameters are defined +in Abramowitz & Stegun, section 14. However, all parameters are permitted +to be complex in this implementation (see references). + +**Plots** + +.. literalinclude :: /plots/coulombf.py +.. image :: /plots/coulombf.png +.. literalinclude :: /plots/coulombf_c.py +.. image :: /plots/coulombf_c.png + +**Examples** + +Evaluation is supported for arbitrary magnitudes of `z`:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> coulombf(2, 1.5, 3.5) + 0.4080998961088761187426445 + >>> coulombf(-2, 1.5, 3.5) + 0.7103040849492536747533465 + >>> coulombf(2, 1.5, '1e-10') + 4.143324917492256448770769e-33 + >>> coulombf(2, 1.5, 1000) + 0.4482623140325567050716179 + >>> coulombf(2, 1.5, 10**10) + -0.066804196437694360046619 + +Verifying the differential equation:: + + >>> l, eta, z = 2, 3, mpf(2.75) + >>> A, B = 1, 2 + >>> f = lambda z: A*coulombf(l,eta,z) + B*coulombg(l,eta,z) + >>> chop(diff(f,z,2) + (1-2*eta/z - l*(l+1)/z**2)*f(z)) + 0.0 + +A Wronskian relation satisfied by the Coulomb wave functions:: + + >>> l = 2 + >>> eta = 1.5 + >>> F = lambda z: coulombf(l,eta,z) + >>> G = lambda z: coulombg(l,eta,z) + >>> for z in [3.5, -1, 2+3j]: + ... chop(diff(F,z)*G(z) - F(z)*diff(G,z)) + ... + 1.0 + 1.0 + 1.0 + +Another Wronskian relation:: + + >>> F = coulombf + >>> G = coulombg + >>> for z in [3.5, -1, 2+3j]: + ... chop(F(l-1,eta,z)*G(l,eta,z)-F(l,eta,z)*G(l-1,eta,z) - l/sqrt(l**2+eta**2)) + ... + 0.0 + 0.0 + 0.0 + +An integral identity connecting the regular and irregular wave functions:: + + >>> l, eta, z = 4+j, 2-j, 5+2j + >>> coulombf(l,eta,z) + j*coulombg(l,eta,z) + (0.7997977752284033239714479 + 0.9294486669502295512503127j) + >>> g = lambda t: exp(-t)*t**(l-j*eta)*(t+2*j*z)**(l+j*eta) + >>> j*exp(-j*z)*z**(-l)/fac(2*l+1)/coulombc(l,eta)*quad(g, [0,inf]) + (0.7997977752284033239714479 + 0.9294486669502295512503127j) + +Some test case with complex parameters, taken from Michel [2]:: + + >>> mp.dps = 15 + >>> coulombf(1+0.1j, 50+50j, 100.156) + (-1.02107292320897e+15 - 2.83675545731519e+15j) + >>> coulombg(1+0.1j, 50+50j, 100.156) + (2.83675545731519e+15 - 1.02107292320897e+15j) + >>> coulombf(1e-5j, 10+1e-5j, 0.1+1e-6j) + (4.30566371247811e-14 - 9.03347835361657e-19j) + >>> coulombg(1e-5j, 10+1e-5j, 0.1+1e-6j) + (778709182061.134 + 18418936.2660553j) + +The following reproduces a table in Abramowitz & Stegun, at twice +the precision:: + + >>> mp.dps = 10 + >>> eta = 2; z = 5 + >>> for l in [5, 4, 3, 2, 1, 0]: + ... print("%s %s %s" % (l, coulombf(l,eta,z), + ... diff(lambda z: coulombf(l,eta,z), z))) + ... + 5 0.09079533488 0.1042553261 + 4 0.2148205331 0.2029591779 + 3 0.4313159311 0.320534053 + 2 0.7212774133 0.3952408216 + 1 0.9935056752 0.3708676452 + 0 1.143337392 0.2937960375 + +**References** + +1. I.J. Thompson & A.R. Barnett, "Coulomb and Bessel Functions of Complex + Arguments and Order", J. Comp. Phys., vol 64, no. 2, June 1986. + +2. N. Michel, "Precise Coulomb wave functions for a wide range of + complex `l`, `\eta` and `z`", http://arxiv.org/abs/physics/0702051v1 + +""" + +coulombg = r""" +Calculates the irregular Coulomb wave function + +.. math :: + + G_l(\eta,z) = \frac{F_l(\eta,z) \cos(\chi) - F_{-l-1}(\eta,z)}{\sin(\chi)} + +where `\chi = \sigma_l - \sigma_{-l-1} - (l+1/2) \pi` +and `\sigma_l(\eta) = (\ln \Gamma(1+l+i\eta)-\ln \Gamma(1+l-i\eta))/(2i)`. + +See :func:`~mpmath.coulombf` for additional information. + +**Plots** + +.. literalinclude :: /plots/coulombg.py +.. image :: /plots/coulombg.png +.. literalinclude :: /plots/coulombg_c.py +.. image :: /plots/coulombg_c.png + +**Examples** + +Evaluation is supported for arbitrary magnitudes of `z`:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> coulombg(-2, 1.5, 3.5) + 1.380011900612186346255524 + >>> coulombg(2, 1.5, 3.5) + 1.919153700722748795245926 + >>> coulombg(-2, 1.5, '1e-10') + 201126715824.7329115106793 + >>> coulombg(-2, 1.5, 1000) + 0.1802071520691149410425512 + >>> coulombg(-2, 1.5, 10**10) + 0.652103020061678070929794 + +The following reproduces a table in Abramowitz & Stegun, +at twice the precision:: + + >>> mp.dps = 10 + >>> eta = 2; z = 5 + >>> for l in [1, 2, 3, 4, 5]: + ... print("%s %s %s" % (l, coulombg(l,eta,z), + ... -diff(lambda z: coulombg(l,eta,z), z))) + ... + 1 1.08148276 0.6028279961 + 2 1.496877075 0.5661803178 + 3 2.048694714 0.7959909551 + 4 3.09408669 1.731802374 + 5 5.629840456 4.549343289 + +Evaluation close to the singularity at `z = 0`:: + + >>> mp.dps = 15 + >>> coulombg(0,10,1) + 3088184933.67358 + >>> coulombg(0,10,'1e-10') + 5554866000719.8 + >>> coulombg(0,10,'1e-100') + 5554866221524.1 + +Evaluation with a half-integer value for `l`:: + + >>> coulombg(1.5, 1, 10) + 0.852320038297334 +""" + +coulombc = r""" +Gives the normalizing Gamow constant for Coulomb wave functions, + +.. math :: + + C_l(\eta) = 2^l \exp\left(-\pi \eta/2 + [\ln \Gamma(1+l+i\eta) + + \ln \Gamma(1+l-i\eta)]/2 - \ln \Gamma(2l+2)\right), + +where the log gamma function with continuous imaginary part +away from the negative half axis (see :func:`~mpmath.loggamma`) is implied. + +This function is used internally for the calculation of +Coulomb wave functions, and automatically cached to make multiple +evaluations with fixed `l`, `\eta` fast. +""" + +ellipfun = r""" +Computes any of the Jacobi elliptic functions, defined +in terms of Jacobi theta functions as + +.. math :: + + \mathrm{sn}(u,m) = \frac{\vartheta_3(0,q)}{\vartheta_2(0,q)} + \frac{\vartheta_1(t,q)}{\vartheta_4(t,q)} + + \mathrm{cn}(u,m) = \frac{\vartheta_4(0,q)}{\vartheta_2(0,q)} + \frac{\vartheta_2(t,q)}{\vartheta_4(t,q)} + + \mathrm{dn}(u,m) = \frac{\vartheta_4(0,q)}{\vartheta_3(0,q)} + \frac{\vartheta_3(t,q)}{\vartheta_4(t,q)}, + +or more generally computes a ratio of two such functions. Here +`t = u/\vartheta_3(0,q)^2`, and `q = q(m)` denotes the nome (see +:func:`~mpmath.nome`). Optionally, you can specify the nome directly +instead of `m` by passing ``q=``, or you can directly +specify the elliptic parameter `k` with ``k=``. + +The first argument should be a two-character string specifying the +function using any combination of ``'s'``, ``'c'``, ``'d'``, ``'n'``. These +letters respectively denote the basic functions +`\mathrm{sn}(u,m)`, `\mathrm{cn}(u,m)`, `\mathrm{dn}(u,m)`, and `1`. +The identifier specifies the ratio of two such functions. +For example, ``'ns'`` identifies the function + +.. math :: + + \mathrm{ns}(u,m) = \frac{1}{\mathrm{sn}(u,m)} + +and ``'cd'`` identifies the function + +.. math :: + + \mathrm{cd}(u,m) = \frac{\mathrm{cn}(u,m)}{\mathrm{dn}(u,m)}. + +If called with only the first argument, a function object +evaluating the chosen function for given arguments is returned. + +**Examples** + +Basic evaluation:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> ellipfun('cd', 3.5, 0.5) + -0.9891101840595543931308394 + >>> ellipfun('cd', 3.5, q=0.25) + 0.07111979240214668158441418 + +The sn-function is doubly periodic in the complex plane with periods +`4 K(m)` and `2 i K(1-m)` (see :func:`~mpmath.ellipk`):: + + >>> sn = ellipfun('sn') + >>> sn(2, 0.25) + 0.9628981775982774425751399 + >>> sn(2+4*ellipk(0.25), 0.25) + 0.9628981775982774425751399 + >>> chop(sn(2+2*j*ellipk(1-0.25), 0.25)) + 0.9628981775982774425751399 + +The cn-function is doubly periodic with periods `4 K(m)` and `2 K(m) + 2 i K(1-m)`:: + + >>> cn = ellipfun('cn') + >>> cn(2, 0.25) + -0.2698649654510865792581416 + >>> cn(2+4*ellipk(0.25), 0.25) + -0.2698649654510865792581416 + >>> chop(cn(2+2*ellipk(0.25)+2*j*ellipk(1-0.25), 0.25)) + -0.2698649654510865792581416 + +The dn-function is doubly periodic with periods `2 K(m)` and `4 i K(1-m)`:: + + >>> dn = ellipfun('dn') + >>> dn(2, 0.25) + 0.8764740583123262286931578 + >>> dn(2+2*ellipk(0.25), 0.25) + 0.8764740583123262286931578 + >>> chop(dn(2+4*j*ellipk(1-0.25), 0.25)) + 0.8764740583123262286931578 + +""" + + +jtheta = r""" +Computes the Jacobi theta function `\vartheta_n(z, q)`, where +`n = 1, 2, 3, 4`, defined by the infinite series: + +.. math :: + + \vartheta_1(z,q) = 2 q^{1/4} \sum_{n=0}^{\infty} + (-1)^n q^{n^2+n\,} \sin((2n+1)z) + + \vartheta_2(z,q) = 2 q^{1/4} \sum_{n=0}^{\infty} + q^{n^{2\,} + n} \cos((2n+1)z) + + \vartheta_3(z,q) = 1 + 2 \sum_{n=1}^{\infty} + q^{n^2\,} \cos(2 n z) + + \vartheta_4(z,q) = 1 + 2 \sum_{n=1}^{\infty} + (-q)^{n^2\,} \cos(2 n z) + +The theta functions are functions of two variables: + +* `z` is the *argument*, an arbitrary real or complex number + +* `q` is the *nome*, which must be a real or complex number + in the unit disk (i.e. `|q| < 1`). For `|q| \ll 1`, the + series converge very quickly, so the Jacobi theta functions + can efficiently be evaluated to high precision. + +The compact notations `\vartheta_n(q) = \vartheta_n(0,q)` +and `\vartheta_n = \vartheta_n(0,q)` are also frequently +encountered. Finally, Jacobi theta functions are frequently +considered as functions of the half-period ratio `\tau` +and then usually denoted by `\vartheta_n(z|\tau)`. + +Optionally, ``jtheta(n, z, q, derivative=d)`` with `d > 0` computes +a `d`-th derivative with respect to `z`. + +**Examples and basic properties** + +Considered as functions of `z`, the Jacobi theta functions may be +viewed as generalizations of the ordinary trigonometric functions +cos and sin. They are periodic functions:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> jtheta(1, 0.25, '0.2') + 0.2945120798627300045053104 + >>> jtheta(1, 0.25 + 2*pi, '0.2') + 0.2945120798627300045053104 + +Indeed, the series defining the theta functions are essentially +trigonometric Fourier series. The coefficients can be retrieved +using :func:`~mpmath.fourier`:: + + >>> mp.dps = 10 + >>> nprint(fourier(lambda x: jtheta(2, x, 0.5), [-pi, pi], 4)) + ([0.0, 1.68179, 0.0, 0.420448, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0]) + +The Jacobi theta functions are also so-called quasiperiodic +functions of `z` and `\tau`, meaning that for fixed `\tau`, +`\vartheta_n(z, q)` and `\vartheta_n(z+\pi \tau, q)` are the same +except for an exponential factor:: + + >>> mp.dps = 25 + >>> tau = 3*j/10 + >>> q = exp(pi*j*tau) + >>> z = 10 + >>> jtheta(4, z+tau*pi, q) + (-0.682420280786034687520568 + 1.526683999721399103332021j) + >>> -exp(-2*j*z)/q * jtheta(4, z, q) + (-0.682420280786034687520568 + 1.526683999721399103332021j) + +The Jacobi theta functions satisfy a huge number of other +functional equations, such as the following identity (valid for +any `q`):: + + >>> q = mpf(3)/10 + >>> jtheta(3,0,q)**4 + 6.823744089352763305137427 + >>> jtheta(2,0,q)**4 + jtheta(4,0,q)**4 + 6.823744089352763305137427 + +Extensive listings of identities satisfied by the Jacobi theta +functions can be found in standard reference works. + +The Jacobi theta functions are related to the gamma function +for special arguments:: + + >>> jtheta(3, 0, exp(-pi)) + 1.086434811213308014575316 + >>> pi**(1/4.) / gamma(3/4.) + 1.086434811213308014575316 + +:func:`~mpmath.jtheta` supports arbitrary precision evaluation and complex +arguments:: + + >>> mp.dps = 50 + >>> jtheta(4, sqrt(2), 0.5) + 2.0549510717571539127004115835148878097035750653737 + >>> mp.dps = 25 + >>> jtheta(4, 1+2j, (1+j)/5) + (7.180331760146805926356634 - 1.634292858119162417301683j) + +Evaluation of derivatives:: + + >>> mp.dps = 25 + >>> jtheta(1, 7, 0.25, 1); diff(lambda z: jtheta(1, z, 0.25), 7) + 1.209857192844475388637236 + 1.209857192844475388637236 + >>> jtheta(1, 7, 0.25, 2); diff(lambda z: jtheta(1, z, 0.25), 7, 2) + -0.2598718791650217206533052 + -0.2598718791650217206533052 + >>> jtheta(2, 7, 0.25, 1); diff(lambda z: jtheta(2, z, 0.25), 7) + -1.150231437070259644461474 + -1.150231437070259644461474 + >>> jtheta(2, 7, 0.25, 2); diff(lambda z: jtheta(2, z, 0.25), 7, 2) + -0.6226636990043777445898114 + -0.6226636990043777445898114 + >>> jtheta(3, 7, 0.25, 1); diff(lambda z: jtheta(3, z, 0.25), 7) + -0.9990312046096634316587882 + -0.9990312046096634316587882 + >>> jtheta(3, 7, 0.25, 2); diff(lambda z: jtheta(3, z, 0.25), 7, 2) + -0.1530388693066334936151174 + -0.1530388693066334936151174 + >>> jtheta(4, 7, 0.25, 1); diff(lambda z: jtheta(4, z, 0.25), 7) + 0.9820995967262793943571139 + 0.9820995967262793943571139 + >>> jtheta(4, 7, 0.25, 2); diff(lambda z: jtheta(4, z, 0.25), 7, 2) + 0.3936902850291437081667755 + 0.3936902850291437081667755 + +**Possible issues** + +For `|q| \ge 1` or `\Im(\tau) \le 0`, :func:`~mpmath.jtheta` raises +``ValueError``. This exception is also raised for `|q|` extremely +close to 1 (or equivalently `\tau` very close to 0), since the +series would converge too slowly:: + + >>> jtheta(1, 10, 0.99999999 * exp(0.5*j)) + Traceback (most recent call last): + ... + ValueError: abs(q) > THETA_Q_LIM = 1.000000 + +""" + +eulernum = r""" +Gives the `n`-th Euler number, defined as the `n`-th derivative of +`\mathrm{sech}(t) = 1/\cosh(t)` evaluated at `t = 0`. Equivalently, the +Euler numbers give the coefficients of the Taylor series + +.. math :: + + \mathrm{sech}(t) = \sum_{n=0}^{\infty} \frac{E_n}{n!} t^n. + +The Euler numbers are closely related to Bernoulli numbers +and Bernoulli polynomials. They can also be evaluated in terms of +Euler polynomials (see :func:`~mpmath.eulerpoly`) as `E_n = 2^n E_n(1/2)`. + +**Examples** + +Computing the first few Euler numbers and verifying that they +agree with the Taylor series:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> [eulernum(n) for n in range(11)] + [1.0, 0.0, -1.0, 0.0, 5.0, 0.0, -61.0, 0.0, 1385.0, 0.0, -50521.0] + >>> chop(diffs(sech, 0, 10)) + [1.0, 0.0, -1.0, 0.0, 5.0, 0.0, -61.0, 0.0, 1385.0, 0.0, -50521.0] + +Euler numbers grow very rapidly. :func:`~mpmath.eulernum` efficiently +computes numerical approximations for large indices:: + + >>> eulernum(50) + -6.053285248188621896314384e+54 + >>> eulernum(1000) + 3.887561841253070615257336e+2371 + >>> eulernum(10**20) + 4.346791453661149089338186e+1936958564106659551331 + +Comparing with an asymptotic formula for the Euler numbers:: + + >>> n = 10**5 + >>> (-1)**(n//2) * 8 * sqrt(n/(2*pi)) * (2*n/(pi*e))**n + 3.69919063017432362805663e+436961 + >>> eulernum(n) + 3.699193712834466537941283e+436961 + +Pass ``exact=True`` to obtain exact values of Euler numbers as integers:: + + >>> print(eulernum(50, exact=True)) + -6053285248188621896314383785111649088103498225146815121 + >>> print(eulernum(200, exact=True) % 10**10) + 1925859625 + >>> eulernum(1001, exact=True) + 0 +""" + +eulerpoly = r""" +Evaluates the Euler polynomial `E_n(z)`, defined by the generating function +representation + +.. math :: + + \frac{2e^{zt}}{e^t+1} = \sum_{n=0}^\infty E_n(z) \frac{t^n}{n!}. + +The Euler polynomials may also be represented in terms of +Bernoulli polynomials (see :func:`~mpmath.bernpoly`) using various formulas, for +example + +.. math :: + + E_n(z) = \frac{2}{n+1} \left( + B_n(z)-2^{n+1}B_n\left(\frac{z}{2}\right) + \right). + +Special values include the Euler numbers `E_n = 2^n E_n(1/2)` (see +:func:`~mpmath.eulernum`). + +**Examples** + +Computing the coefficients of the first few Euler polynomials:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> for n in range(6): + ... chop(taylor(lambda z: eulerpoly(n,z), 0, n)) + ... + [1.0] + [-0.5, 1.0] + [0.0, -1.0, 1.0] + [0.25, 0.0, -1.5, 1.0] + [0.0, 1.0, 0.0, -2.0, 1.0] + [-0.5, 0.0, 2.5, 0.0, -2.5, 1.0] + +Evaluation for arbitrary `z`:: + + >>> eulerpoly(2,3) + 6.0 + >>> eulerpoly(5,4) + 423.5 + >>> eulerpoly(35, 11111111112) + 3.994957561486776072734601e+351 + >>> eulerpoly(4, 10+20j) + (-47990.0 - 235980.0j) + >>> eulerpoly(2, '-3.5e-5') + 0.000035001225 + >>> eulerpoly(3, 0.5) + 0.0 + >>> eulerpoly(55, -10**80) + -1.0e+4400 + >>> eulerpoly(5, -inf) + -inf + >>> eulerpoly(6, -inf) + +inf + +Computing Euler numbers:: + + >>> 2**26 * eulerpoly(26,0.5) + -4087072509293123892361.0 + >>> eulernum(26) + -4087072509293123892361.0 + +Evaluation is accurate for large `n` and small `z`:: + + >>> eulerpoly(100, 0.5) + 2.29047999988194114177943e+108 + >>> eulerpoly(1000, 10.5) + 3.628120031122876847764566e+2070 + >>> eulerpoly(10000, 10.5) + 1.149364285543783412210773e+30688 +""" + +spherharm = r""" +Evaluates the spherical harmonic `Y_l^m(\theta,\phi)`, + +.. math :: + + Y_l^m(\theta,\phi) = \sqrt{\frac{2l+1}{4\pi}\frac{(l-m)!}{(l+m)!}} + P_l^m(\cos \theta) e^{i m \phi} + +where `P_l^m` is an associated Legendre function (see :func:`~mpmath.legenp`). + +Here `\theta \in [0, \pi]` denotes the polar coordinate (ranging +from the north pole to the south pole) and `\phi \in [0, 2 \pi]` denotes the +azimuthal coordinate on a sphere. Care should be used since many different +conventions for spherical coordinate variables are used. + +Usually spherical harmonics are considered for `l \in \mathbb{N}`, +`m \in \mathbb{Z}`, `|m| \le l`. More generally, `l,m,\theta,\phi` +are permitted to be complex numbers. + +.. note :: + + :func:`~mpmath.spherharm` returns a complex number, even if the value is + purely real. + +**Plots** + +.. literalinclude :: /plots/spherharm40.py + +`Y_{4,0}`: + +.. image :: /plots/spherharm40.png + +`Y_{4,1}`: + +.. image :: /plots/spherharm41.png + +`Y_{4,2}`: + +.. image :: /plots/spherharm42.png + +`Y_{4,3}`: + +.. image :: /plots/spherharm43.png + +`Y_{4,4}`: + +.. image :: /plots/spherharm44.png + +**Examples** + +Some low-order spherical harmonics with reference values:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> theta = pi/4 + >>> phi = pi/3 + >>> spherharm(0,0,theta,phi); 0.5*sqrt(1/pi)*expj(0) + (0.2820947917738781434740397 + 0.0j) + (0.2820947917738781434740397 + 0.0j) + >>> spherharm(1,-1,theta,phi); 0.5*sqrt(3/(2*pi))*expj(-phi)*sin(theta) + (0.1221506279757299803965962 - 0.2115710938304086076055298j) + (0.1221506279757299803965962 - 0.2115710938304086076055298j) + >>> spherharm(1,0,theta,phi); 0.5*sqrt(3/pi)*cos(theta)*expj(0) + (0.3454941494713354792652446 + 0.0j) + (0.3454941494713354792652446 + 0.0j) + >>> spherharm(1,1,theta,phi); -0.5*sqrt(3/(2*pi))*expj(phi)*sin(theta) + (-0.1221506279757299803965962 - 0.2115710938304086076055298j) + (-0.1221506279757299803965962 - 0.2115710938304086076055298j) + +With the normalization convention used, the spherical harmonics are orthonormal +on the unit sphere:: + + >>> sphere = [0,pi], [0,2*pi] + >>> dS = lambda t,p: fp.sin(t) # differential element + >>> Y1 = lambda t,p: fp.spherharm(l1,m1,t,p) + >>> Y2 = lambda t,p: fp.conj(fp.spherharm(l2,m2,t,p)) + >>> l1 = l2 = 3; m1 = m2 = 2 + >>> fp.chop(fp.quad(lambda t,p: Y1(t,p)*Y2(t,p)*dS(t,p), *sphere)) + 1.0000000000000007 + >>> m2 = 1 # m1 != m2 + >>> print(fp.chop(fp.quad(lambda t,p: Y1(t,p)*Y2(t,p)*dS(t,p), *sphere))) + 0.0 + +Evaluation is accurate for large orders:: + + >>> spherharm(1000,750,0.5,0.25) + (3.776445785304252879026585e-102 - 5.82441278771834794493484e-102j) + +Evaluation works with complex parameter values:: + + >>> spherharm(1+j, 2j, 2+3j, -0.5j) + (64.44922331113759992154992 + 1981.693919841408089681743j) +""" + +scorergi = r""" +Evaluates the Scorer function + +.. math :: + + \operatorname{Gi}(z) = + \operatorname{Ai}(z) \int_0^z \operatorname{Bi}(t) dt + + \operatorname{Bi}(z) \int_z^{\infty} \operatorname{Ai}(t) dt + +which gives a particular solution to the inhomogeneous Airy +differential equation `f''(z) - z f(z) = 1/\pi`. Another +particular solution is given by the Scorer Hi-function +(:func:`~mpmath.scorerhi`). The two functions are related as +`\operatorname{Gi}(z) + \operatorname{Hi}(z) = \operatorname{Bi}(z)`. + +**Plots** + +.. literalinclude :: /plots/gi.py +.. image :: /plots/gi.png +.. literalinclude :: /plots/gi_c.py +.. image :: /plots/gi_c.png + +**Examples** + +Some values and limits:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> scorergi(0); 1/(power(3,'7/6')*gamma('2/3')) + 0.2049755424820002450503075 + 0.2049755424820002450503075 + >>> diff(scorergi, 0); 1/(power(3,'5/6')*gamma('1/3')) + 0.1494294524512754526382746 + 0.1494294524512754526382746 + >>> scorergi(+inf); scorergi(-inf) + 0.0 + 0.0 + >>> scorergi(1) + 0.2352184398104379375986902 + >>> scorergi(-1) + -0.1166722172960152826494198 + +Evaluation for large arguments:: + + >>> scorergi(10) + 0.03189600510067958798062034 + >>> scorergi(100) + 0.003183105228162961476590531 + >>> scorergi(1000000) + 0.0000003183098861837906721743873 + >>> 1/(pi*1000000) + 0.0000003183098861837906715377675 + >>> scorergi(-1000) + -0.08358288400262780392338014 + >>> scorergi(-100000) + 0.02886866118619660226809581 + >>> scorergi(50+10j) + (0.0061214102799778578790984 - 0.001224335676457532180747917j) + >>> scorergi(-50-10j) + (5.236047850352252236372551e+29 - 3.08254224233701381482228e+29j) + >>> scorergi(100000j) + (-8.806659285336231052679025e+6474077 + 8.684731303500835514850962e+6474077j) + +Verifying the connection between Gi and Hi:: + + >>> z = 0.25 + >>> scorergi(z) + scorerhi(z) + 0.7287469039362150078694543 + >>> airybi(z) + 0.7287469039362150078694543 + +Verifying the differential equation:: + + >>> for z in [-3.4, 0, 2.5, 1+2j]: + ... chop(diff(scorergi,z,2) - z*scorergi(z)) + ... + -0.3183098861837906715377675 + -0.3183098861837906715377675 + -0.3183098861837906715377675 + -0.3183098861837906715377675 + +Verifying the integral representation:: + + >>> z = 0.5 + >>> scorergi(z) + 0.2447210432765581976910539 + >>> Ai,Bi = airyai,airybi + >>> Bi(z)*(Ai(inf,-1)-Ai(z,-1)) + Ai(z)*(Bi(z,-1)-Bi(0,-1)) + 0.2447210432765581976910539 + +**References** + +1. [DLMF]_ section 9.12: Scorer Functions + +""" + +scorerhi = r""" +Evaluates the second Scorer function + +.. math :: + + \operatorname{Hi}(z) = + \operatorname{Bi}(z) \int_{-\infty}^z \operatorname{Ai}(t) dt - + \operatorname{Ai}(z) \int_{-\infty}^z \operatorname{Bi}(t) dt + +which gives a particular solution to the inhomogeneous Airy +differential equation `f''(z) - z f(z) = 1/\pi`. See also +:func:`~mpmath.scorergi`. + +**Plots** + +.. literalinclude :: /plots/hi.py +.. image :: /plots/hi.png +.. literalinclude :: /plots/hi_c.py +.. image :: /plots/hi_c.png + +**Examples** + +Some values and limits:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> scorerhi(0); 2/(power(3,'7/6')*gamma('2/3')) + 0.4099510849640004901006149 + 0.4099510849640004901006149 + >>> diff(scorerhi,0); 2/(power(3,'5/6')*gamma('1/3')) + 0.2988589049025509052765491 + 0.2988589049025509052765491 + >>> scorerhi(+inf); scorerhi(-inf) + +inf + 0.0 + >>> scorerhi(1) + 0.9722051551424333218376886 + >>> scorerhi(-1) + 0.2206696067929598945381098 + +Evaluation for large arguments:: + + >>> scorerhi(10) + 455641153.5163291358991077 + >>> scorerhi(100) + 6.041223996670201399005265e+288 + >>> scorerhi(1000000) + 7.138269638197858094311122e+289529652 + >>> scorerhi(-10) + 0.0317685352825022727415011 + >>> scorerhi(-100) + 0.003183092495767499864680483 + >>> scorerhi(100j) + (-6.366197716545672122983857e-9 + 0.003183098861710582761688475j) + >>> scorerhi(50+50j) + (-5.322076267321435669290334e+63 + 1.478450291165243789749427e+65j) + >>> scorerhi(-1000-1000j) + (0.0001591549432510502796565538 - 0.000159154943091895334973109j) + +Verifying the differential equation:: + + >>> for z in [-3.4, 0, 2, 1+2j]: + ... chop(diff(scorerhi,z,2) - z*scorerhi(z)) + ... + 0.3183098861837906715377675 + 0.3183098861837906715377675 + 0.3183098861837906715377675 + 0.3183098861837906715377675 + +Verifying the integral representation:: + + >>> z = 0.5 + >>> scorerhi(z) + 0.6095559998265972956089949 + >>> Ai,Bi = airyai,airybi + >>> Bi(z)*(Ai(z,-1)-Ai(-inf,-1)) - Ai(z)*(Bi(z,-1)-Bi(-inf,-1)) + 0.6095559998265972956089949 + +""" + + +stirling1 = r""" +Gives the Stirling number of the first kind `s(n,k)`, defined by + +.. math :: + + x(x-1)(x-2)\cdots(x-n+1) = \sum_{k=0}^n s(n,k) x^k. + +The value is computed using an integer recurrence. The implementation +is not optimized for approximating large values quickly. + +**Examples** + +Comparing with the generating function:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> taylor(lambda x: ff(x, 5), 0, 5) + [0.0, 24.0, -50.0, 35.0, -10.0, 1.0] + >>> [stirling1(5, k) for k in range(6)] + [0.0, 24.0, -50.0, 35.0, -10.0, 1.0] + +Recurrence relation:: + + >>> n, k = 5, 3 + >>> stirling1(n+1,k) + n*stirling1(n,k) - stirling1(n,k-1) + 0.0 + +The matrices of Stirling numbers of first and second kind are inverses +of each other:: + + >>> A = matrix(5, 5); B = matrix(5, 5) + >>> for n in range(5): + ... for k in range(5): + ... A[n,k] = stirling1(n,k) + ... B[n,k] = stirling2(n,k) + ... + >>> A * B + [1.0 0.0 0.0 0.0 0.0] + [0.0 1.0 0.0 0.0 0.0] + [0.0 0.0 1.0 0.0 0.0] + [0.0 0.0 0.0 1.0 0.0] + [0.0 0.0 0.0 0.0 1.0] + +Pass ``exact=True`` to obtain exact values of Stirling numbers as integers:: + + >>> stirling1(42, 5) + -2.864498971768501633736628e+50 + >>> print(stirling1(42, 5, exact=True)) + -286449897176850163373662803014001546235808317440000 + +""" + +stirling2 = r""" +Gives the Stirling number of the second kind `S(n,k)`, defined by + +.. math :: + + x^n = \sum_{k=0}^n S(n,k) x(x-1)(x-2)\cdots(x-k+1) + +The value is computed using integer arithmetic to evaluate a power sum. +The implementation is not optimized for approximating large values quickly. + +**Examples** + +Comparing with the generating function:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> taylor(lambda x: sum(stirling2(5,k) * ff(x,k) for k in range(6)), 0, 5) + [0.0, 0.0, 0.0, 0.0, 0.0, 1.0] + +Recurrence relation:: + + >>> n, k = 5, 3 + >>> stirling2(n+1,k) - k*stirling2(n,k) - stirling2(n,k-1) + 0.0 + +Pass ``exact=True`` to obtain exact values of Stirling numbers as integers:: + + >>> stirling2(52, 10) + 2.641822121003543906807485e+45 + >>> print(stirling2(52, 10, exact=True)) + 2641822121003543906807485307053638921722527655 + + +""" + +squarew = r""" +Computes the square wave function using the definition: + +.. math:: + x(t) = A(-1)^{\left\lfloor{2t / P}\right\rfloor} + +where `P` is the period of the wave and `A` is the amplitude. + +**Examples** + +Square wave with period = 2, amplitude = 1 :: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> squarew(0,1,2) + 1.0 + >>> squarew(0.5,1,2) + 1.0 + >>> squarew(1,1,2) + -1.0 + >>> squarew(1.5,1,2) + -1.0 + >>> squarew(2,1,2) + 1.0 +""" + +trianglew = r""" +Computes the triangle wave function using the definition: + +.. math:: + x(t) = 2A\left(\frac{1}{2}-\left|1-2 \operatorname{frac}\left(\frac{x}{P}+\frac{1}{4}\right)\right|\right) + +where :math:`\operatorname{frac}\left(\frac{t}{T}\right) = \frac{t}{T}-\left\lfloor{\frac{t}{T}}\right\rfloor` +, `P` is the period of the wave, and `A` is the amplitude. + +**Examples** + +Triangle wave with period = 2, amplitude = 1 :: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> trianglew(0,1,2) + 0.0 + >>> trianglew(0.25,1,2) + 0.5 + >>> trianglew(0.5,1,2) + 1.0 + >>> trianglew(1,1,2) + 0.0 + >>> trianglew(1.5,1,2) + -1.0 + >>> trianglew(2,1,2) + 0.0 +""" + +sawtoothw = r""" +Computes the sawtooth wave function using the definition: + +.. math:: + x(t) = A\operatorname{frac}\left(\frac{t}{T}\right) + +where :math:`\operatorname{frac}\left(\frac{t}{T}\right) = \frac{t}{T}-\left\lfloor{\frac{t}{T}}\right\rfloor`, +`P` is the period of the wave, and `A` is the amplitude. + +**Examples** + +Sawtooth wave with period = 2, amplitude = 1 :: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> sawtoothw(0,1,2) + 0.0 + >>> sawtoothw(0.5,1,2) + 0.25 + >>> sawtoothw(1,1,2) + 0.5 + >>> sawtoothw(1.5,1,2) + 0.75 + >>> sawtoothw(2,1,2) + 0.0 +""" + +unit_triangle = r""" +Computes the unit triangle using the definition: + +.. math:: + x(t) = A(-\left| t \right| + 1) + +where `A` is the amplitude. + +**Examples** + +Unit triangle with amplitude = 1 :: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> unit_triangle(-1,1) + 0.0 + >>> unit_triangle(-0.5,1) + 0.5 + >>> unit_triangle(0,1) + 1.0 + >>> unit_triangle(0.5,1) + 0.5 + >>> unit_triangle(1,1) + 0.0 +""" + +sigmoid = r""" +Computes the sigmoid function using the definition: + +.. math:: + x(t) = \frac{A}{1 + e^{-t}} + +where `A` is the amplitude. + +**Examples** + +Sigmoid function with amplitude = 1 :: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> sigmoid(-1,1) + 0.2689414213699951207488408 + >>> sigmoid(-0.5,1) + 0.3775406687981454353610994 + >>> sigmoid(0,1) + 0.5 + >>> sigmoid(0.5,1) + 0.6224593312018545646389006 + >>> sigmoid(1,1) + 0.7310585786300048792511592 + +""" diff --git a/lib/python3.12/site-packages/mpmath/functions/__init__.py b/lib/python3.12/site-packages/mpmath/functions/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..5896ed0579eceab086dc5c67eaa649b6061a53dc --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/functions/__init__.py @@ -0,0 +1,14 @@ +from . import functions +# Hack to update methods +from . import factorials +from . import hypergeometric +from . import expintegrals +from . import bessel +from . import orthogonal +from . import theta +from . import elliptic +from . import signals +from . import zeta +from . import rszeta +from . import zetazeros +from . import qfunctions diff --git a/lib/python3.12/site-packages/mpmath/functions/__pycache__/__init__.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/functions/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..313d50d16128800d7b302d0da0b4c33706a601e0 Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/functions/__pycache__/__init__.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/functions/__pycache__/bessel.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/functions/__pycache__/bessel.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bd741f4ee4633ef6cb535d36715a164b84adc909 Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/functions/__pycache__/bessel.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/functions/__pycache__/elliptic.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/functions/__pycache__/elliptic.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e167c708992ccbf4c7f8f8f90178715772b5e5a7 Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/functions/__pycache__/elliptic.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/functions/__pycache__/expintegrals.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/functions/__pycache__/expintegrals.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..da48f6bddadabd11a7c6dfc6a8b65a79f1dc8047 Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/functions/__pycache__/expintegrals.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/functions/__pycache__/factorials.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/functions/__pycache__/factorials.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..537b949577bc08fb6ba49fc0bdb156d47e2ceefb Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/functions/__pycache__/factorials.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/functions/__pycache__/functions.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/functions/__pycache__/functions.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bb9b27d0f33594381bc5378a92d5dfa4bf0162c4 Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/functions/__pycache__/functions.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/functions/__pycache__/hypergeometric.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/functions/__pycache__/hypergeometric.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0d430bc619e1061d38aaf89a1d857b14269ac3af Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/functions/__pycache__/hypergeometric.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/functions/__pycache__/orthogonal.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/functions/__pycache__/orthogonal.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0c2a0205dc253be1a31cb8248e2b4812d5cb719a Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/functions/__pycache__/orthogonal.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/functions/__pycache__/qfunctions.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/functions/__pycache__/qfunctions.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0bdd0f1decbc584eb0c4f7405fdf2dab9c5f71f8 Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/functions/__pycache__/qfunctions.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/functions/__pycache__/rszeta.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/functions/__pycache__/rszeta.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..312e96531802ad36c041b93cf00fbb86e5771db7 Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/functions/__pycache__/rszeta.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/functions/__pycache__/signals.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/functions/__pycache__/signals.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..983597534c65ba8b52df1d961d27e36f30f8f282 Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/functions/__pycache__/signals.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/functions/__pycache__/theta.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/functions/__pycache__/theta.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4d8d0c8ecf3a78451091a7b59d780528e12031ce Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/functions/__pycache__/theta.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/functions/__pycache__/zeta.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/functions/__pycache__/zeta.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e2949a9f7bc289837733db9ee7c4402d57a2397c Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/functions/__pycache__/zeta.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/functions/__pycache__/zetazeros.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/functions/__pycache__/zetazeros.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..86b99a1034e120ccc04fe0ee6300b84ff8a73ce6 Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/functions/__pycache__/zetazeros.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/functions/bessel.py b/lib/python3.12/site-packages/mpmath/functions/bessel.py new file mode 100644 index 0000000000000000000000000000000000000000..8b41d87bb0118de61d5561433dabcb181f872f84 --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/functions/bessel.py @@ -0,0 +1,1108 @@ +from .functions import defun, defun_wrapped + +@defun +def j0(ctx, x): + """Computes the Bessel function `J_0(x)`. See :func:`~mpmath.besselj`.""" + return ctx.besselj(0, x) + +@defun +def j1(ctx, x): + """Computes the Bessel function `J_1(x)`. See :func:`~mpmath.besselj`.""" + return ctx.besselj(1, x) + +@defun +def besselj(ctx, n, z, derivative=0, **kwargs): + if type(n) is int: + n_isint = True + else: + n = ctx.convert(n) + n_isint = ctx.isint(n) + if n_isint: + n = int(ctx._re(n)) + if n_isint and n < 0: + return (-1)**n * ctx.besselj(-n, z, derivative, **kwargs) + z = ctx.convert(z) + M = ctx.mag(z) + if derivative: + d = ctx.convert(derivative) + # TODO: the integer special-casing shouldn't be necessary. + # However, the hypergeometric series gets inaccurate for large d + # because of inaccurate pole cancellation at a pole far from + # zero (needs to be fixed in hypercomb or hypsum) + if ctx.isint(d) and d >= 0: + d = int(d) + orig = ctx.prec + try: + ctx.prec += 15 + v = ctx.fsum((-1)**k * ctx.binomial(d,k) * ctx.besselj(2*k+n-d,z) + for k in range(d+1)) + finally: + ctx.prec = orig + v *= ctx.mpf(2)**(-d) + else: + def h(n,d): + r = ctx.fmul(ctx.fmul(z, z, prec=ctx.prec+M), -0.25, exact=True) + B = [0.5*(n-d+1), 0.5*(n-d+2)] + T = [([2,ctx.pi,z],[d-2*n,0.5,n-d],[],B,[(n+1)*0.5,(n+2)*0.5],B+[n+1],r)] + return T + v = ctx.hypercomb(h, [n,d], **kwargs) + else: + # Fast case: J_n(x), n int, appropriate magnitude for fixed-point calculation + if (not derivative) and n_isint and abs(M) < 10 and abs(n) < 20: + try: + return ctx._besselj(n, z) + except NotImplementedError: + pass + if not z: + if not n: + v = ctx.one + n+z + elif ctx.re(n) > 0: + v = n*z + else: + v = ctx.inf + z + n + else: + #v = 0 + orig = ctx.prec + try: + # XXX: workaround for accuracy in low level hypergeometric series + # when alternating, large arguments + ctx.prec += min(3*abs(M), ctx.prec) + w = ctx.fmul(z, 0.5, exact=True) + def h(n): + r = ctx.fneg(ctx.fmul(w, w, prec=max(0,ctx.prec+M)), exact=True) + return [([w], [n], [], [n+1], [], [n+1], r)] + v = ctx.hypercomb(h, [n], **kwargs) + finally: + ctx.prec = orig + v = +v + return v + +@defun +def besseli(ctx, n, z, derivative=0, **kwargs): + n = ctx.convert(n) + z = ctx.convert(z) + if not z: + if derivative: + raise ValueError + if not n: + # I(0,0) = 1 + return 1+n+z + if ctx.isint(n): + return 0*(n+z) + r = ctx.re(n) + if r == 0: + return ctx.nan*(n+z) + elif r > 0: + return 0*(n+z) + else: + return ctx.inf+(n+z) + M = ctx.mag(z) + if derivative: + d = ctx.convert(derivative) + def h(n,d): + r = ctx.fmul(ctx.fmul(z, z, prec=ctx.prec+M), 0.25, exact=True) + B = [0.5*(n-d+1), 0.5*(n-d+2), n+1] + T = [([2,ctx.pi,z],[d-2*n,0.5,n-d],[n+1],B,[(n+1)*0.5,(n+2)*0.5],B,r)] + return T + v = ctx.hypercomb(h, [n,d], **kwargs) + else: + def h(n): + w = ctx.fmul(z, 0.5, exact=True) + r = ctx.fmul(w, w, prec=max(0,ctx.prec+M)) + return [([w], [n], [], [n+1], [], [n+1], r)] + v = ctx.hypercomb(h, [n], **kwargs) + return v + +@defun_wrapped +def bessely(ctx, n, z, derivative=0, **kwargs): + if not z: + if derivative: + # Not implemented + raise ValueError + if not n: + # ~ log(z/2) + return -ctx.inf + (n+z) + if ctx.im(n): + return ctx.nan * (n+z) + r = ctx.re(n) + q = n+0.5 + if ctx.isint(q): + if n > 0: + return -ctx.inf + (n+z) + else: + return 0 * (n+z) + if r < 0 and int(ctx.floor(q)) % 2: + return ctx.inf + (n+z) + else: + return ctx.ninf + (n+z) + # XXX: use hypercomb + ctx.prec += 10 + m, d = ctx.nint_distance(n) + if d < -ctx.prec: + h = +ctx.eps + ctx.prec *= 2 + n += h + elif d < 0: + ctx.prec -= d + # TODO: avoid cancellation for imaginary arguments + cos, sin = ctx.cospi_sinpi(n) + return (ctx.besselj(n,z,derivative,**kwargs)*cos - \ + ctx.besselj(-n,z,derivative,**kwargs))/sin + +@defun_wrapped +def besselk(ctx, n, z, **kwargs): + if not z: + return ctx.inf + M = ctx.mag(z) + if M < 1: + # Represent as limit definition + def h(n): + r = (z/2)**2 + T1 = [z, 2], [-n, n-1], [n], [], [], [1-n], r + T2 = [z, 2], [n, -n-1], [-n], [], [], [1+n], r + return T1, T2 + # We could use the limit definition always, but it leads + # to very bad cancellation (of exponentially large terms) + # for large real z + # Instead represent in terms of 2F0 + else: + ctx.prec += M + def h(n): + return [([ctx.pi/2, z, ctx.exp(-z)], [0.5,-0.5,1], [], [], \ + [n+0.5, 0.5-n], [], -1/(2*z))] + return ctx.hypercomb(h, [n], **kwargs) + +@defun_wrapped +def hankel1(ctx,n,x,**kwargs): + return ctx.besselj(n,x,**kwargs) + ctx.j*ctx.bessely(n,x,**kwargs) + +@defun_wrapped +def hankel2(ctx,n,x,**kwargs): + return ctx.besselj(n,x,**kwargs) - ctx.j*ctx.bessely(n,x,**kwargs) + +@defun_wrapped +def whitm(ctx,k,m,z,**kwargs): + if z == 0: + # M(k,m,z) = 0^(1/2+m) + if ctx.re(m) > -0.5: + return z + elif ctx.re(m) < -0.5: + return ctx.inf + z + else: + return ctx.nan * z + x = ctx.fmul(-0.5, z, exact=True) + y = 0.5+m + return ctx.exp(x) * z**y * ctx.hyp1f1(y-k, 1+2*m, z, **kwargs) + +@defun_wrapped +def whitw(ctx,k,m,z,**kwargs): + if z == 0: + g = abs(ctx.re(m)) + if g < 0.5: + return z + elif g > 0.5: + return ctx.inf + z + else: + return ctx.nan * z + x = ctx.fmul(-0.5, z, exact=True) + y = 0.5+m + return ctx.exp(x) * z**y * ctx.hyperu(y-k, 1+2*m, z, **kwargs) + +@defun +def hyperu(ctx, a, b, z, **kwargs): + a, atype = ctx._convert_param(a) + b, btype = ctx._convert_param(b) + z = ctx.convert(z) + if not z: + if ctx.re(b) <= 1: + return ctx.gammaprod([1-b],[a-b+1]) + else: + return ctx.inf + z + bb = 1+a-b + bb, bbtype = ctx._convert_param(bb) + try: + orig = ctx.prec + try: + ctx.prec += 10 + v = ctx.hypsum(2, 0, (atype, bbtype), [a, bb], -1/z, maxterms=ctx.prec) + return v / z**a + finally: + ctx.prec = orig + except ctx.NoConvergence: + pass + def h(a,b): + w = ctx.sinpi(b) + T1 = ([ctx.pi,w],[1,-1],[],[a-b+1,b],[a],[b],z) + T2 = ([-ctx.pi,w,z],[1,-1,1-b],[],[a,2-b],[a-b+1],[2-b],z) + return T1, T2 + return ctx.hypercomb(h, [a,b], **kwargs) + +@defun +def struveh(ctx,n,z, **kwargs): + n = ctx.convert(n) + z = ctx.convert(z) + # http://functions.wolfram.com/Bessel-TypeFunctions/StruveH/26/01/02/ + def h(n): + return [([z/2, 0.5*ctx.sqrt(ctx.pi)], [n+1, -1], [], [n+1.5], [1], [1.5, n+1.5], -(z/2)**2)] + return ctx.hypercomb(h, [n], **kwargs) + +@defun +def struvel(ctx,n,z, **kwargs): + n = ctx.convert(n) + z = ctx.convert(z) + # http://functions.wolfram.com/Bessel-TypeFunctions/StruveL/26/01/02/ + def h(n): + return [([z/2, 0.5*ctx.sqrt(ctx.pi)], [n+1, -1], [], [n+1.5], [1], [1.5, n+1.5], (z/2)**2)] + return ctx.hypercomb(h, [n], **kwargs) + +def _anger(ctx,which,v,z,**kwargs): + v = ctx._convert_param(v)[0] + z = ctx.convert(z) + def h(v): + b = ctx.mpq_1_2 + u = v*b + m = b*3 + a1,a2,b1,b2 = m-u, m+u, 1-u, 1+u + c, s = ctx.cospi_sinpi(u) + if which == 0: + A, B = [b*z, s], [c] + if which == 1: + A, B = [b*z, -c], [s] + w = ctx.square_exp_arg(z, mult=-0.25) + T1 = A, [1, 1], [], [a1,a2], [1], [a1,a2], w + T2 = B, [1], [], [b1,b2], [1], [b1,b2], w + return T1, T2 + return ctx.hypercomb(h, [v], **kwargs) + +@defun +def angerj(ctx, v, z, **kwargs): + return _anger(ctx, 0, v, z, **kwargs) + +@defun +def webere(ctx, v, z, **kwargs): + return _anger(ctx, 1, v, z, **kwargs) + +@defun +def lommels1(ctx, u, v, z, **kwargs): + u = ctx._convert_param(u)[0] + v = ctx._convert_param(v)[0] + z = ctx.convert(z) + def h(u,v): + b = ctx.mpq_1_2 + w = ctx.square_exp_arg(z, mult=-0.25) + return ([u-v+1, u+v+1, z], [-1, -1, u+1], [], [], [1], \ + [b*(u-v+3),b*(u+v+3)], w), + return ctx.hypercomb(h, [u,v], **kwargs) + +@defun +def lommels2(ctx, u, v, z, **kwargs): + u = ctx._convert_param(u)[0] + v = ctx._convert_param(v)[0] + z = ctx.convert(z) + # Asymptotic expansion (GR p. 947) -- need to be careful + # not to use for small arguments + # def h(u,v): + # b = ctx.mpq_1_2 + # w = -(z/2)**(-2) + # return ([z], [u-1], [], [], [b*(1-u+v)], [b*(1-u-v)], w), + def h(u,v): + b = ctx.mpq_1_2 + w = ctx.square_exp_arg(z, mult=-0.25) + T1 = [u-v+1, u+v+1, z], [-1, -1, u+1], [], [], [1], [b*(u-v+3),b*(u+v+3)], w + T2 = [2, z], [u+v-1, -v], [v, b*(u+v+1)], [b*(v-u+1)], [], [1-v], w + T3 = [2, z], [u-v-1, v], [-v, b*(u-v+1)], [b*(1-u-v)], [], [1+v], w + #c1 = ctx.cospi((u-v)*b) + #c2 = ctx.cospi((u+v)*b) + #s = ctx.sinpi(v) + #r1 = (u-v+1)*b + #r2 = (u+v+1)*b + #T2 = [c1, s, z, 2], [1, -1, -v, v], [], [-v+1], [], [-v+1], w + #T3 = [-c2, s, z, 2], [1, -1, v, -v], [], [v+1], [], [v+1], w + #T2 = [c1, s, z, 2], [1, -1, -v, v+u-1], [r1, r2], [-v+1], [], [-v+1], w + #T3 = [-c2, s, z, 2], [1, -1, v, -v+u-1], [r1, r2], [v+1], [], [v+1], w + return T1, T2, T3 + return ctx.hypercomb(h, [u,v], **kwargs) + +@defun +def ber(ctx, n, z, **kwargs): + n = ctx.convert(n) + z = ctx.convert(z) + # http://functions.wolfram.com/Bessel-TypeFunctions/KelvinBer2/26/01/02/0001/ + def h(n): + r = -(z/4)**4 + cos, sin = ctx.cospi_sinpi(-0.75*n) + T1 = [cos, z/2], [1, n], [], [n+1], [], [0.5, 0.5*(n+1), 0.5*n+1], r + T2 = [sin, z/2], [1, n+2], [], [n+2], [], [1.5, 0.5*(n+3), 0.5*n+1], r + return T1, T2 + return ctx.hypercomb(h, [n], **kwargs) + +@defun +def bei(ctx, n, z, **kwargs): + n = ctx.convert(n) + z = ctx.convert(z) + # http://functions.wolfram.com/Bessel-TypeFunctions/KelvinBei2/26/01/02/0001/ + def h(n): + r = -(z/4)**4 + cos, sin = ctx.cospi_sinpi(0.75*n) + T1 = [cos, z/2], [1, n+2], [], [n+2], [], [1.5, 0.5*(n+3), 0.5*n+1], r + T2 = [sin, z/2], [1, n], [], [n+1], [], [0.5, 0.5*(n+1), 0.5*n+1], r + return T1, T2 + return ctx.hypercomb(h, [n], **kwargs) + +@defun +def ker(ctx, n, z, **kwargs): + n = ctx.convert(n) + z = ctx.convert(z) + # http://functions.wolfram.com/Bessel-TypeFunctions/KelvinKer2/26/01/02/0001/ + def h(n): + r = -(z/4)**4 + cos1, sin1 = ctx.cospi_sinpi(0.25*n) + cos2, sin2 = ctx.cospi_sinpi(0.75*n) + T1 = [2, z, 4*cos1], [-n-3, n, 1], [-n], [], [], [0.5, 0.5*(1+n), 0.5*(n+2)], r + T2 = [2, z, -sin1], [-n-3, 2+n, 1], [-n-1], [], [], [1.5, 0.5*(3+n), 0.5*(n+2)], r + T3 = [2, z, 4*cos2], [n-3, -n, 1], [n], [], [], [0.5, 0.5*(1-n), 1-0.5*n], r + T4 = [2, z, -sin2], [n-3, 2-n, 1], [n-1], [], [], [1.5, 0.5*(3-n), 1-0.5*n], r + return T1, T2, T3, T4 + return ctx.hypercomb(h, [n], **kwargs) + +@defun +def kei(ctx, n, z, **kwargs): + n = ctx.convert(n) + z = ctx.convert(z) + # http://functions.wolfram.com/Bessel-TypeFunctions/KelvinKei2/26/01/02/0001/ + def h(n): + r = -(z/4)**4 + cos1, sin1 = ctx.cospi_sinpi(0.75*n) + cos2, sin2 = ctx.cospi_sinpi(0.25*n) + T1 = [-cos1, 2, z], [1, n-3, 2-n], [n-1], [], [], [1.5, 0.5*(3-n), 1-0.5*n], r + T2 = [-sin1, 2, z], [1, n-1, -n], [n], [], [], [0.5, 0.5*(1-n), 1-0.5*n], r + T3 = [-sin2, 2, z], [1, -n-1, n], [-n], [], [], [0.5, 0.5*(n+1), 0.5*(n+2)], r + T4 = [-cos2, 2, z], [1, -n-3, n+2], [-n-1], [], [], [1.5, 0.5*(n+3), 0.5*(n+2)], r + return T1, T2, T3, T4 + return ctx.hypercomb(h, [n], **kwargs) + +# TODO: do this more generically? +def c_memo(f): + name = f.__name__ + def f_wrapped(ctx): + cache = ctx._misc_const_cache + prec = ctx.prec + p,v = cache.get(name, (-1,0)) + if p >= prec: + return +v + else: + cache[name] = (prec, f(ctx)) + return cache[name][1] + return f_wrapped + +@c_memo +def _airyai_C1(ctx): + return 1 / (ctx.cbrt(9) * ctx.gamma(ctx.mpf(2)/3)) + +@c_memo +def _airyai_C2(ctx): + return -1 / (ctx.cbrt(3) * ctx.gamma(ctx.mpf(1)/3)) + +@c_memo +def _airybi_C1(ctx): + return 1 / (ctx.nthroot(3,6) * ctx.gamma(ctx.mpf(2)/3)) + +@c_memo +def _airybi_C2(ctx): + return ctx.nthroot(3,6) / ctx.gamma(ctx.mpf(1)/3) + +def _airybi_n2_inf(ctx): + prec = ctx.prec + try: + v = ctx.power(3,'2/3')*ctx.gamma('2/3')/(2*ctx.pi) + finally: + ctx.prec = prec + return +v + +# Derivatives at z = 0 +# TODO: could be expressed more elegantly using triple factorials +def _airyderiv_0(ctx, z, n, ntype, which): + if ntype == 'Z': + if n < 0: + return z + r = ctx.mpq_1_3 + prec = ctx.prec + try: + ctx.prec += 10 + v = ctx.gamma((n+1)*r) * ctx.power(3,n*r) / ctx.pi + if which == 0: + v *= ctx.sinpi(2*(n+1)*r) + v /= ctx.power(3,'2/3') + else: + v *= abs(ctx.sinpi(2*(n+1)*r)) + v /= ctx.power(3,'1/6') + finally: + ctx.prec = prec + return +v + z + else: + # singular (does the limit exist?) + raise NotImplementedError + +@defun +def airyai(ctx, z, derivative=0, **kwargs): + z = ctx.convert(z) + if derivative: + n, ntype = ctx._convert_param(derivative) + else: + n = 0 + # Values at infinities + if not ctx.isnormal(z) and z: + if n and ntype == 'Z': + if n == -1: + if z == ctx.inf: + return ctx.mpf(1)/3 + 1/z + if z == ctx.ninf: + return ctx.mpf(-2)/3 + 1/z + if n < -1: + if z == ctx.inf: + return z + if z == ctx.ninf: + return (-1)**n * (-z) + if (not n) and z == ctx.inf or z == ctx.ninf: + return 1/z + # TODO: limits + raise ValueError("essential singularity of Ai(z)") + # Account for exponential scaling + if z: + extraprec = max(0, int(1.5*ctx.mag(z))) + else: + extraprec = 0 + if n: + if n == 1: + def h(): + # http://functions.wolfram.com/03.07.06.0005.01 + if ctx._re(z) > 4: + ctx.prec += extraprec + w = z**1.5; r = -0.75/w; u = -2*w/3 + ctx.prec -= extraprec + C = -ctx.exp(u)/(2*ctx.sqrt(ctx.pi))*ctx.nthroot(z,4) + return ([C],[1],[],[],[(-1,6),(7,6)],[],r), + # http://functions.wolfram.com/03.07.26.0001.01 + else: + ctx.prec += extraprec + w = z**3 / 9 + ctx.prec -= extraprec + C1 = _airyai_C1(ctx) * 0.5 + C2 = _airyai_C2(ctx) + T1 = [C1,z],[1,2],[],[],[],[ctx.mpq_5_3],w + T2 = [C2],[1],[],[],[],[ctx.mpq_1_3],w + return T1, T2 + return ctx.hypercomb(h, [], **kwargs) + else: + if z == 0: + return _airyderiv_0(ctx, z, n, ntype, 0) + # http://functions.wolfram.com/03.05.20.0004.01 + def h(n): + ctx.prec += extraprec + w = z**3/9 + ctx.prec -= extraprec + q13,q23,q43 = ctx.mpq_1_3, ctx.mpq_2_3, ctx.mpq_4_3 + a1=q13; a2=1; b1=(1-n)*q13; b2=(2-n)*q13; b3=1-n*q13 + T1 = [3, z], [n-q23, -n], [a1], [b1,b2,b3], \ + [a1,a2], [b1,b2,b3], w + a1=q23; b1=(2-n)*q13; b2=1-n*q13; b3=(4-n)*q13 + T2 = [3, z, -z], [n-q43, -n, 1], [a1], [b1,b2,b3], \ + [a1,a2], [b1,b2,b3], w + return T1, T2 + v = ctx.hypercomb(h, [n], **kwargs) + if ctx._is_real_type(z) and ctx.isint(n): + v = ctx._re(v) + return v + else: + def h(): + if ctx._re(z) > 4: + # We could use 1F1, but it results in huge cancellation; + # the following expansion is better. + # TODO: asymptotic series for derivatives + ctx.prec += extraprec + w = z**1.5; r = -0.75/w; u = -2*w/3 + ctx.prec -= extraprec + C = ctx.exp(u)/(2*ctx.sqrt(ctx.pi)*ctx.nthroot(z,4)) + return ([C],[1],[],[],[(1,6),(5,6)],[],r), + else: + ctx.prec += extraprec + w = z**3 / 9 + ctx.prec -= extraprec + C1 = _airyai_C1(ctx) + C2 = _airyai_C2(ctx) + T1 = [C1],[1],[],[],[],[ctx.mpq_2_3],w + T2 = [z*C2],[1],[],[],[],[ctx.mpq_4_3],w + return T1, T2 + return ctx.hypercomb(h, [], **kwargs) + +@defun +def airybi(ctx, z, derivative=0, **kwargs): + z = ctx.convert(z) + if derivative: + n, ntype = ctx._convert_param(derivative) + else: + n = 0 + # Values at infinities + if not ctx.isnormal(z) and z: + if n and ntype == 'Z': + if z == ctx.inf: + return z + if z == ctx.ninf: + if n == -1: + return 1/z + if n == -2: + return _airybi_n2_inf(ctx) + if n < -2: + return (-1)**n * (-z) + if not n: + if z == ctx.inf: + return z + if z == ctx.ninf: + return 1/z + # TODO: limits + raise ValueError("essential singularity of Bi(z)") + if z: + extraprec = max(0, int(1.5*ctx.mag(z))) + else: + extraprec = 0 + if n: + if n == 1: + # http://functions.wolfram.com/03.08.26.0001.01 + def h(): + ctx.prec += extraprec + w = z**3 / 9 + ctx.prec -= extraprec + C1 = _airybi_C1(ctx)*0.5 + C2 = _airybi_C2(ctx) + T1 = [C1,z],[1,2],[],[],[],[ctx.mpq_5_3],w + T2 = [C2],[1],[],[],[],[ctx.mpq_1_3],w + return T1, T2 + return ctx.hypercomb(h, [], **kwargs) + else: + if z == 0: + return _airyderiv_0(ctx, z, n, ntype, 1) + def h(n): + ctx.prec += extraprec + w = z**3/9 + ctx.prec -= extraprec + q13,q23,q43 = ctx.mpq_1_3, ctx.mpq_2_3, ctx.mpq_4_3 + q16 = ctx.mpq_1_6 + q56 = ctx.mpq_5_6 + a1=q13; a2=1; b1=(1-n)*q13; b2=(2-n)*q13; b3=1-n*q13 + T1 = [3, z], [n-q16, -n], [a1], [b1,b2,b3], \ + [a1,a2], [b1,b2,b3], w + a1=q23; b1=(2-n)*q13; b2=1-n*q13; b3=(4-n)*q13 + T2 = [3, z], [n-q56, 1-n], [a1], [b1,b2,b3], \ + [a1,a2], [b1,b2,b3], w + return T1, T2 + v = ctx.hypercomb(h, [n], **kwargs) + if ctx._is_real_type(z) and ctx.isint(n): + v = ctx._re(v) + return v + else: + def h(): + ctx.prec += extraprec + w = z**3 / 9 + ctx.prec -= extraprec + C1 = _airybi_C1(ctx) + C2 = _airybi_C2(ctx) + T1 = [C1],[1],[],[],[],[ctx.mpq_2_3],w + T2 = [z*C2],[1],[],[],[],[ctx.mpq_4_3],w + return T1, T2 + return ctx.hypercomb(h, [], **kwargs) + +def _airy_zero(ctx, which, k, derivative, complex=False): + # Asymptotic formulas are given in DLMF section 9.9 + def U(t): return t**(2/3.)*(1-7/(t**2*48)) + def T(t): return t**(2/3.)*(1+5/(t**2*48)) + k = int(k) + if k < 1: + raise ValueError("k cannot be less than 1") + if not derivative in (0,1): + raise ValueError("Derivative should lie between 0 and 1") + if which == 0: + if derivative: + return ctx.findroot(lambda z: ctx.airyai(z,1), + -U(3*ctx.pi*(4*k-3)/8)) + return ctx.findroot(ctx.airyai, -T(3*ctx.pi*(4*k-1)/8)) + if which == 1 and complex == False: + if derivative: + return ctx.findroot(lambda z: ctx.airybi(z,1), + -U(3*ctx.pi*(4*k-1)/8)) + return ctx.findroot(ctx.airybi, -T(3*ctx.pi*(4*k-3)/8)) + if which == 1 and complex == True: + if derivative: + t = 3*ctx.pi*(4*k-3)/8 + 0.75j*ctx.ln2 + s = ctx.expjpi(ctx.mpf(1)/3) * T(t) + return ctx.findroot(lambda z: ctx.airybi(z,1), s) + t = 3*ctx.pi*(4*k-1)/8 + 0.75j*ctx.ln2 + s = ctx.expjpi(ctx.mpf(1)/3) * U(t) + return ctx.findroot(ctx.airybi, s) + +@defun +def airyaizero(ctx, k, derivative=0): + return _airy_zero(ctx, 0, k, derivative, False) + +@defun +def airybizero(ctx, k, derivative=0, complex=False): + return _airy_zero(ctx, 1, k, derivative, complex) + +def _scorer(ctx, z, which, kwargs): + z = ctx.convert(z) + if ctx.isinf(z): + if z == ctx.inf: + if which == 0: return 1/z + if which == 1: return z + if z == ctx.ninf: + return 1/z + raise ValueError("essential singularity") + if z: + extraprec = max(0, int(1.5*ctx.mag(z))) + else: + extraprec = 0 + if kwargs.get('derivative'): + raise NotImplementedError + # Direct asymptotic expansions, to avoid + # exponentially large cancellation + try: + if ctx.mag(z) > 3: + if which == 0 and abs(ctx.arg(z)) < ctx.pi/3 * 0.999: + def h(): + return (([ctx.pi,z],[-1,-1],[],[],[(1,3),(2,3),1],[],9/z**3),) + return ctx.hypercomb(h, [], maxterms=ctx.prec, force_series=True) + if which == 1 and abs(ctx.arg(-z)) < 2*ctx.pi/3 * 0.999: + def h(): + return (([-ctx.pi,z],[-1,-1],[],[],[(1,3),(2,3),1],[],9/z**3),) + return ctx.hypercomb(h, [], maxterms=ctx.prec, force_series=True) + except ctx.NoConvergence: + pass + def h(): + A = ctx.airybi(z, **kwargs)/3 + B = -2*ctx.pi + if which == 1: + A *= 2 + B *= -1 + ctx.prec += extraprec + w = z**3/9 + ctx.prec -= extraprec + T1 = [A], [1], [], [], [], [], 0 + T2 = [B,z], [-1,2], [], [], [1], [ctx.mpq_4_3,ctx.mpq_5_3], w + return T1, T2 + return ctx.hypercomb(h, [], **kwargs) + +@defun +def scorergi(ctx, z, **kwargs): + return _scorer(ctx, z, 0, kwargs) + +@defun +def scorerhi(ctx, z, **kwargs): + return _scorer(ctx, z, 1, kwargs) + +@defun_wrapped +def coulombc(ctx, l, eta, _cache={}): + if (l, eta) in _cache and _cache[l,eta][0] >= ctx.prec: + return +_cache[l,eta][1] + G3 = ctx.loggamma(2*l+2) + G1 = ctx.loggamma(1+l+ctx.j*eta) + G2 = ctx.loggamma(1+l-ctx.j*eta) + v = 2**l * ctx.exp((-ctx.pi*eta+G1+G2)/2 - G3) + if not (ctx.im(l) or ctx.im(eta)): + v = ctx.re(v) + _cache[l,eta] = (ctx.prec, v) + return v + +@defun_wrapped +def coulombf(ctx, l, eta, z, w=1, chop=True, **kwargs): + # Regular Coulomb wave function + # Note: w can be either 1 or -1; the other may be better in some cases + # TODO: check that chop=True chops when and only when it should + #ctx.prec += 10 + def h(l, eta): + try: + jw = ctx.j*w + jwz = ctx.fmul(jw, z, exact=True) + jwz2 = ctx.fmul(jwz, -2, exact=True) + C = ctx.coulombc(l, eta) + T1 = [C, z, ctx.exp(jwz)], [1, l+1, 1], [], [], [1+l+jw*eta], \ + [2*l+2], jwz2 + except ValueError: + T1 = [0], [-1], [], [], [], [], 0 + return (T1,) + v = ctx.hypercomb(h, [l,eta], **kwargs) + if chop and (not ctx.im(l)) and (not ctx.im(eta)) and (not ctx.im(z)) and \ + (ctx.re(z) >= 0): + v = ctx.re(v) + return v + +@defun_wrapped +def _coulomb_chi(ctx, l, eta, _cache={}): + if (l, eta) in _cache and _cache[l,eta][0] >= ctx.prec: + return _cache[l,eta][1] + def terms(): + l2 = -l-1 + jeta = ctx.j*eta + return [ctx.loggamma(1+l+jeta) * (-0.5j), + ctx.loggamma(1+l-jeta) * (0.5j), + ctx.loggamma(1+l2+jeta) * (0.5j), + ctx.loggamma(1+l2-jeta) * (-0.5j), + -(l+0.5)*ctx.pi] + v = ctx.sum_accurately(terms, 1) + _cache[l,eta] = (ctx.prec, v) + return v + +@defun_wrapped +def coulombg(ctx, l, eta, z, w=1, chop=True, **kwargs): + # Irregular Coulomb wave function + # Note: w can be either 1 or -1; the other may be better in some cases + # TODO: check that chop=True chops when and only when it should + if not ctx._im(l): + l = ctx._re(l) # XXX: for isint + def h(l, eta): + # Force perturbation for integers and half-integers + if ctx.isint(l*2): + T1 = [0], [-1], [], [], [], [], 0 + return (T1,) + l2 = -l-1 + try: + chi = ctx._coulomb_chi(l, eta) + jw = ctx.j*w + s = ctx.sin(chi); c = ctx.cos(chi) + C1 = ctx.coulombc(l,eta) + C2 = ctx.coulombc(l2,eta) + u = ctx.exp(jw*z) + x = -2*jw*z + T1 = [s, C1, z, u, c], [-1, 1, l+1, 1, 1], [], [], \ + [1+l+jw*eta], [2*l+2], x + T2 = [-s, C2, z, u], [-1, 1, l2+1, 1], [], [], \ + [1+l2+jw*eta], [2*l2+2], x + return T1, T2 + except ValueError: + T1 = [0], [-1], [], [], [], [], 0 + return (T1,) + v = ctx.hypercomb(h, [l,eta], **kwargs) + if chop and (not ctx._im(l)) and (not ctx._im(eta)) and (not ctx._im(z)) and \ + (ctx._re(z) >= 0): + v = ctx._re(v) + return v + +def mcmahon(ctx,kind,prime,v,m): + """ + Computes an estimate for the location of the Bessel function zero + j_{v,m}, y_{v,m}, j'_{v,m} or y'_{v,m} using McMahon's asymptotic + expansion (Abramowitz & Stegun 9.5.12-13, DLMF 20.21(vi)). + + Returns (r,err) where r is the estimated location of the root + and err is a positive number estimating the error of the + asymptotic expansion. + """ + u = 4*v**2 + if kind == 1 and not prime: b = (4*m+2*v-1)*ctx.pi/4 + if kind == 2 and not prime: b = (4*m+2*v-3)*ctx.pi/4 + if kind == 1 and prime: b = (4*m+2*v-3)*ctx.pi/4 + if kind == 2 and prime: b = (4*m+2*v-1)*ctx.pi/4 + if not prime: + s1 = b + s2 = -(u-1)/(8*b) + s3 = -4*(u-1)*(7*u-31)/(3*(8*b)**3) + s4 = -32*(u-1)*(83*u**2-982*u+3779)/(15*(8*b)**5) + s5 = -64*(u-1)*(6949*u**3-153855*u**2+1585743*u-6277237)/(105*(8*b)**7) + if prime: + s1 = b + s2 = -(u+3)/(8*b) + s3 = -4*(7*u**2+82*u-9)/(3*(8*b)**3) + s4 = -32*(83*u**3+2075*u**2-3039*u+3537)/(15*(8*b)**5) + s5 = -64*(6949*u**4+296492*u**3-1248002*u**2+7414380*u-5853627)/(105*(8*b)**7) + terms = [s1,s2,s3,s4,s5] + s = s1 + err = 0.0 + for i in range(1,len(terms)): + if abs(terms[i]) < abs(terms[i-1]): + s += terms[i] + else: + err = abs(terms[i]) + if i == len(terms)-1: + err = abs(terms[-1]) + return s, err + +def generalized_bisection(ctx,f,a,b,n): + """ + Given f known to have exactly n simple roots within [a,b], + return a list of n intervals isolating the roots + and having opposite signs at the endpoints. + + TODO: this can be optimized, e.g. by reusing evaluation points. + """ + if n < 1: + raise ValueError("n cannot be less than 1") + N = n+1 + points = [] + signs = [] + while 1: + points = ctx.linspace(a,b,N) + signs = [ctx.sign(f(x)) for x in points] + ok_intervals = [(points[i],points[i+1]) for i in range(N-1) \ + if signs[i]*signs[i+1] == -1] + if len(ok_intervals) == n: + return ok_intervals + N = N*2 + +def find_in_interval(ctx, f, ab): + return ctx.findroot(f, ab, solver='illinois', verify=False) + +def bessel_zero(ctx, kind, prime, v, m, isoltol=0.01, _interval_cache={}): + prec = ctx.prec + workprec = max(prec, ctx.mag(v), ctx.mag(m))+10 + try: + ctx.prec = workprec + v = ctx.mpf(v) + m = int(m) + prime = int(prime) + if v < 0: + raise ValueError("v cannot be negative") + if m < 1: + raise ValueError("m cannot be less than 1") + if not prime in (0,1): + raise ValueError("prime should lie between 0 and 1") + if kind == 1: + if prime: f = lambda x: ctx.besselj(v,x,derivative=1) + else: f = lambda x: ctx.besselj(v,x) + if kind == 2: + if prime: f = lambda x: ctx.bessely(v,x,derivative=1) + else: f = lambda x: ctx.bessely(v,x) + # The first root of J' is very close to 0 for small + # orders, and this needs to be special-cased + if kind == 1 and prime and m == 1: + if v == 0: + return ctx.zero + if v <= 1: + # TODO: use v <= j'_{v,1} < y_{v,1}? + r = 2*ctx.sqrt(v*(1+v)/(v+2)) + return find_in_interval(ctx, f, (r/10, 2*r)) + if (kind,prime,v,m) in _interval_cache: + return find_in_interval(ctx, f, _interval_cache[kind,prime,v,m]) + r, err = mcmahon(ctx, kind, prime, v, m) + if err < isoltol: + return find_in_interval(ctx, f, (r-isoltol, r+isoltol)) + # An x such that 0 < x < r_{v,1} + if kind == 1 and not prime: low = 2.4 + if kind == 1 and prime: low = 1.8 + if kind == 2 and not prime: low = 0.8 + if kind == 2 and prime: low = 2.0 + n = m+1 + while 1: + r1, err = mcmahon(ctx, kind, prime, v, n) + if err < isoltol: + r2, err2 = mcmahon(ctx, kind, prime, v, n+1) + intervals = generalized_bisection(ctx, f, low, 0.5*(r1+r2), n) + for k, ab in enumerate(intervals): + _interval_cache[kind,prime,v,k+1] = ab + return find_in_interval(ctx, f, intervals[m-1]) + else: + n = n*2 + finally: + ctx.prec = prec + +@defun +def besseljzero(ctx, v, m, derivative=0): + r""" + For a real order `\nu \ge 0` and a positive integer `m`, returns + `j_{\nu,m}`, the `m`-th positive zero of the Bessel function of the + first kind `J_{\nu}(z)` (see :func:`~mpmath.besselj`). Alternatively, + with *derivative=1*, gives the first nonnegative simple zero + `j'_{\nu,m}` of `J'_{\nu}(z)`. + + The indexing convention is that used by Abramowitz & Stegun + and the DLMF. Note the special case `j'_{0,1} = 0`, while all other + zeros are positive. In effect, only simple zeros are counted + (all zeros of Bessel functions are simple except possibly `z = 0`) + and `j_{\nu,m}` becomes a monotonic function of both `\nu` + and `m`. + + The zeros are interlaced according to the inequalities + + .. math :: + + j'_{\nu,k} < j_{\nu,k} < j'_{\nu,k+1} + + j_{\nu,1} < j_{\nu+1,2} < j_{\nu,2} < j_{\nu+1,2} < j_{\nu,3} < \cdots + + **Examples** + + Initial zeros of the Bessel functions `J_0(z), J_1(z), J_2(z)`:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> besseljzero(0,1); besseljzero(0,2); besseljzero(0,3) + 2.404825557695772768621632 + 5.520078110286310649596604 + 8.653727912911012216954199 + >>> besseljzero(1,1); besseljzero(1,2); besseljzero(1,3) + 3.831705970207512315614436 + 7.01558666981561875353705 + 10.17346813506272207718571 + >>> besseljzero(2,1); besseljzero(2,2); besseljzero(2,3) + 5.135622301840682556301402 + 8.417244140399864857783614 + 11.61984117214905942709415 + + Initial zeros of `J'_0(z), J'_1(z), J'_2(z)`:: + + 0.0 + 3.831705970207512315614436 + 7.01558666981561875353705 + >>> besseljzero(1,1,1); besseljzero(1,2,1); besseljzero(1,3,1) + 1.84118378134065930264363 + 5.331442773525032636884016 + 8.536316366346285834358961 + >>> besseljzero(2,1,1); besseljzero(2,2,1); besseljzero(2,3,1) + 3.054236928227140322755932 + 6.706133194158459146634394 + 9.969467823087595793179143 + + Zeros with large index:: + + >>> besseljzero(0,100); besseljzero(0,1000); besseljzero(0,10000) + 313.3742660775278447196902 + 3140.807295225078628895545 + 31415.14114171350798533666 + >>> besseljzero(5,100); besseljzero(5,1000); besseljzero(5,10000) + 321.1893195676003157339222 + 3148.657306813047523500494 + 31422.9947255486291798943 + >>> besseljzero(0,100,1); besseljzero(0,1000,1); besseljzero(0,10000,1) + 311.8018681873704508125112 + 3139.236339643802482833973 + 31413.57032947022399485808 + + Zeros of functions with large order:: + + >>> besseljzero(50,1) + 57.11689916011917411936228 + >>> besseljzero(50,2) + 62.80769876483536093435393 + >>> besseljzero(50,100) + 388.6936600656058834640981 + >>> besseljzero(50,1,1) + 52.99764038731665010944037 + >>> besseljzero(50,2,1) + 60.02631933279942589882363 + >>> besseljzero(50,100,1) + 387.1083151608726181086283 + + Zeros of functions with fractional order:: + + >>> besseljzero(0.5,1); besseljzero(1.5,1); besseljzero(2.25,4) + 3.141592653589793238462643 + 4.493409457909064175307881 + 15.15657692957458622921634 + + Both `J_{\nu}(z)` and `J'_{\nu}(z)` can be expressed as infinite + products over their zeros:: + + >>> v,z = 2, mpf(1) + >>> (z/2)**v/gamma(v+1) * \ + ... nprod(lambda k: 1-(z/besseljzero(v,k))**2, [1,inf]) + ... + 0.1149034849319004804696469 + >>> besselj(v,z) + 0.1149034849319004804696469 + >>> (z/2)**(v-1)/2/gamma(v) * \ + ... nprod(lambda k: 1-(z/besseljzero(v,k,1))**2, [1,inf]) + ... + 0.2102436158811325550203884 + >>> besselj(v,z,1) + 0.2102436158811325550203884 + + """ + return +bessel_zero(ctx, 1, derivative, v, m) + +@defun +def besselyzero(ctx, v, m, derivative=0): + r""" + For a real order `\nu \ge 0` and a positive integer `m`, returns + `y_{\nu,m}`, the `m`-th positive zero of the Bessel function of the + second kind `Y_{\nu}(z)` (see :func:`~mpmath.bessely`). Alternatively, + with *derivative=1*, gives the first positive zero `y'_{\nu,m}` of + `Y'_{\nu}(z)`. + + The zeros are interlaced according to the inequalities + + .. math :: + + y_{\nu,k} < y'_{\nu,k} < y_{\nu,k+1} + + y_{\nu,1} < y_{\nu+1,2} < y_{\nu,2} < y_{\nu+1,2} < y_{\nu,3} < \cdots + + **Examples** + + Initial zeros of the Bessel functions `Y_0(z), Y_1(z), Y_2(z)`:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> besselyzero(0,1); besselyzero(0,2); besselyzero(0,3) + 0.8935769662791675215848871 + 3.957678419314857868375677 + 7.086051060301772697623625 + >>> besselyzero(1,1); besselyzero(1,2); besselyzero(1,3) + 2.197141326031017035149034 + 5.429681040794135132772005 + 8.596005868331168926429606 + >>> besselyzero(2,1); besselyzero(2,2); besselyzero(2,3) + 3.384241767149593472701426 + 6.793807513268267538291167 + 10.02347797936003797850539 + + Initial zeros of `Y'_0(z), Y'_1(z), Y'_2(z)`:: + + >>> besselyzero(0,1,1); besselyzero(0,2,1); besselyzero(0,3,1) + 2.197141326031017035149034 + 5.429681040794135132772005 + 8.596005868331168926429606 + >>> besselyzero(1,1,1); besselyzero(1,2,1); besselyzero(1,3,1) + 3.683022856585177699898967 + 6.941499953654175655751944 + 10.12340465543661307978775 + >>> besselyzero(2,1,1); besselyzero(2,2,1); besselyzero(2,3,1) + 5.002582931446063945200176 + 8.350724701413079526349714 + 11.57419546521764654624265 + + Zeros with large index:: + + >>> besselyzero(0,100); besselyzero(0,1000); besselyzero(0,10000) + 311.8034717601871549333419 + 3139.236498918198006794026 + 31413.57034538691205229188 + >>> besselyzero(5,100); besselyzero(5,1000); besselyzero(5,10000) + 319.6183338562782156235062 + 3147.086508524556404473186 + 31421.42392920214673402828 + >>> besselyzero(0,100,1); besselyzero(0,1000,1); besselyzero(0,10000,1) + 313.3726705426359345050449 + 3140.807136030340213610065 + 31415.14112579761578220175 + + Zeros of functions with large order:: + + >>> besselyzero(50,1) + 53.50285882040036394680237 + >>> besselyzero(50,2) + 60.11244442774058114686022 + >>> besselyzero(50,100) + 387.1096509824943957706835 + >>> besselyzero(50,1,1) + 56.96290427516751320063605 + >>> besselyzero(50,2,1) + 62.74888166945933944036623 + >>> besselyzero(50,100,1) + 388.6923300548309258355475 + + Zeros of functions with fractional order:: + + >>> besselyzero(0.5,1); besselyzero(1.5,1); besselyzero(2.25,4) + 1.570796326794896619231322 + 2.798386045783887136720249 + 13.56721208770735123376018 + + """ + return +bessel_zero(ctx, 2, derivative, v, m) diff --git a/lib/python3.12/site-packages/mpmath/functions/elliptic.py b/lib/python3.12/site-packages/mpmath/functions/elliptic.py new file mode 100644 index 0000000000000000000000000000000000000000..1e198697fa042b7cc8bcba9e9e770f5c8106dad6 --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/functions/elliptic.py @@ -0,0 +1,1431 @@ +r""" +Elliptic functions historically comprise the elliptic integrals +and their inverses, and originate from the problem of computing the +arc length of an ellipse. From a more modern point of view, +an elliptic function is defined as a doubly periodic function, i.e. +a function which satisfies + +.. math :: + + f(z + 2 \omega_1) = f(z + 2 \omega_2) = f(z) + +for some half-periods `\omega_1, \omega_2` with +`\mathrm{Im}[\omega_1 / \omega_2] > 0`. The canonical elliptic +functions are the Jacobi elliptic functions. More broadly, this section +includes quasi-doubly periodic functions (such as the Jacobi theta +functions) and other functions useful in the study of elliptic functions. + +Many different conventions for the arguments of +elliptic functions are in use. It is even standard to use +different parameterizations for different functions in the same +text or software (and mpmath is no exception). +The usual parameters are the elliptic nome `q`, which usually +must satisfy `|q| < 1`; the elliptic parameter `m` (an arbitrary +complex number); the elliptic modulus `k` (an arbitrary complex +number); and the half-period ratio `\tau`, which usually must +satisfy `\mathrm{Im}[\tau] > 0`. +These quantities can be expressed in terms of each other +using the following relations: + +.. math :: + + m = k^2 + +.. math :: + + \tau = i \frac{K(1-m)}{K(m)} + +.. math :: + + q = e^{i \pi \tau} + +.. math :: + + k = \frac{\vartheta_2^2(q)}{\vartheta_3^2(q)} + +In addition, an alternative definition is used for the nome in +number theory, which we here denote by q-bar: + +.. math :: + + \bar{q} = q^2 = e^{2 i \pi \tau} + +For convenience, mpmath provides functions to convert +between the various parameters (:func:`~mpmath.qfrom`, :func:`~mpmath.mfrom`, +:func:`~mpmath.kfrom`, :func:`~mpmath.taufrom`, :func:`~mpmath.qbarfrom`). + +**References** + +1. [AbramowitzStegun]_ + +2. [WhittakerWatson]_ + +""" + +from .functions import defun, defun_wrapped + +@defun_wrapped +def eta(ctx, tau): + r""" + Returns the Dedekind eta function of tau in the upper half-plane. + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> eta(1j); gamma(0.25) / (2*pi**0.75) + (0.7682254223260566590025942 + 0.0j) + 0.7682254223260566590025942 + >>> tau = sqrt(2) + sqrt(5)*1j + >>> eta(-1/tau); sqrt(-1j*tau) * eta(tau) + (0.9022859908439376463573294 + 0.07985093673948098408048575j) + (0.9022859908439376463573295 + 0.07985093673948098408048575j) + >>> eta(tau+1); exp(pi*1j/12) * eta(tau) + (0.4493066139717553786223114 + 0.3290014793877986663915939j) + (0.4493066139717553786223114 + 0.3290014793877986663915939j) + >>> f = lambda z: diff(eta, z) / eta(z) + >>> chop(36*diff(f,tau)**2 - 24*diff(f,tau,2)*f(tau) + diff(f,tau,3)) + 0.0 + + """ + if ctx.im(tau) <= 0.0: + raise ValueError("eta is only defined in the upper half-plane") + q = ctx.expjpi(tau/12) + return q * ctx.qp(q**24) + +def nome(ctx, m): + m = ctx.convert(m) + if not m: + return m + if m == ctx.one: + return m + if ctx.isnan(m): + return m + if ctx.isinf(m): + if m == ctx.ninf: + return type(m)(-1) + else: + return ctx.mpc(-1) + a = ctx.ellipk(ctx.one-m) + b = ctx.ellipk(m) + v = ctx.exp(-ctx.pi*a/b) + if not ctx._im(m) and ctx._re(m) < 1: + if ctx._is_real_type(m): + return v.real + else: + return v.real + 0j + elif m == 2: + v = ctx.mpc(0, v.imag) + return v + +@defun_wrapped +def qfrom(ctx, q=None, m=None, k=None, tau=None, qbar=None): + r""" + Returns the elliptic nome `q`, given any of `q, m, k, \tau, \bar{q}`:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> qfrom(q=0.25) + 0.25 + >>> qfrom(m=mfrom(q=0.25)) + 0.25 + >>> qfrom(k=kfrom(q=0.25)) + 0.25 + >>> qfrom(tau=taufrom(q=0.25)) + (0.25 + 0.0j) + >>> qfrom(qbar=qbarfrom(q=0.25)) + 0.25 + + """ + if q is not None: + return ctx.convert(q) + if m is not None: + return nome(ctx, m) + if k is not None: + return nome(ctx, ctx.convert(k)**2) + if tau is not None: + return ctx.expjpi(tau) + if qbar is not None: + return ctx.sqrt(qbar) + +@defun_wrapped +def qbarfrom(ctx, q=None, m=None, k=None, tau=None, qbar=None): + r""" + Returns the number-theoretic nome `\bar q`, given any of + `q, m, k, \tau, \bar{q}`:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> qbarfrom(qbar=0.25) + 0.25 + >>> qbarfrom(q=qfrom(qbar=0.25)) + 0.25 + >>> qbarfrom(m=extraprec(20)(mfrom)(qbar=0.25)) # ill-conditioned + 0.25 + >>> qbarfrom(k=extraprec(20)(kfrom)(qbar=0.25)) # ill-conditioned + 0.25 + >>> qbarfrom(tau=taufrom(qbar=0.25)) + (0.25 + 0.0j) + + """ + if qbar is not None: + return ctx.convert(qbar) + if q is not None: + return ctx.convert(q) ** 2 + if m is not None: + return nome(ctx, m) ** 2 + if k is not None: + return nome(ctx, ctx.convert(k)**2) ** 2 + if tau is not None: + return ctx.expjpi(2*tau) + +@defun_wrapped +def taufrom(ctx, q=None, m=None, k=None, tau=None, qbar=None): + r""" + Returns the elliptic half-period ratio `\tau`, given any of + `q, m, k, \tau, \bar{q}`:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> taufrom(tau=0.5j) + (0.0 + 0.5j) + >>> taufrom(q=qfrom(tau=0.5j)) + (0.0 + 0.5j) + >>> taufrom(m=mfrom(tau=0.5j)) + (0.0 + 0.5j) + >>> taufrom(k=kfrom(tau=0.5j)) + (0.0 + 0.5j) + >>> taufrom(qbar=qbarfrom(tau=0.5j)) + (0.0 + 0.5j) + + """ + if tau is not None: + return ctx.convert(tau) + if m is not None: + m = ctx.convert(m) + return ctx.j*ctx.ellipk(1-m)/ctx.ellipk(m) + if k is not None: + k = ctx.convert(k) + return ctx.j*ctx.ellipk(1-k**2)/ctx.ellipk(k**2) + if q is not None: + return ctx.log(q) / (ctx.pi*ctx.j) + if qbar is not None: + qbar = ctx.convert(qbar) + return ctx.log(qbar) / (2*ctx.pi*ctx.j) + +@defun_wrapped +def kfrom(ctx, q=None, m=None, k=None, tau=None, qbar=None): + r""" + Returns the elliptic modulus `k`, given any of + `q, m, k, \tau, \bar{q}`:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> kfrom(k=0.25) + 0.25 + >>> kfrom(m=mfrom(k=0.25)) + 0.25 + >>> kfrom(q=qfrom(k=0.25)) + 0.25 + >>> kfrom(tau=taufrom(k=0.25)) + (0.25 + 0.0j) + >>> kfrom(qbar=qbarfrom(k=0.25)) + 0.25 + + As `q \to 1` and `q \to -1`, `k` rapidly approaches + `1` and `i \infty` respectively:: + + >>> kfrom(q=0.75) + 0.9999999999999899166471767 + >>> kfrom(q=-0.75) + (0.0 + 7041781.096692038332790615j) + >>> kfrom(q=1) + 1 + >>> kfrom(q=-1) + (0.0 + +infj) + """ + if k is not None: + return ctx.convert(k) + if m is not None: + return ctx.sqrt(m) + if tau is not None: + q = ctx.expjpi(tau) + if qbar is not None: + q = ctx.sqrt(qbar) + if q == 1: + return q + if q == -1: + return ctx.mpc(0,'inf') + return (ctx.jtheta(2,0,q)/ctx.jtheta(3,0,q))**2 + +@defun_wrapped +def mfrom(ctx, q=None, m=None, k=None, tau=None, qbar=None): + r""" + Returns the elliptic parameter `m`, given any of + `q, m, k, \tau, \bar{q}`:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> mfrom(m=0.25) + 0.25 + >>> mfrom(q=qfrom(m=0.25)) + 0.25 + >>> mfrom(k=kfrom(m=0.25)) + 0.25 + >>> mfrom(tau=taufrom(m=0.25)) + (0.25 + 0.0j) + >>> mfrom(qbar=qbarfrom(m=0.25)) + 0.25 + + As `q \to 1` and `q \to -1`, `m` rapidly approaches + `1` and `-\infty` respectively:: + + >>> mfrom(q=0.75) + 0.9999999999999798332943533 + >>> mfrom(q=-0.75) + -49586681013729.32611558353 + >>> mfrom(q=1) + 1.0 + >>> mfrom(q=-1) + -inf + + The inverse nome as a function of `q` has an integer + Taylor series expansion:: + + >>> taylor(lambda q: mfrom(q), 0, 7) + [0.0, 16.0, -128.0, 704.0, -3072.0, 11488.0, -38400.0, 117632.0] + + """ + if m is not None: + return m + if k is not None: + return k**2 + if tau is not None: + q = ctx.expjpi(tau) + if qbar is not None: + q = ctx.sqrt(qbar) + if q == 1: + return ctx.convert(q) + if q == -1: + return q*ctx.inf + v = (ctx.jtheta(2,0,q)/ctx.jtheta(3,0,q))**4 + if ctx._is_real_type(q) and q < 0: + v = v.real + return v + +jacobi_spec = { + 'sn' : ([3],[2],[1],[4], 'sin', 'tanh'), + 'cn' : ([4],[2],[2],[4], 'cos', 'sech'), + 'dn' : ([4],[3],[3],[4], '1', 'sech'), + 'ns' : ([2],[3],[4],[1], 'csc', 'coth'), + 'nc' : ([2],[4],[4],[2], 'sec', 'cosh'), + 'nd' : ([3],[4],[4],[3], '1', 'cosh'), + 'sc' : ([3],[4],[1],[2], 'tan', 'sinh'), + 'sd' : ([3,3],[2,4],[1],[3], 'sin', 'sinh'), + 'cd' : ([3],[2],[2],[3], 'cos', '1'), + 'cs' : ([4],[3],[2],[1], 'cot', 'csch'), + 'dc' : ([2],[3],[3],[2], 'sec', '1'), + 'ds' : ([2,4],[3,3],[3],[1], 'csc', 'csch'), + 'cc' : None, + 'ss' : None, + 'nn' : None, + 'dd' : None +} + +@defun +def ellipfun(ctx, kind, u=None, m=None, q=None, k=None, tau=None): + try: + S = jacobi_spec[kind] + except KeyError: + raise ValueError("First argument must be a two-character string " + "containing 's', 'c', 'd' or 'n', e.g.: 'sn'") + if u is None: + def f(*args, **kwargs): + return ctx.ellipfun(kind, *args, **kwargs) + f.__name__ = kind + return f + prec = ctx.prec + try: + ctx.prec += 10 + u = ctx.convert(u) + q = ctx.qfrom(m=m, q=q, k=k, tau=tau) + if S is None: + v = ctx.one + 0*q*u + elif q == ctx.zero: + if S[4] == '1': v = ctx.one + else: v = getattr(ctx, S[4])(u) + v += 0*q*u + elif q == ctx.one: + if S[5] == '1': v = ctx.one + else: v = getattr(ctx, S[5])(u) + v += 0*q*u + else: + t = u / ctx.jtheta(3, 0, q)**2 + v = ctx.one + for a in S[0]: v *= ctx.jtheta(a, 0, q) + for b in S[1]: v /= ctx.jtheta(b, 0, q) + for c in S[2]: v *= ctx.jtheta(c, t, q) + for d in S[3]: v /= ctx.jtheta(d, t, q) + finally: + ctx.prec = prec + return +v + +@defun_wrapped +def kleinj(ctx, tau=None, **kwargs): + r""" + Evaluates the Klein j-invariant, which is a modular function defined for + `\tau` in the upper half-plane as + + .. math :: + + J(\tau) = \frac{g_2^3(\tau)}{g_2^3(\tau) - 27 g_3^2(\tau)} + + where `g_2` and `g_3` are the modular invariants of the Weierstrass + elliptic function, + + .. math :: + + g_2(\tau) = 60 \sum_{(m,n) \in \mathbb{Z}^2 \setminus (0,0)} (m \tau+n)^{-4} + + g_3(\tau) = 140 \sum_{(m,n) \in \mathbb{Z}^2 \setminus (0,0)} (m \tau+n)^{-6}. + + An alternative, common notation is that of the j-function + `j(\tau) = 1728 J(\tau)`. + + **Plots** + + .. literalinclude :: /plots/kleinj.py + .. image :: /plots/kleinj.png + .. literalinclude :: /plots/kleinj2.py + .. image :: /plots/kleinj2.png + + **Examples** + + Verifying the functional equation `J(\tau) = J(\tau+1) = J(-\tau^{-1})`:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> tau = 0.625+0.75*j + >>> tau = 0.625+0.75*j + >>> kleinj(tau) + (-0.1507492166511182267125242 + 0.07595948379084571927228948j) + >>> kleinj(tau+1) + (-0.1507492166511182267125242 + 0.07595948379084571927228948j) + >>> kleinj(-1/tau) + (-0.1507492166511182267125242 + 0.07595948379084571927228946j) + + The j-function has a famous Laurent series expansion in terms of the nome + `\bar{q}`, `j(\tau) = \bar{q}^{-1} + 744 + 196884\bar{q} + \ldots`:: + + >>> mp.dps = 15 + >>> taylor(lambda q: 1728*q*kleinj(qbar=q), 0, 5, singular=True) + [1.0, 744.0, 196884.0, 21493760.0, 864299970.0, 20245856256.0] + + The j-function admits exact evaluation at special algebraic points + related to the Heegner numbers 1, 2, 3, 7, 11, 19, 43, 67, 163:: + + >>> @extraprec(10) + ... def h(n): + ... v = (1+sqrt(n)*j) + ... if n > 2: + ... v *= 0.5 + ... return v + ... + >>> mp.dps = 25 + >>> for n in [1,2,3,7,11,19,43,67,163]: + ... n, chop(1728*kleinj(h(n))) + ... + (1, 1728.0) + (2, 8000.0) + (3, 0.0) + (7, -3375.0) + (11, -32768.0) + (19, -884736.0) + (43, -884736000.0) + (67, -147197952000.0) + (163, -262537412640768000.0) + + Also at other special points, the j-function assumes explicit + algebraic values, e.g.:: + + >>> chop(1728*kleinj(j*sqrt(5))) + 1264538.909475140509320227 + >>> identify(cbrt(_)) # note: not simplified + '((100+sqrt(13520))/2)' + >>> (50+26*sqrt(5))**3 + 1264538.909475140509320227 + + """ + q = ctx.qfrom(tau=tau, **kwargs) + t2 = ctx.jtheta(2,0,q) + t3 = ctx.jtheta(3,0,q) + t4 = ctx.jtheta(4,0,q) + P = (t2**8 + t3**8 + t4**8)**3 + Q = 54*(t2*t3*t4)**8 + return P/Q + + +def RF_calc(ctx, x, y, z, r): + if y == z: return RC_calc(ctx, x, y, r) + if x == z: return RC_calc(ctx, y, x, r) + if x == y: return RC_calc(ctx, z, x, r) + if not (ctx.isnormal(x) and ctx.isnormal(y) and ctx.isnormal(z)): + if ctx.isnan(x) or ctx.isnan(y) or ctx.isnan(z): + return x*y*z + if ctx.isinf(x) or ctx.isinf(y) or ctx.isinf(z): + return ctx.zero + xm,ym,zm = x,y,z + A0 = Am = (x+y+z)/3 + Q = ctx.root(3*r, -6) * max(abs(A0-x),abs(A0-y),abs(A0-z)) + g = ctx.mpf(0.25) + pow4 = ctx.one + while 1: + xs = ctx.sqrt(xm) + ys = ctx.sqrt(ym) + zs = ctx.sqrt(zm) + lm = xs*ys + xs*zs + ys*zs + Am1 = (Am+lm)*g + xm, ym, zm = (xm+lm)*g, (ym+lm)*g, (zm+lm)*g + if pow4 * Q < abs(Am): + break + Am = Am1 + pow4 *= g + t = pow4/Am + X = (A0-x)*t + Y = (A0-y)*t + Z = -X-Y + E2 = X*Y-Z**2 + E3 = X*Y*Z + return ctx.power(Am,-0.5) * (9240-924*E2+385*E2**2+660*E3-630*E2*E3)/9240 + +def RC_calc(ctx, x, y, r, pv=True): + if not (ctx.isnormal(x) and ctx.isnormal(y)): + if ctx.isinf(x) or ctx.isinf(y): + return 1/(x*y) + if y == 0: + return ctx.inf + if x == 0: + return ctx.pi / ctx.sqrt(y) / 2 + raise ValueError + # Cauchy principal value + if pv and ctx._im(y) == 0 and ctx._re(y) < 0: + return ctx.sqrt(x/(x-y)) * RC_calc(ctx, x-y, -y, r) + if x == y: + return 1/ctx.sqrt(x) + extraprec = 2*max(0,-ctx.mag(x-y)+ctx.mag(x)) + ctx.prec += extraprec + if ctx._is_real_type(x) and ctx._is_real_type(y): + x = ctx._re(x) + y = ctx._re(y) + a = ctx.sqrt(x/y) + if x < y: + b = ctx.sqrt(y-x) + v = ctx.acos(a)/b + else: + b = ctx.sqrt(x-y) + v = ctx.acosh(a)/b + else: + sx = ctx.sqrt(x) + sy = ctx.sqrt(y) + v = ctx.acos(sx/sy)/(ctx.sqrt((1-x/y))*sy) + ctx.prec -= extraprec + return v + +def RJ_calc(ctx, x, y, z, p, r, integration): + """ + With integration == 0, computes RJ only using Carlson's algorithm + (may be wrong for some values). + With integration == 1, uses an initial integration to make sure + Carlson's algorithm is correct. + With integration == 2, uses only integration. + """ + if not (ctx.isnormal(x) and ctx.isnormal(y) and \ + ctx.isnormal(z) and ctx.isnormal(p)): + if ctx.isnan(x) or ctx.isnan(y) or ctx.isnan(z) or ctx.isnan(p): + return x*y*z + if ctx.isinf(x) or ctx.isinf(y) or ctx.isinf(z) or ctx.isinf(p): + return ctx.zero + if not p: + return ctx.inf + if (not x) + (not y) + (not z) > 1: + return ctx.inf + # Check conditions and fall back on integration for argument + # reduction if needed. The following conditions might be needlessly + # restrictive. + initial_integral = ctx.zero + if integration >= 1: + ok = (x.real >= 0 and y.real >= 0 and z.real >= 0 and p.real > 0) + if not ok: + if x == p or y == p or z == p: + ok = True + if not ok: + if p.imag != 0 or p.real >= 0: + if (x.imag == 0 and x.real >= 0 and ctx.conj(y) == z): + ok = True + if (y.imag == 0 and y.real >= 0 and ctx.conj(x) == z): + ok = True + if (z.imag == 0 and z.real >= 0 and ctx.conj(x) == y): + ok = True + if not ok or (integration == 2): + N = ctx.ceil(-min(x.real, y.real, z.real, p.real)) + 1 + # Integrate around any singularities + if all((t.imag >= 0 or t.real > 0) for t in [x, y, z, p]): + margin = ctx.j + elif all((t.imag < 0 or t.real > 0) for t in [x, y, z, p]): + margin = -ctx.j + else: + margin = 1 + # Go through the upper half-plane, but low enough that any + # parameter starting in the lower plane doesn't cross the + # branch cut + for t in [x, y, z, p]: + if t.imag >= 0 or t.real > 0: + continue + margin = min(margin, abs(t.imag) * 0.5) + margin *= ctx.j + N += margin + F = lambda t: 1/(ctx.sqrt(t+x)*ctx.sqrt(t+y)*ctx.sqrt(t+z)*(t+p)) + if integration == 2: + return 1.5 * ctx.quadsubdiv(F, [0, N, ctx.inf]) + initial_integral = 1.5 * ctx.quadsubdiv(F, [0, N]) + x += N; y += N; z += N; p += N + xm,ym,zm,pm = x,y,z,p + A0 = Am = (x + y + z + 2*p)/5 + delta = (p-x)*(p-y)*(p-z) + Q = ctx.root(0.25*r, -6) * max(abs(A0-x),abs(A0-y),abs(A0-z),abs(A0-p)) + g = ctx.mpf(0.25) + pow4 = ctx.one + S = 0 + while 1: + sx = ctx.sqrt(xm) + sy = ctx.sqrt(ym) + sz = ctx.sqrt(zm) + sp = ctx.sqrt(pm) + lm = sx*sy + sx*sz + sy*sz + Am1 = (Am+lm)*g + xm = (xm+lm)*g; ym = (ym+lm)*g; zm = (zm+lm)*g; pm = (pm+lm)*g + dm = (sp+sx) * (sp+sy) * (sp+sz) + em = delta * pow4**3 / dm**2 + if pow4 * Q < abs(Am): + break + T = RC_calc(ctx, ctx.one, ctx.one+em, r) * pow4 / dm + S += T + pow4 *= g + Am = Am1 + t = pow4 / Am + X = (A0-x)*t + Y = (A0-y)*t + Z = (A0-z)*t + P = (-X-Y-Z)/2 + E2 = X*Y + X*Z + Y*Z - 3*P**2 + E3 = X*Y*Z + 2*E2*P + 4*P**3 + E4 = (2*X*Y*Z + E2*P + 3*P**3)*P + E5 = X*Y*Z*P**2 + P = 24024 - 5148*E2 + 2457*E2**2 + 4004*E3 - 4158*E2*E3 - 3276*E4 + 2772*E5 + Q = 24024 + v1 = pow4 * ctx.power(Am, -1.5) * P/Q + v2 = 6*S + return initial_integral + v1 + v2 + +@defun +def elliprf(ctx, x, y, z): + r""" + Evaluates the Carlson symmetric elliptic integral of the first kind + + .. math :: + + R_F(x,y,z) = \frac{1}{2} + \int_0^{\infty} \frac{dt}{\sqrt{(t+x)(t+y)(t+z)}} + + which is defined for `x,y,z \notin (-\infty,0)`, and with + at most one of `x,y,z` being zero. + + For real `x,y,z \ge 0`, the principal square root is taken in the integrand. + For complex `x,y,z`, the principal square root is taken as `t \to \infty` + and as `t \to 0` non-principal branches are chosen as necessary so as to + make the integrand continuous. + + **Examples** + + Some basic values and limits:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> elliprf(0,1,1); pi/2 + 1.570796326794896619231322 + 1.570796326794896619231322 + >>> elliprf(0,1,inf) + 0.0 + >>> elliprf(1,1,1) + 1.0 + >>> elliprf(2,2,2)**2 + 0.5 + >>> elliprf(1,0,0); elliprf(0,0,1); elliprf(0,1,0); elliprf(0,0,0) + +inf + +inf + +inf + +inf + + Representing complete elliptic integrals in terms of `R_F`:: + + >>> m = mpf(0.75) + >>> ellipk(m); elliprf(0,1-m,1) + 2.156515647499643235438675 + 2.156515647499643235438675 + >>> ellipe(m); elliprf(0,1-m,1)-m*elliprd(0,1-m,1)/3 + 1.211056027568459524803563 + 1.211056027568459524803563 + + Some symmetries and argument transformations:: + + >>> x,y,z = 2,3,4 + >>> elliprf(x,y,z); elliprf(y,x,z); elliprf(z,y,x) + 0.5840828416771517066928492 + 0.5840828416771517066928492 + 0.5840828416771517066928492 + >>> k = mpf(100000) + >>> elliprf(k*x,k*y,k*z); k**(-0.5) * elliprf(x,y,z) + 0.001847032121923321253219284 + 0.001847032121923321253219284 + >>> l = sqrt(x*y) + sqrt(y*z) + sqrt(z*x) + >>> elliprf(x,y,z); 2*elliprf(x+l,y+l,z+l) + 0.5840828416771517066928492 + 0.5840828416771517066928492 + >>> elliprf((x+l)/4,(y+l)/4,(z+l)/4) + 0.5840828416771517066928492 + + Comparing with numerical integration:: + + >>> x,y,z = 2,3,4 + >>> elliprf(x,y,z) + 0.5840828416771517066928492 + >>> f = lambda t: 0.5*((t+x)*(t+y)*(t+z))**(-0.5) + >>> q = extradps(25)(quad) + >>> q(f, [0,inf]) + 0.5840828416771517066928492 + + With the following arguments, the square root in the integrand becomes + discontinuous at `t = 1/2` if the principal branch is used. To obtain + the right value, `-\sqrt{r}` must be taken instead of `\sqrt{r}` + on `t \in (0, 1/2)`:: + + >>> x,y,z = j-1,j,0 + >>> elliprf(x,y,z) + (0.7961258658423391329305694 - 1.213856669836495986430094j) + >>> -q(f, [0,0.5]) + q(f, [0.5,inf]) + (0.7961258658423391329305694 - 1.213856669836495986430094j) + + The so-called *first lemniscate constant*, a transcendental number:: + + >>> elliprf(0,1,2) + 1.31102877714605990523242 + >>> extradps(25)(quad)(lambda t: 1/sqrt(1-t**4), [0,1]) + 1.31102877714605990523242 + >>> gamma('1/4')**2/(4*sqrt(2*pi)) + 1.31102877714605990523242 + + **References** + + 1. [Carlson]_ + 2. [DLMF]_ Chapter 19. Elliptic Integrals + + """ + x = ctx.convert(x) + y = ctx.convert(y) + z = ctx.convert(z) + prec = ctx.prec + try: + ctx.prec += 20 + tol = ctx.eps * 2**10 + v = RF_calc(ctx, x, y, z, tol) + finally: + ctx.prec = prec + return +v + +@defun +def elliprc(ctx, x, y, pv=True): + r""" + Evaluates the degenerate Carlson symmetric elliptic integral + of the first kind + + .. math :: + + R_C(x,y) = R_F(x,y,y) = + \frac{1}{2} \int_0^{\infty} \frac{dt}{(t+y) \sqrt{(t+x)}}. + + If `y \in (-\infty,0)`, either a value defined by continuity, + or with *pv=True* the Cauchy principal value, can be computed. + + If `x \ge 0, y > 0`, the value can be expressed in terms of + elementary functions as + + .. math :: + + R_C(x,y) = + \begin{cases} + \dfrac{1}{\sqrt{y-x}} + \cos^{-1}\left(\sqrt{\dfrac{x}{y}}\right), & x < y \\ + \dfrac{1}{\sqrt{y}}, & x = y \\ + \dfrac{1}{\sqrt{x-y}} + \cosh^{-1}\left(\sqrt{\dfrac{x}{y}}\right), & x > y \\ + \end{cases}. + + **Examples** + + Some special values and limits:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> elliprc(1,2)*4; elliprc(0,1)*2; +pi + 3.141592653589793238462643 + 3.141592653589793238462643 + 3.141592653589793238462643 + >>> elliprc(1,0) + +inf + >>> elliprc(5,5)**2 + 0.2 + >>> elliprc(1,inf); elliprc(inf,1); elliprc(inf,inf) + 0.0 + 0.0 + 0.0 + + Comparing with the elementary closed-form solution:: + + >>> elliprc('1/3', '1/5'); sqrt(7.5)*acosh(sqrt('5/3')) + 2.041630778983498390751238 + 2.041630778983498390751238 + >>> elliprc('1/5', '1/3'); sqrt(7.5)*acos(sqrt('3/5')) + 1.875180765206547065111085 + 1.875180765206547065111085 + + Comparing with numerical integration:: + + >>> q = extradps(25)(quad) + >>> elliprc(2, -3, pv=True) + 0.3333969101113672670749334 + >>> elliprc(2, -3, pv=False) + (0.3333969101113672670749334 + 0.7024814731040726393156375j) + >>> 0.5*q(lambda t: 1/(sqrt(t+2)*(t-3)), [0,3-j,6,inf]) + (0.3333969101113672670749334 + 0.7024814731040726393156375j) + + """ + x = ctx.convert(x) + y = ctx.convert(y) + prec = ctx.prec + try: + ctx.prec += 20 + tol = ctx.eps * 2**10 + v = RC_calc(ctx, x, y, tol, pv) + finally: + ctx.prec = prec + return +v + +@defun +def elliprj(ctx, x, y, z, p, integration=1): + r""" + Evaluates the Carlson symmetric elliptic integral of the third kind + + .. math :: + + R_J(x,y,z,p) = \frac{3}{2} + \int_0^{\infty} \frac{dt}{(t+p)\sqrt{(t+x)(t+y)(t+z)}}. + + Like :func:`~mpmath.elliprf`, the branch of the square root in the integrand + is defined so as to be continuous along the path of integration for + complex values of the arguments. + + **Examples** + + Some values and limits:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> elliprj(1,1,1,1) + 1.0 + >>> elliprj(2,2,2,2); 1/(2*sqrt(2)) + 0.3535533905932737622004222 + 0.3535533905932737622004222 + >>> elliprj(0,1,2,2) + 1.067937989667395702268688 + >>> 3*(2*gamma('5/4')**2-pi**2/gamma('1/4')**2)/(sqrt(2*pi)) + 1.067937989667395702268688 + >>> elliprj(0,1,1,2); 3*pi*(2-sqrt(2))/4 + 1.380226776765915172432054 + 1.380226776765915172432054 + >>> elliprj(1,3,2,0); elliprj(0,1,1,0); elliprj(0,0,0,0) + +inf + +inf + +inf + >>> elliprj(1,inf,1,0); elliprj(1,1,1,inf) + 0.0 + 0.0 + >>> chop(elliprj(1+j, 1-j, 1, 1)) + 0.8505007163686739432927844 + + Scale transformation:: + + >>> x,y,z,p = 2,3,4,5 + >>> k = mpf(100000) + >>> elliprj(k*x,k*y,k*z,k*p); k**(-1.5)*elliprj(x,y,z,p) + 4.521291677592745527851168e-9 + 4.521291677592745527851168e-9 + + Comparing with numerical integration:: + + >>> elliprj(1,2,3,4) + 0.2398480997495677621758617 + >>> f = lambda t: 1/((t+4)*sqrt((t+1)*(t+2)*(t+3))) + >>> 1.5*quad(f, [0,inf]) + 0.2398480997495677621758617 + >>> elliprj(1,2+1j,3,4-2j) + (0.216888906014633498739952 + 0.04081912627366673332369512j) + >>> f = lambda t: 1/((t+4-2j)*sqrt((t+1)*(t+2+1j)*(t+3))) + >>> 1.5*quad(f, [0,inf]) + (0.216888906014633498739952 + 0.04081912627366673332369511j) + + """ + x = ctx.convert(x) + y = ctx.convert(y) + z = ctx.convert(z) + p = ctx.convert(p) + prec = ctx.prec + try: + ctx.prec += 20 + tol = ctx.eps * 2**10 + v = RJ_calc(ctx, x, y, z, p, tol, integration) + finally: + ctx.prec = prec + return +v + +@defun +def elliprd(ctx, x, y, z): + r""" + Evaluates the degenerate Carlson symmetric elliptic integral + of the third kind or Carlson elliptic integral of the + second kind `R_D(x,y,z) = R_J(x,y,z,z)`. + + See :func:`~mpmath.elliprj` for additional information. + + **Examples** + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> elliprd(1,2,3) + 0.2904602810289906442326534 + >>> elliprj(1,2,3,3) + 0.2904602810289906442326534 + + The so-called *second lemniscate constant*, a transcendental number:: + + >>> elliprd(0,2,1)/3 + 0.5990701173677961037199612 + >>> extradps(25)(quad)(lambda t: t**2/sqrt(1-t**4), [0,1]) + 0.5990701173677961037199612 + >>> gamma('3/4')**2/sqrt(2*pi) + 0.5990701173677961037199612 + + """ + return ctx.elliprj(x,y,z,z) + +@defun +def elliprg(ctx, x, y, z): + r""" + Evaluates the Carlson completely symmetric elliptic integral + of the second kind + + .. math :: + + R_G(x,y,z) = \frac{1}{4} \int_0^{\infty} + \frac{t}{\sqrt{(t+x)(t+y)(t+z)}} + \left( \frac{x}{t+x} + \frac{y}{t+y} + \frac{z}{t+z}\right) dt. + + **Examples** + + Evaluation for real and complex arguments:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> elliprg(0,1,1)*4; +pi + 3.141592653589793238462643 + 3.141592653589793238462643 + >>> elliprg(0,0.5,1) + 0.6753219405238377512600874 + >>> chop(elliprg(1+j, 1-j, 2)) + 1.172431327676416604532822 + + A double integral that can be evaluated in terms of `R_G`:: + + >>> x,y,z = 2,3,4 + >>> def f(t,u): + ... st = fp.sin(t); ct = fp.cos(t) + ... su = fp.sin(u); cu = fp.cos(u) + ... return (x*(st*cu)**2 + y*(st*su)**2 + z*ct**2)**0.5 * st + ... + >>> nprint(mpf(fp.quad(f, [0,fp.pi], [0,2*fp.pi])/(4*fp.pi)), 13) + 1.725503028069 + >>> nprint(elliprg(x,y,z), 13) + 1.725503028069 + + """ + x = ctx.convert(x) + y = ctx.convert(y) + z = ctx.convert(z) + zeros = (not x) + (not y) + (not z) + if zeros == 3: + return (x+y+z)*0 + if zeros == 2: + if x: return 0.5*ctx.sqrt(x) + if y: return 0.5*ctx.sqrt(y) + return 0.5*ctx.sqrt(z) + if zeros == 1: + if not z: + x, z = z, x + def terms(): + T1 = 0.5*z*ctx.elliprf(x,y,z) + T2 = -0.5*(x-z)*(y-z)*ctx.elliprd(x,y,z)/3 + T3 = 0.5*ctx.sqrt(x)*ctx.sqrt(y)/ctx.sqrt(z) + return T1,T2,T3 + return ctx.sum_accurately(terms) + + +@defun_wrapped +def ellipf(ctx, phi, m): + r""" + Evaluates the Legendre incomplete elliptic integral of the first kind + + .. math :: + + F(\phi,m) = \int_0^{\phi} \frac{dt}{\sqrt{1-m \sin^2 t}} + + or equivalently + + .. math :: + + F(\phi,m) = \int_0^{\sin \phi} + \frac{dt}{\left(\sqrt{1-t^2}\right)\left(\sqrt{1-mt^2}\right)}. + + The function reduces to a complete elliptic integral of the first kind + (see :func:`~mpmath.ellipk`) when `\phi = \frac{\pi}{2}`; that is, + + .. math :: + + F\left(\frac{\pi}{2}, m\right) = K(m). + + In the defining integral, it is assumed that the principal branch + of the square root is taken and that the path of integration avoids + crossing any branch cuts. Outside `-\pi/2 \le \Re(\phi) \le \pi/2`, + the function extends quasi-periodically as + + .. math :: + + F(\phi + n \pi, m) = 2 n K(m) + F(\phi,m), n \in \mathbb{Z}. + + **Plots** + + .. literalinclude :: /plots/ellipf.py + .. image :: /plots/ellipf.png + + **Examples** + + Basic values and limits:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> ellipf(0,1) + 0.0 + >>> ellipf(0,0) + 0.0 + >>> ellipf(1,0); ellipf(2+3j,0) + 1.0 + (2.0 + 3.0j) + >>> ellipf(1,1); log(sec(1)+tan(1)) + 1.226191170883517070813061 + 1.226191170883517070813061 + >>> ellipf(pi/2, -0.5); ellipk(-0.5) + 1.415737208425956198892166 + 1.415737208425956198892166 + >>> ellipf(pi/2+eps, 1); ellipf(-pi/2-eps, 1) + +inf + +inf + >>> ellipf(1.5, 1) + 3.340677542798311003320813 + + Comparing with numerical integration:: + + >>> z,m = 0.5, 1.25 + >>> ellipf(z,m) + 0.5287219202206327872978255 + >>> quad(lambda t: (1-m*sin(t)**2)**(-0.5), [0,z]) + 0.5287219202206327872978255 + + The arguments may be complex numbers:: + + >>> ellipf(3j, 0.5) + (0.0 + 1.713602407841590234804143j) + >>> ellipf(3+4j, 5-6j) + (1.269131241950351323305741 - 0.3561052815014558335412538j) + >>> z,m = 2+3j, 1.25 + >>> k = 1011 + >>> ellipf(z+pi*k,m); ellipf(z,m) + 2*k*ellipk(m) + (4086.184383622179764082821 - 3003.003538923749396546871j) + (4086.184383622179764082821 - 3003.003538923749396546871j) + + For `|\Re(z)| < \pi/2`, the function can be expressed as a + hypergeometric series of two variables + (see :func:`~mpmath.appellf1`):: + + >>> z,m = 0.5, 0.25 + >>> ellipf(z,m) + 0.5050887275786480788831083 + >>> sin(z)*appellf1(0.5,0.5,0.5,1.5,sin(z)**2,m*sin(z)**2) + 0.5050887275786480788831083 + + """ + z = phi + if not (ctx.isnormal(z) and ctx.isnormal(m)): + if m == 0: + return z + m + if z == 0: + return z * m + if m == ctx.inf or m == ctx.ninf: return z/m + raise ValueError + x = z.real + ctx.prec += max(0, ctx.mag(x)) + pi = +ctx.pi + away = abs(x) > pi/2 + if m == 1: + if away: + return ctx.inf + if away: + d = ctx.nint(x/pi) + z = z-pi*d + P = 2*d*ctx.ellipk(m) + else: + P = 0 + c, s = ctx.cos_sin(z) + return s * ctx.elliprf(c**2, 1-m*s**2, 1) + P + +@defun_wrapped +def ellipe(ctx, *args): + r""" + Called with a single argument `m`, evaluates the Legendre complete + elliptic integral of the second kind, `E(m)`, defined by + + .. math :: E(m) = \int_0^{\pi/2} \sqrt{1-m \sin^2 t} \, dt \,=\, + \frac{\pi}{2} + \,_2F_1\left(\frac{1}{2}, -\frac{1}{2}, 1, m\right). + + Called with two arguments `\phi, m`, evaluates the incomplete elliptic + integral of the second kind + + .. math :: + + E(\phi,m) = \int_0^{\phi} \sqrt{1-m \sin^2 t} \, dt = + \int_0^{\sin z} + \frac{\sqrt{1-mt^2}}{\sqrt{1-t^2}} \, dt. + + The incomplete integral reduces to a complete integral when + `\phi = \frac{\pi}{2}`; that is, + + .. math :: + + E\left(\frac{\pi}{2}, m\right) = E(m). + + In the defining integral, it is assumed that the principal branch + of the square root is taken and that the path of integration avoids + crossing any branch cuts. Outside `-\pi/2 \le \Re(z) \le \pi/2`, + the function extends quasi-periodically as + + .. math :: + + E(\phi + n \pi, m) = 2 n E(m) + E(\phi,m), n \in \mathbb{Z}. + + **Plots** + + .. literalinclude :: /plots/ellipe.py + .. image :: /plots/ellipe.png + + **Examples for the complete integral** + + Basic values and limits:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> ellipe(0) + 1.570796326794896619231322 + >>> ellipe(1) + 1.0 + >>> ellipe(-1) + 1.910098894513856008952381 + >>> ellipe(2) + (0.5990701173677961037199612 + 0.5990701173677961037199612j) + >>> ellipe(inf) + (0.0 + +infj) + >>> ellipe(-inf) + +inf + + Verifying the defining integral and hypergeometric + representation:: + + >>> ellipe(0.5) + 1.350643881047675502520175 + >>> quad(lambda t: sqrt(1-0.5*sin(t)**2), [0, pi/2]) + 1.350643881047675502520175 + >>> pi/2*hyp2f1(0.5,-0.5,1,0.5) + 1.350643881047675502520175 + + Evaluation is supported for arbitrary complex `m`:: + + >>> ellipe(0.5+0.25j) + (1.360868682163129682716687 - 0.1238733442561786843557315j) + >>> ellipe(3+4j) + (1.499553520933346954333612 - 1.577879007912758274533309j) + + A definite integral:: + + >>> quad(ellipe, [0,1]) + 1.333333333333333333333333 + + **Examples for the incomplete integral** + + Basic values and limits:: + + >>> ellipe(0,1) + 0.0 + >>> ellipe(0,0) + 0.0 + >>> ellipe(1,0) + 1.0 + >>> ellipe(2+3j,0) + (2.0 + 3.0j) + >>> ellipe(1,1); sin(1) + 0.8414709848078965066525023 + 0.8414709848078965066525023 + >>> ellipe(pi/2, -0.5); ellipe(-0.5) + 1.751771275694817862026502 + 1.751771275694817862026502 + >>> ellipe(pi/2, 1); ellipe(-pi/2, 1) + 1.0 + -1.0 + >>> ellipe(1.5, 1) + 0.9974949866040544309417234 + + Comparing with numerical integration:: + + >>> z,m = 0.5, 1.25 + >>> ellipe(z,m) + 0.4740152182652628394264449 + >>> quad(lambda t: sqrt(1-m*sin(t)**2), [0,z]) + 0.4740152182652628394264449 + + The arguments may be complex numbers:: + + >>> ellipe(3j, 0.5) + (0.0 + 7.551991234890371873502105j) + >>> ellipe(3+4j, 5-6j) + (24.15299022574220502424466 + 75.2503670480325997418156j) + >>> k = 35 + >>> z,m = 2+3j, 1.25 + >>> ellipe(z+pi*k,m); ellipe(z,m) + 2*k*ellipe(m) + (48.30138799412005235090766 + 17.47255216721987688224357j) + (48.30138799412005235090766 + 17.47255216721987688224357j) + + For `|\Re(z)| < \pi/2`, the function can be expressed as a + hypergeometric series of two variables + (see :func:`~mpmath.appellf1`):: + + >>> z,m = 0.5, 0.25 + >>> ellipe(z,m) + 0.4950017030164151928870375 + >>> sin(z)*appellf1(0.5,0.5,-0.5,1.5,sin(z)**2,m*sin(z)**2) + 0.4950017030164151928870376 + + """ + if len(args) == 1: + return ctx._ellipe(args[0]) + else: + phi, m = args + z = phi + if not (ctx.isnormal(z) and ctx.isnormal(m)): + if m == 0: + return z + m + if z == 0: + return z * m + if m == ctx.inf or m == ctx.ninf: + return ctx.inf + raise ValueError + x = z.real + ctx.prec += max(0, ctx.mag(x)) + pi = +ctx.pi + away = abs(x) > pi/2 + if away: + d = ctx.nint(x/pi) + z = z-pi*d + P = 2*d*ctx.ellipe(m) + else: + P = 0 + def terms(): + c, s = ctx.cos_sin(z) + x = c**2 + y = 1-m*s**2 + RF = ctx.elliprf(x, y, 1) + RD = ctx.elliprd(x, y, 1) + return s*RF, -m*s**3*RD/3 + return ctx.sum_accurately(terms) + P + +@defun_wrapped +def ellippi(ctx, *args): + r""" + Called with three arguments `n, \phi, m`, evaluates the Legendre + incomplete elliptic integral of the third kind + + .. math :: + + \Pi(n; \phi, m) = \int_0^{\phi} + \frac{dt}{(1-n \sin^2 t) \sqrt{1-m \sin^2 t}} = + \int_0^{\sin \phi} + \frac{dt}{(1-nt^2) \sqrt{1-t^2} \sqrt{1-mt^2}}. + + Called with two arguments `n, m`, evaluates the complete + elliptic integral of the third kind + `\Pi(n,m) = \Pi(n; \frac{\pi}{2},m)`. + + In the defining integral, it is assumed that the principal branch + of the square root is taken and that the path of integration avoids + crossing any branch cuts. Outside `-\pi/2 \le \Re(\phi) \le \pi/2`, + the function extends quasi-periodically as + + .. math :: + + \Pi(n,\phi+k\pi,m) = 2k\Pi(n,m) + \Pi(n,\phi,m), k \in \mathbb{Z}. + + **Plots** + + .. literalinclude :: /plots/ellippi.py + .. image :: /plots/ellippi.png + + **Examples for the complete integral** + + Some basic values and limits:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> ellippi(0,-5); ellipk(-5) + 0.9555039270640439337379334 + 0.9555039270640439337379334 + >>> ellippi(inf,2) + 0.0 + >>> ellippi(2,inf) + 0.0 + >>> abs(ellippi(1,5)) + +inf + >>> abs(ellippi(0.25,1)) + +inf + + Evaluation in terms of simpler functions:: + + >>> ellippi(0.25,0.25); ellipe(0.25)/(1-0.25) + 1.956616279119236207279727 + 1.956616279119236207279727 + >>> ellippi(3,0); pi/(2*sqrt(-2)) + (0.0 - 1.11072073453959156175397j) + (0.0 - 1.11072073453959156175397j) + >>> ellippi(-3,0); pi/(2*sqrt(4)) + 0.7853981633974483096156609 + 0.7853981633974483096156609 + + **Examples for the incomplete integral** + + Basic values and limits:: + + >>> ellippi(0.25,-0.5); ellippi(0.25,pi/2,-0.5) + 1.622944760954741603710555 + 1.622944760954741603710555 + >>> ellippi(1,0,1) + 0.0 + >>> ellippi(inf,0,1) + 0.0 + >>> ellippi(0,0.25,0.5); ellipf(0.25,0.5) + 0.2513040086544925794134591 + 0.2513040086544925794134591 + >>> ellippi(1,1,1); (log(sec(1)+tan(1))+sec(1)*tan(1))/2 + 2.054332933256248668692452 + 2.054332933256248668692452 + >>> ellippi(0.25, 53*pi/2, 0.75); 53*ellippi(0.25,0.75) + 135.240868757890840755058 + 135.240868757890840755058 + >>> ellippi(0.5,pi/4,0.5); 2*ellipe(pi/4,0.5)-1/sqrt(3) + 0.9190227391656969903987269 + 0.9190227391656969903987269 + + Complex arguments are supported:: + + >>> ellippi(0.5, 5+6j-2*pi, -7-8j) + (-0.3612856620076747660410167 + 0.5217735339984807829755815j) + + Some degenerate cases:: + + >>> ellippi(1,1) + +inf + >>> ellippi(1,0) + +inf + >>> ellippi(1,2,0) + +inf + >>> ellippi(1,2,1) + +inf + >>> ellippi(1,0,1) + 0.0 + + """ + if len(args) == 2: + n, m = args + complete = True + z = phi = ctx.pi/2 + else: + n, phi, m = args + complete = False + z = phi + if not (ctx.isnormal(n) and ctx.isnormal(z) and ctx.isnormal(m)): + if ctx.isnan(n) or ctx.isnan(z) or ctx.isnan(m): + raise ValueError + if complete: + if m == 0: + if n == 1: + return ctx.inf + return ctx.pi/(2*ctx.sqrt(1-n)) + if n == 0: return ctx.ellipk(m) + if ctx.isinf(n) or ctx.isinf(m): return ctx.zero + else: + if z == 0: return z + if ctx.isinf(n): return ctx.zero + if ctx.isinf(m): return ctx.zero + if ctx.isinf(n) or ctx.isinf(z) or ctx.isinf(m): + raise ValueError + if complete: + if m == 1: + if n == 1: + return ctx.inf + return -ctx.inf/ctx.sign(n-1) + away = False + else: + x = z.real + ctx.prec += max(0, ctx.mag(x)) + pi = +ctx.pi + away = abs(x) > pi/2 + if away: + d = ctx.nint(x/pi) + z = z-pi*d + P = 2*d*ctx.ellippi(n,m) + if ctx.isinf(P): + return ctx.inf + else: + P = 0 + def terms(): + if complete: + c, s = ctx.zero, ctx.one + else: + c, s = ctx.cos_sin(z) + x = c**2 + y = 1-m*s**2 + RF = ctx.elliprf(x, y, 1) + RJ = ctx.elliprj(x, y, 1, 1-n*s**2) + return s*RF, n*s**3*RJ/3 + return ctx.sum_accurately(terms) + P diff --git a/lib/python3.12/site-packages/mpmath/functions/expintegrals.py b/lib/python3.12/site-packages/mpmath/functions/expintegrals.py new file mode 100644 index 0000000000000000000000000000000000000000..0dee8356c0386819d8f0421fded476ee77229359 --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/functions/expintegrals.py @@ -0,0 +1,425 @@ +from .functions import defun, defun_wrapped + +@defun_wrapped +def _erf_complex(ctx, z): + z2 = ctx.square_exp_arg(z, -1) + #z2 = -z**2 + v = (2/ctx.sqrt(ctx.pi))*z * ctx.hyp1f1((1,2),(3,2), z2) + if not ctx._re(z): + v = ctx._im(v)*ctx.j + return v + +@defun_wrapped +def _erfc_complex(ctx, z): + if ctx.re(z) > 2: + z2 = ctx.square_exp_arg(z) + nz2 = ctx.fneg(z2, exact=True) + v = ctx.exp(nz2)/ctx.sqrt(ctx.pi) * ctx.hyperu((1,2),(1,2), z2) + else: + v = 1 - ctx._erf_complex(z) + if not ctx._re(z): + v = 1+ctx._im(v)*ctx.j + return v + +@defun +def erf(ctx, z): + z = ctx.convert(z) + if ctx._is_real_type(z): + try: + return ctx._erf(z) + except NotImplementedError: + pass + if ctx._is_complex_type(z) and not z.imag: + try: + return type(z)(ctx._erf(z.real)) + except NotImplementedError: + pass + return ctx._erf_complex(z) + +@defun +def erfc(ctx, z): + z = ctx.convert(z) + if ctx._is_real_type(z): + try: + return ctx._erfc(z) + except NotImplementedError: + pass + if ctx._is_complex_type(z) and not z.imag: + try: + return type(z)(ctx._erfc(z.real)) + except NotImplementedError: + pass + return ctx._erfc_complex(z) + +@defun +def square_exp_arg(ctx, z, mult=1, reciprocal=False): + prec = ctx.prec*4+20 + if reciprocal: + z2 = ctx.fmul(z, z, prec=prec) + z2 = ctx.fdiv(ctx.one, z2, prec=prec) + else: + z2 = ctx.fmul(z, z, prec=prec) + if mult != 1: + z2 = ctx.fmul(z2, mult, exact=True) + return z2 + +@defun_wrapped +def erfi(ctx, z): + if not z: + return z + z2 = ctx.square_exp_arg(z) + v = (2/ctx.sqrt(ctx.pi)*z) * ctx.hyp1f1((1,2), (3,2), z2) + if not ctx._re(z): + v = ctx._im(v)*ctx.j + return v + +@defun_wrapped +def erfinv(ctx, x): + xre = ctx._re(x) + if (xre != x) or (xre < -1) or (xre > 1): + return ctx.bad_domain("erfinv(x) is defined only for -1 <= x <= 1") + x = xre + #if ctx.isnan(x): return x + if not x: return x + if x == 1: return ctx.inf + if x == -1: return ctx.ninf + if abs(x) < 0.9: + a = 0.53728*x**3 + 0.813198*x + else: + # An asymptotic formula + u = ctx.ln(2/ctx.pi/(abs(x)-1)**2) + a = ctx.sign(x) * ctx.sqrt(u - ctx.ln(u))/ctx.sqrt(2) + ctx.prec += 10 + return ctx.findroot(lambda t: ctx.erf(t)-x, a) + +@defun_wrapped +def npdf(ctx, x, mu=0, sigma=1): + sigma = ctx.convert(sigma) + return ctx.exp(-(x-mu)**2/(2*sigma**2)) / (sigma*ctx.sqrt(2*ctx.pi)) + +@defun_wrapped +def ncdf(ctx, x, mu=0, sigma=1): + a = (x-mu)/(sigma*ctx.sqrt(2)) + if a < 0: + return ctx.erfc(-a)/2 + else: + return (1+ctx.erf(a))/2 + +@defun_wrapped +def betainc(ctx, a, b, x1=0, x2=1, regularized=False): + if x1 == x2: + v = 0 + elif not x1: + if x1 == 0 and x2 == 1: + v = ctx.beta(a, b) + else: + v = x2**a * ctx.hyp2f1(a, 1-b, a+1, x2) / a + else: + m, d = ctx.nint_distance(a) + if m <= 0: + if d < -ctx.prec: + h = +ctx.eps + ctx.prec *= 2 + a += h + elif d < -4: + ctx.prec -= d + s1 = x2**a * ctx.hyp2f1(a,1-b,a+1,x2) + s2 = x1**a * ctx.hyp2f1(a,1-b,a+1,x1) + v = (s1 - s2) / a + if regularized: + v /= ctx.beta(a,b) + return v + +@defun +def gammainc(ctx, z, a=0, b=None, regularized=False): + regularized = bool(regularized) + z = ctx.convert(z) + if a is None: + a = ctx.zero + lower_modified = False + else: + a = ctx.convert(a) + lower_modified = a != ctx.zero + if b is None: + b = ctx.inf + upper_modified = False + else: + b = ctx.convert(b) + upper_modified = b != ctx.inf + # Complete gamma function + if not (upper_modified or lower_modified): + if regularized: + if ctx.re(z) < 0: + return ctx.inf + elif ctx.re(z) > 0: + return ctx.one + else: + return ctx.nan + return ctx.gamma(z) + if a == b: + return ctx.zero + # Standardize + if ctx.re(a) > ctx.re(b): + return -ctx.gammainc(z, b, a, regularized) + # Generalized gamma + if upper_modified and lower_modified: + return +ctx._gamma3(z, a, b, regularized) + # Upper gamma + elif lower_modified: + return ctx._upper_gamma(z, a, regularized) + # Lower gamma + elif upper_modified: + return ctx._lower_gamma(z, b, regularized) + +@defun +def _lower_gamma(ctx, z, b, regularized=False): + # Pole + if ctx.isnpint(z): + return type(z)(ctx.inf) + G = [z] * regularized + negb = ctx.fneg(b, exact=True) + def h(z): + T1 = [ctx.exp(negb), b, z], [1, z, -1], [], G, [1], [1+z], b + return (T1,) + return ctx.hypercomb(h, [z]) + +@defun +def _upper_gamma(ctx, z, a, regularized=False): + # Fast integer case, when available + if ctx.isint(z): + try: + if regularized: + # Gamma pole + if ctx.isnpint(z): + return type(z)(ctx.zero) + orig = ctx.prec + try: + ctx.prec += 10 + return ctx._gamma_upper_int(z, a) / ctx.gamma(z) + finally: + ctx.prec = orig + else: + return ctx._gamma_upper_int(z, a) + except NotImplementedError: + pass + # hypercomb is unable to detect the exact zeros, so handle them here + if z == 2 and a == -1: + return (z+a)*0 + if z == 3 and (a == -1-1j or a == -1+1j): + return (z+a)*0 + nega = ctx.fneg(a, exact=True) + G = [z] * regularized + # Use 2F0 series when possible; fall back to lower gamma representation + try: + def h(z): + r = z-1 + return [([ctx.exp(nega), a], [1, r], [], G, [1, -r], [], 1/nega)] + return ctx.hypercomb(h, [z], force_series=True) + except ctx.NoConvergence: + def h(z): + T1 = [], [1, z-1], [z], G, [], [], 0 + T2 = [-ctx.exp(nega), a, z], [1, z, -1], [], G, [1], [1+z], a + return T1, T2 + return ctx.hypercomb(h, [z]) + +@defun +def _gamma3(ctx, z, a, b, regularized=False): + pole = ctx.isnpint(z) + if regularized and pole: + return ctx.zero + try: + ctx.prec += 15 + # We don't know in advance whether it's better to write as a difference + # of lower or upper gamma functions, so try both + T1 = ctx.gammainc(z, a, regularized=regularized) + T2 = ctx.gammainc(z, b, regularized=regularized) + R = T1 - T2 + if ctx.mag(R) - max(ctx.mag(T1), ctx.mag(T2)) > -10: + return R + if not pole: + T1 = ctx.gammainc(z, 0, b, regularized=regularized) + T2 = ctx.gammainc(z, 0, a, regularized=regularized) + R = T1 - T2 + # May be ok, but should probably at least print a warning + # about possible cancellation + if 1: #ctx.mag(R) - max(ctx.mag(T1), ctx.mag(T2)) > -10: + return R + finally: + ctx.prec -= 15 + raise NotImplementedError + +@defun_wrapped +def expint(ctx, n, z): + if ctx.isint(n) and ctx._is_real_type(z): + try: + return ctx._expint_int(n, z) + except NotImplementedError: + pass + if ctx.isnan(n) or ctx.isnan(z): + return z*n + if z == ctx.inf: + return 1/z + if z == 0: + # integral from 1 to infinity of t^n + if ctx.re(n) <= 1: + # TODO: reasonable sign of infinity + return type(z)(ctx.inf) + else: + return ctx.one/(n-1) + if n == 0: + return ctx.exp(-z)/z + if n == -1: + return ctx.exp(-z)*(z+1)/z**2 + return z**(n-1) * ctx.gammainc(1-n, z) + +@defun_wrapped +def li(ctx, z, offset=False): + if offset: + if z == 2: + return ctx.zero + return ctx.ei(ctx.ln(z)) - ctx.ei(ctx.ln2) + if not z: + return z + if z == 1: + return ctx.ninf + return ctx.ei(ctx.ln(z)) + +@defun +def ei(ctx, z): + try: + return ctx._ei(z) + except NotImplementedError: + return ctx._ei_generic(z) + +@defun_wrapped +def _ei_generic(ctx, z): + # Note: the following is currently untested because mp and fp + # both use special-case ei code + if z == ctx.inf: + return z + if z == ctx.ninf: + return ctx.zero + if ctx.mag(z) > 1: + try: + r = ctx.one/z + v = ctx.exp(z)*ctx.hyper([1,1],[],r, + maxterms=ctx.prec, force_series=True)/z + im = ctx._im(z) + if im > 0: + v += ctx.pi*ctx.j + if im < 0: + v -= ctx.pi*ctx.j + return v + except ctx.NoConvergence: + pass + v = z*ctx.hyp2f2(1,1,2,2,z) + ctx.euler + if ctx._im(z): + v += 0.5*(ctx.log(z) - ctx.log(ctx.one/z)) + else: + v += ctx.log(abs(z)) + return v + +@defun +def e1(ctx, z): + try: + return ctx._e1(z) + except NotImplementedError: + return ctx.expint(1, z) + +@defun +def ci(ctx, z): + try: + return ctx._ci(z) + except NotImplementedError: + return ctx._ci_generic(z) + +@defun_wrapped +def _ci_generic(ctx, z): + if ctx.isinf(z): + if z == ctx.inf: return ctx.zero + if z == ctx.ninf: return ctx.pi*1j + jz = ctx.fmul(ctx.j,z,exact=True) + njz = ctx.fneg(jz,exact=True) + v = 0.5*(ctx.ei(jz) + ctx.ei(njz)) + zreal = ctx._re(z) + zimag = ctx._im(z) + if zreal == 0: + if zimag > 0: v += ctx.pi*0.5j + if zimag < 0: v -= ctx.pi*0.5j + if zreal < 0: + if zimag >= 0: v += ctx.pi*1j + if zimag < 0: v -= ctx.pi*1j + if ctx._is_real_type(z) and zreal > 0: + v = ctx._re(v) + return v + +@defun +def si(ctx, z): + try: + return ctx._si(z) + except NotImplementedError: + return ctx._si_generic(z) + +@defun_wrapped +def _si_generic(ctx, z): + if ctx.isinf(z): + if z == ctx.inf: return 0.5*ctx.pi + if z == ctx.ninf: return -0.5*ctx.pi + # Suffers from cancellation near 0 + if ctx.mag(z) >= -1: + jz = ctx.fmul(ctx.j,z,exact=True) + njz = ctx.fneg(jz,exact=True) + v = (-0.5j)*(ctx.ei(jz) - ctx.ei(njz)) + zreal = ctx._re(z) + if zreal > 0: + v -= 0.5*ctx.pi + if zreal < 0: + v += 0.5*ctx.pi + if ctx._is_real_type(z): + v = ctx._re(v) + return v + else: + return z*ctx.hyp1f2((1,2),(3,2),(3,2),-0.25*z*z) + +@defun_wrapped +def chi(ctx, z): + nz = ctx.fneg(z, exact=True) + v = 0.5*(ctx.ei(z) + ctx.ei(nz)) + zreal = ctx._re(z) + zimag = ctx._im(z) + if zimag > 0: + v += ctx.pi*0.5j + elif zimag < 0: + v -= ctx.pi*0.5j + elif zreal < 0: + v += ctx.pi*1j + return v + +@defun_wrapped +def shi(ctx, z): + # Suffers from cancellation near 0 + if ctx.mag(z) >= -1: + nz = ctx.fneg(z, exact=True) + v = 0.5*(ctx.ei(z) - ctx.ei(nz)) + zimag = ctx._im(z) + if zimag > 0: v -= 0.5j*ctx.pi + if zimag < 0: v += 0.5j*ctx.pi + return v + else: + return z * ctx.hyp1f2((1,2),(3,2),(3,2),0.25*z*z) + +@defun_wrapped +def fresnels(ctx, z): + if z == ctx.inf: + return ctx.mpf(0.5) + if z == ctx.ninf: + return ctx.mpf(-0.5) + return ctx.pi*z**3/6*ctx.hyp1f2((3,4),(3,2),(7,4),-ctx.pi**2*z**4/16) + +@defun_wrapped +def fresnelc(ctx, z): + if z == ctx.inf: + return ctx.mpf(0.5) + if z == ctx.ninf: + return ctx.mpf(-0.5) + return z*ctx.hyp1f2((1,4),(1,2),(5,4),-ctx.pi**2*z**4/16) diff --git a/lib/python3.12/site-packages/mpmath/functions/factorials.py b/lib/python3.12/site-packages/mpmath/functions/factorials.py new file mode 100644 index 0000000000000000000000000000000000000000..9259e40b95bf1c908a7ad98b59bbb33528606b07 --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/functions/factorials.py @@ -0,0 +1,187 @@ +from ..libmp.backend import xrange +from .functions import defun, defun_wrapped + +@defun +def gammaprod(ctx, a, b, _infsign=False): + a = [ctx.convert(x) for x in a] + b = [ctx.convert(x) for x in b] + poles_num = [] + poles_den = [] + regular_num = [] + regular_den = [] + for x in a: [regular_num, poles_num][ctx.isnpint(x)].append(x) + for x in b: [regular_den, poles_den][ctx.isnpint(x)].append(x) + # One more pole in numerator or denominator gives 0 or inf + if len(poles_num) < len(poles_den): return ctx.zero + if len(poles_num) > len(poles_den): + # Get correct sign of infinity for x+h, h -> 0 from above + # XXX: hack, this should be done properly + if _infsign: + a = [x and x*(1+ctx.eps) or x+ctx.eps for x in poles_num] + b = [x and x*(1+ctx.eps) or x+ctx.eps for x in poles_den] + return ctx.sign(ctx.gammaprod(a+regular_num,b+regular_den)) * ctx.inf + else: + return ctx.inf + # All poles cancel + # lim G(i)/G(j) = (-1)**(i+j) * gamma(1-j) / gamma(1-i) + p = ctx.one + orig = ctx.prec + try: + ctx.prec = orig + 15 + while poles_num: + i = poles_num.pop() + j = poles_den.pop() + p *= (-1)**(i+j) * ctx.gamma(1-j) / ctx.gamma(1-i) + for x in regular_num: p *= ctx.gamma(x) + for x in regular_den: p /= ctx.gamma(x) + finally: + ctx.prec = orig + return +p + +@defun +def beta(ctx, x, y): + x = ctx.convert(x) + y = ctx.convert(y) + if ctx.isinf(y): + x, y = y, x + if ctx.isinf(x): + if x == ctx.inf and not ctx._im(y): + if y == ctx.ninf: + return ctx.nan + if y > 0: + return ctx.zero + if ctx.isint(y): + return ctx.nan + if y < 0: + return ctx.sign(ctx.gamma(y)) * ctx.inf + return ctx.nan + xy = ctx.fadd(x, y, prec=2*ctx.prec) + return ctx.gammaprod([x, y], [xy]) + +@defun +def binomial(ctx, n, k): + n1 = ctx.fadd(n, 1, prec=2*ctx.prec) + k1 = ctx.fadd(k, 1, prec=2*ctx.prec) + nk1 = ctx.fsub(n1, k, prec=2*ctx.prec) + return ctx.gammaprod([n1], [k1, nk1]) + +@defun +def rf(ctx, x, n): + xn = ctx.fadd(x, n, prec=2*ctx.prec) + return ctx.gammaprod([xn], [x]) + +@defun +def ff(ctx, x, n): + x1 = ctx.fadd(x, 1, prec=2*ctx.prec) + xn1 = ctx.fadd(ctx.fsub(x, n, prec=2*ctx.prec), 1, prec=2*ctx.prec) + return ctx.gammaprod([x1], [xn1]) + +@defun_wrapped +def fac2(ctx, x): + if ctx.isinf(x): + if x == ctx.inf: + return x + return ctx.nan + return 2**(x/2)*(ctx.pi/2)**((ctx.cospi(x)-1)/4)*ctx.gamma(x/2+1) + +@defun_wrapped +def barnesg(ctx, z): + if ctx.isinf(z): + if z == ctx.inf: + return z + return ctx.nan + if ctx.isnan(z): + return z + if (not ctx._im(z)) and ctx._re(z) <= 0 and ctx.isint(ctx._re(z)): + return z*0 + # Account for size (would not be needed if computing log(G)) + if abs(z) > 5: + ctx.dps += 2*ctx.log(abs(z),2) + # Reflection formula + if ctx.re(z) < -ctx.dps: + w = 1-z + pi2 = 2*ctx.pi + u = ctx.expjpi(2*w) + v = ctx.j*ctx.pi/12 - ctx.j*ctx.pi*w**2/2 + w*ctx.ln(1-u) - \ + ctx.j*ctx.polylog(2, u)/pi2 + v = ctx.barnesg(2-z)*ctx.exp(v)/pi2**w + if ctx._is_real_type(z): + v = ctx._re(v) + return v + # Estimate terms for asymptotic expansion + # TODO: fixme, obviously + N = ctx.dps // 2 + 5 + G = 1 + while abs(z) < N or ctx.re(z) < 1: + G /= ctx.gamma(z) + z += 1 + z -= 1 + s = ctx.mpf(1)/12 + s -= ctx.log(ctx.glaisher) + s += z*ctx.log(2*ctx.pi)/2 + s += (z**2/2-ctx.mpf(1)/12)*ctx.log(z) + s -= 3*z**2/4 + z2k = z2 = z**2 + for k in xrange(1, N+1): + t = ctx.bernoulli(2*k+2) / (4*k*(k+1)*z2k) + if abs(t) < ctx.eps: + #print k, N # check how many terms were needed + break + z2k *= z2 + s += t + #if k == N: + # print "warning: series for barnesg failed to converge", ctx.dps + return G*ctx.exp(s) + +@defun +def superfac(ctx, z): + return ctx.barnesg(z+2) + +@defun_wrapped +def hyperfac(ctx, z): + # XXX: estimate needed extra bits accurately + if z == ctx.inf: + return z + if abs(z) > 5: + extra = 4*int(ctx.log(abs(z),2)) + else: + extra = 0 + ctx.prec += extra + if not ctx._im(z) and ctx._re(z) < 0 and ctx.isint(ctx._re(z)): + n = int(ctx.re(z)) + h = ctx.hyperfac(-n-1) + if ((n+1)//2) & 1: + h = -h + if ctx._is_complex_type(z): + return h + 0j + return h + zp1 = z+1 + # Wrong branch cut + #v = ctx.gamma(zp1)**z + #ctx.prec -= extra + #return v / ctx.barnesg(zp1) + v = ctx.exp(z*ctx.loggamma(zp1)) + ctx.prec -= extra + return v / ctx.barnesg(zp1) + +''' +@defun +def psi0(ctx, z): + """Shortcut for psi(0,z) (the digamma function)""" + return ctx.psi(0, z) + +@defun +def psi1(ctx, z): + """Shortcut for psi(1,z) (the trigamma function)""" + return ctx.psi(1, z) + +@defun +def psi2(ctx, z): + """Shortcut for psi(2,z) (the tetragamma function)""" + return ctx.psi(2, z) + +@defun +def psi3(ctx, z): + """Shortcut for psi(3,z) (the pentagamma function)""" + return ctx.psi(3, z) +''' diff --git a/lib/python3.12/site-packages/mpmath/functions/functions.py b/lib/python3.12/site-packages/mpmath/functions/functions.py new file mode 100644 index 0000000000000000000000000000000000000000..4cdf5dd921418db10847ea75b32f8e6dfacdba64 --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/functions/functions.py @@ -0,0 +1,645 @@ +from ..libmp.backend import xrange + +class SpecialFunctions(object): + """ + This class implements special functions using high-level code. + + Elementary and some other functions (e.g. gamma function, basecase + hypergeometric series) are assumed to be predefined by the context as + "builtins" or "low-level" functions. + """ + defined_functions = {} + + # The series for the Jacobi theta functions converge for |q| < 1; + # in the current implementation they throw a ValueError for + # abs(q) > THETA_Q_LIM + THETA_Q_LIM = 1 - 10**-7 + + def __init__(self): + cls = self.__class__ + for name in cls.defined_functions: + f, wrap = cls.defined_functions[name] + cls._wrap_specfun(name, f, wrap) + + self.mpq_1 = self._mpq((1,1)) + self.mpq_0 = self._mpq((0,1)) + self.mpq_1_2 = self._mpq((1,2)) + self.mpq_3_2 = self._mpq((3,2)) + self.mpq_1_4 = self._mpq((1,4)) + self.mpq_1_16 = self._mpq((1,16)) + self.mpq_3_16 = self._mpq((3,16)) + self.mpq_5_2 = self._mpq((5,2)) + self.mpq_3_4 = self._mpq((3,4)) + self.mpq_7_4 = self._mpq((7,4)) + self.mpq_5_4 = self._mpq((5,4)) + self.mpq_1_3 = self._mpq((1,3)) + self.mpq_2_3 = self._mpq((2,3)) + self.mpq_4_3 = self._mpq((4,3)) + self.mpq_1_6 = self._mpq((1,6)) + self.mpq_5_6 = self._mpq((5,6)) + self.mpq_5_3 = self._mpq((5,3)) + + self._misc_const_cache = {} + + self._aliases.update({ + 'phase' : 'arg', + 'conjugate' : 'conj', + 'nthroot' : 'root', + 'polygamma' : 'psi', + 'hurwitz' : 'zeta', + #'digamma' : 'psi0', + #'trigamma' : 'psi1', + #'tetragamma' : 'psi2', + #'pentagamma' : 'psi3', + 'fibonacci' : 'fib', + 'factorial' : 'fac', + }) + + self.zetazero_memoized = self.memoize(self.zetazero) + + # Default -- do nothing + @classmethod + def _wrap_specfun(cls, name, f, wrap): + setattr(cls, name, f) + + # Optional fast versions of common functions in common cases. + # If not overridden, default (generic hypergeometric series) + # implementations will be used + def _besselj(ctx, n, z): raise NotImplementedError + def _erf(ctx, z): raise NotImplementedError + def _erfc(ctx, z): raise NotImplementedError + def _gamma_upper_int(ctx, z, a): raise NotImplementedError + def _expint_int(ctx, n, z): raise NotImplementedError + def _zeta(ctx, s): raise NotImplementedError + def _zetasum_fast(ctx, s, a, n, derivatives, reflect): raise NotImplementedError + def _ei(ctx, z): raise NotImplementedError + def _e1(ctx, z): raise NotImplementedError + def _ci(ctx, z): raise NotImplementedError + def _si(ctx, z): raise NotImplementedError + def _altzeta(ctx, s): raise NotImplementedError + +def defun_wrapped(f): + SpecialFunctions.defined_functions[f.__name__] = f, True + return f + +def defun(f): + SpecialFunctions.defined_functions[f.__name__] = f, False + return f + +def defun_static(f): + setattr(SpecialFunctions, f.__name__, f) + return f + +@defun_wrapped +def cot(ctx, z): return ctx.one / ctx.tan(z) + +@defun_wrapped +def sec(ctx, z): return ctx.one / ctx.cos(z) + +@defun_wrapped +def csc(ctx, z): return ctx.one / ctx.sin(z) + +@defun_wrapped +def coth(ctx, z): return ctx.one / ctx.tanh(z) + +@defun_wrapped +def sech(ctx, z): return ctx.one / ctx.cosh(z) + +@defun_wrapped +def csch(ctx, z): return ctx.one / ctx.sinh(z) + +@defun_wrapped +def acot(ctx, z): + if not z: + return ctx.pi * 0.5 + else: + return ctx.atan(ctx.one / z) + +@defun_wrapped +def asec(ctx, z): return ctx.acos(ctx.one / z) + +@defun_wrapped +def acsc(ctx, z): return ctx.asin(ctx.one / z) + +@defun_wrapped +def acoth(ctx, z): + if not z: + return ctx.pi * 0.5j + else: + return ctx.atanh(ctx.one / z) + + +@defun_wrapped +def asech(ctx, z): return ctx.acosh(ctx.one / z) + +@defun_wrapped +def acsch(ctx, z): return ctx.asinh(ctx.one / z) + +@defun +def sign(ctx, x): + x = ctx.convert(x) + if not x or ctx.isnan(x): + return x + if ctx._is_real_type(x): + if x > 0: + return ctx.one + else: + return -ctx.one + return x / abs(x) + +@defun +def agm(ctx, a, b=1): + if b == 1: + return ctx.agm1(a) + a = ctx.convert(a) + b = ctx.convert(b) + return ctx._agm(a, b) + +@defun_wrapped +def sinc(ctx, x): + if ctx.isinf(x): + return 1/x + if not x: + return x+1 + return ctx.sin(x)/x + +@defun_wrapped +def sincpi(ctx, x): + if ctx.isinf(x): + return 1/x + if not x: + return x+1 + return ctx.sinpi(x)/(ctx.pi*x) + +# TODO: tests; improve implementation +@defun_wrapped +def expm1(ctx, x): + if not x: + return ctx.zero + # exp(x) - 1 ~ x + if ctx.mag(x) < -ctx.prec: + return x + 0.5*x**2 + # TODO: accurately eval the smaller of the real/imag parts + return ctx.sum_accurately(lambda: iter([ctx.exp(x),-1]),1) + +@defun_wrapped +def log1p(ctx, x): + if not x: + return ctx.zero + if ctx.mag(x) < -ctx.prec: + return x - 0.5*x**2 + return ctx.log(ctx.fadd(1, x, prec=2*ctx.prec)) + +@defun_wrapped +def powm1(ctx, x, y): + mag = ctx.mag + one = ctx.one + w = x**y - one + M = mag(w) + # Only moderate cancellation + if M > -8: + return w + # Check for the only possible exact cases + if not w: + if (not y) or (x in (1, -1, 1j, -1j) and ctx.isint(y)): + return w + x1 = x - one + magy = mag(y) + lnx = ctx.ln(x) + # Small y: x^y - 1 ~ log(x)*y + O(log(x)^2 * y^2) + if magy + mag(lnx) < -ctx.prec: + return lnx*y + (lnx*y)**2/2 + # TODO: accurately eval the smaller of the real/imag part + return ctx.sum_accurately(lambda: iter([x**y, -1]), 1) + +@defun +def _rootof1(ctx, k, n): + k = int(k) + n = int(n) + k %= n + if not k: + return ctx.one + elif 2*k == n: + return -ctx.one + elif 4*k == n: + return ctx.j + elif 4*k == 3*n: + return -ctx.j + return ctx.expjpi(2*ctx.mpf(k)/n) + +@defun +def root(ctx, x, n, k=0): + n = int(n) + x = ctx.convert(x) + if k: + # Special case: there is an exact real root + if (n & 1 and 2*k == n-1) and (not ctx.im(x)) and (ctx.re(x) < 0): + return -ctx.root(-x, n) + # Multiply by root of unity + prec = ctx.prec + try: + ctx.prec += 10 + v = ctx.root(x, n, 0) * ctx._rootof1(k, n) + finally: + ctx.prec = prec + return +v + return ctx._nthroot(x, n) + +@defun +def unitroots(ctx, n, primitive=False): + gcd = ctx._gcd + prec = ctx.prec + try: + ctx.prec += 10 + if primitive: + v = [ctx._rootof1(k,n) for k in range(n) if gcd(k,n) == 1] + else: + # TODO: this can be done *much* faster + v = [ctx._rootof1(k,n) for k in range(n)] + finally: + ctx.prec = prec + return [+x for x in v] + +@defun +def arg(ctx, x): + x = ctx.convert(x) + re = ctx._re(x) + im = ctx._im(x) + return ctx.atan2(im, re) + +@defun +def fabs(ctx, x): + return abs(ctx.convert(x)) + +@defun +def re(ctx, x): + x = ctx.convert(x) + if hasattr(x, "real"): # py2.5 doesn't have .real/.imag for all numbers + return x.real + return x + +@defun +def im(ctx, x): + x = ctx.convert(x) + if hasattr(x, "imag"): # py2.5 doesn't have .real/.imag for all numbers + return x.imag + return ctx.zero + +@defun +def conj(ctx, x): + x = ctx.convert(x) + try: + return x.conjugate() + except AttributeError: + return x + +@defun +def polar(ctx, z): + return (ctx.fabs(z), ctx.arg(z)) + +@defun_wrapped +def rect(ctx, r, phi): + return r * ctx.mpc(*ctx.cos_sin(phi)) + +@defun +def log(ctx, x, b=None): + if b is None: + return ctx.ln(x) + wp = ctx.prec + 20 + return ctx.ln(x, prec=wp) / ctx.ln(b, prec=wp) + +@defun +def log10(ctx, x): + return ctx.log(x, 10) + +@defun +def fmod(ctx, x, y): + return ctx.convert(x) % ctx.convert(y) + +@defun +def degrees(ctx, x): + return x / ctx.degree + +@defun +def radians(ctx, x): + return x * ctx.degree + +def _lambertw_special(ctx, z, k): + # W(0,0) = 0; all other branches are singular + if not z: + if not k: + return z + return ctx.ninf + z + if z == ctx.inf: + if k == 0: + return z + else: + return z + 2*k*ctx.pi*ctx.j + if z == ctx.ninf: + return (-z) + (2*k+1)*ctx.pi*ctx.j + # Some kind of nan or complex inf/nan? + return ctx.ln(z) + +import math +import cmath + +def _lambertw_approx_hybrid(z, k): + imag_sign = 0 + if hasattr(z, "imag"): + x = float(z.real) + y = z.imag + if y: + imag_sign = (-1) ** (y < 0) + y = float(y) + else: + x = float(z) + y = 0.0 + imag_sign = 0 + # hack to work regardless of whether Python supports -0.0 + if not y: + y = 0.0 + z = complex(x,y) + if k == 0: + if -4.0 < y < 4.0 and -1.0 < x < 2.5: + if imag_sign: + # Taylor series in upper/lower half-plane + if y > 1.00: return (0.876+0.645j) + (0.118-0.174j)*(z-(0.75+2.5j)) + if y > 0.25: return (0.505+0.204j) + (0.375-0.132j)*(z-(0.75+0.5j)) + if y < -1.00: return (0.876-0.645j) + (0.118+0.174j)*(z-(0.75-2.5j)) + if y < -0.25: return (0.505-0.204j) + (0.375+0.132j)*(z-(0.75-0.5j)) + # Taylor series near -1 + if x < -0.5: + if imag_sign >= 0: + return (-0.318+1.34j) + (-0.697-0.593j)*(z+1) + else: + return (-0.318-1.34j) + (-0.697+0.593j)*(z+1) + # return real type + r = -0.367879441171442 + if (not imag_sign) and x > r: + z = x + # Singularity near -1/e + if x < -0.2: + return -1 + 2.33164398159712*(z-r)**0.5 - 1.81218788563936*(z-r) + # Taylor series near 0 + if x < 0.5: return z + # Simple linear approximation + return 0.2 + 0.3*z + if (not imag_sign) and x > 0.0: + L1 = math.log(x); L2 = math.log(L1) + else: + L1 = cmath.log(z); L2 = cmath.log(L1) + elif k == -1: + # return real type + r = -0.367879441171442 + if (not imag_sign) and r < x < 0.0: + z = x + if (imag_sign >= 0) and y < 0.1 and -0.6 < x < -0.2: + return -1 - 2.33164398159712*(z-r)**0.5 - 1.81218788563936*(z-r) + if (not imag_sign) and -0.2 <= x < 0.0: + L1 = math.log(-x) + return L1 - math.log(-L1) + else: + if imag_sign == -1 and (not y) and x < 0.0: + L1 = cmath.log(z) - 3.1415926535897932j + else: + L1 = cmath.log(z) - 6.2831853071795865j + L2 = cmath.log(L1) + return L1 - L2 + L2/L1 + L2*(L2-2)/(2*L1**2) + +def _lambertw_series(ctx, z, k, tol): + """ + Return rough approximation for W_k(z) from an asymptotic series, + sufficiently accurate for the Halley iteration to converge to + the correct value. + """ + magz = ctx.mag(z) + if (-10 < magz < 900) and (-1000 < k < 1000): + # Near the branch point at -1/e + if magz < 1 and abs(z+0.36787944117144) < 0.05: + if k == 0 or (k == -1 and ctx._im(z) >= 0) or \ + (k == 1 and ctx._im(z) < 0): + delta = ctx.sum_accurately(lambda: [z, ctx.exp(-1)]) + cancellation = -ctx.mag(delta) + ctx.prec += cancellation + # Use series given in Corless et al. + p = ctx.sqrt(2*(ctx.e*z+1)) + ctx.prec -= cancellation + u = {0:ctx.mpf(-1), 1:ctx.mpf(1)} + a = {0:ctx.mpf(2), 1:ctx.mpf(-1)} + if k != 0: + p = -p + s = ctx.zero + # The series converges, so we could use it directly, but unless + # *extremely* close, it is better to just use the first few + # terms to get a good approximation for the iteration + for l in xrange(max(2,cancellation)): + if l not in u: + a[l] = ctx.fsum(u[j]*u[l+1-j] for j in xrange(2,l)) + u[l] = (l-1)*(u[l-2]/2+a[l-2]/4)/(l+1)-a[l]/2-u[l-1]/(l+1) + term = u[l] * p**l + s += term + if ctx.mag(term) < -tol: + return s, True + l += 1 + ctx.prec += cancellation//2 + return s, False + if k == 0 or k == -1: + return _lambertw_approx_hybrid(z, k), False + if k == 0: + if magz < -1: + return z*(1-z), False + L1 = ctx.ln(z) + L2 = ctx.ln(L1) + elif k == -1 and (not ctx._im(z)) and (-0.36787944117144 < ctx._re(z) < 0): + L1 = ctx.ln(-z) + return L1 - ctx.ln(-L1), False + else: + # This holds both as z -> 0 and z -> inf. + # Relative error is O(1/log(z)). + L1 = ctx.ln(z) + 2j*ctx.pi*k + L2 = ctx.ln(L1) + return L1 - L2 + L2/L1 + L2*(L2-2)/(2*L1**2), False + +@defun +def lambertw(ctx, z, k=0): + z = ctx.convert(z) + k = int(k) + if not ctx.isnormal(z): + return _lambertw_special(ctx, z, k) + prec = ctx.prec + ctx.prec += 20 + ctx.mag(k or 1) + wp = ctx.prec + tol = wp - 5 + w, done = _lambertw_series(ctx, z, k, tol) + if not done: + # Use Halley iteration to solve w*exp(w) = z + two = ctx.mpf(2) + for i in xrange(100): + ew = ctx.exp(w) + wew = w*ew + wewz = wew-z + wn = w - wewz/(wew+ew-(w+two)*wewz/(two*w+two)) + if ctx.mag(wn-w) <= ctx.mag(wn) - tol: + w = wn + break + else: + w = wn + if i == 100: + ctx.warn("Lambert W iteration failed to converge for z = %s" % z) + ctx.prec = prec + return +w + +@defun_wrapped +def bell(ctx, n, x=1): + x = ctx.convert(x) + if not n: + if ctx.isnan(x): + return x + return type(x)(1) + if ctx.isinf(x) or ctx.isinf(n) or ctx.isnan(x) or ctx.isnan(n): + return x**n + if n == 1: return x + if n == 2: return x*(x+1) + if x == 0: return ctx.sincpi(n) + return _polyexp(ctx, n, x, True) / ctx.exp(x) + +def _polyexp(ctx, n, x, extra=False): + def _terms(): + if extra: + yield ctx.sincpi(n) + t = x + k = 1 + while 1: + yield k**n * t + k += 1 + t = t*x/k + return ctx.sum_accurately(_terms, check_step=4) + +@defun_wrapped +def polyexp(ctx, s, z): + if ctx.isinf(z) or ctx.isinf(s) or ctx.isnan(z) or ctx.isnan(s): + return z**s + if z == 0: return z*s + if s == 0: return ctx.expm1(z) + if s == 1: return ctx.exp(z)*z + if s == 2: return ctx.exp(z)*z*(z+1) + return _polyexp(ctx, s, z) + +@defun_wrapped +def cyclotomic(ctx, n, z): + n = int(n) + if n < 0: + raise ValueError("n cannot be negative") + p = ctx.one + if n == 0: + return p + if n == 1: + return z - p + if n == 2: + return z + p + # Use divisor product representation. Unfortunately, this sometimes + # includes singularities for roots of unity, which we have to cancel out. + # Matching zeros/poles pairwise, we have (1-z^a)/(1-z^b) ~ a/b + O(z-1). + a_prod = 1 + b_prod = 1 + num_zeros = 0 + num_poles = 0 + for d in range(1,n+1): + if not n % d: + w = ctx.moebius(n//d) + # Use powm1 because it is important that we get 0 only + # if it really is exactly 0 + b = -ctx.powm1(z, d) + if b: + p *= b**w + else: + if w == 1: + a_prod *= d + num_zeros += 1 + elif w == -1: + b_prod *= d + num_poles += 1 + #print n, num_zeros, num_poles + if num_zeros: + if num_zeros > num_poles: + p *= 0 + else: + p *= a_prod + p /= b_prod + return p + +@defun +def mangoldt(ctx, n): + r""" + Evaluates the von Mangoldt function `\Lambda(n) = \log p` + if `n = p^k` a power of a prime, and `\Lambda(n) = 0` otherwise. + + **Examples** + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> [mangoldt(n) for n in range(-2,3)] + [0.0, 0.0, 0.0, 0.0, 0.6931471805599453094172321] + >>> mangoldt(6) + 0.0 + >>> mangoldt(7) + 1.945910149055313305105353 + >>> mangoldt(8) + 0.6931471805599453094172321 + >>> fsum(mangoldt(n) for n in range(101)) + 94.04531122935739224600493 + >>> fsum(mangoldt(n) for n in range(10001)) + 10013.39669326311478372032 + + """ + n = int(n) + if n < 2: + return ctx.zero + if n % 2 == 0: + # Must be a power of two + if n & (n-1) == 0: + return +ctx.ln2 + else: + return ctx.zero + # TODO: the following could be generalized into a perfect + # power testing function + # --- + # Look for a small factor + for p in (3,5,7,11,13,17,19,23,29,31): + if not n % p: + q, r = n // p, 0 + while q > 1: + q, r = divmod(q, p) + if r: + return ctx.zero + return ctx.ln(p) + if ctx.isprime(n): + return ctx.ln(n) + # Obviously, we could use arbitrary-precision arithmetic for this... + if n > 10**30: + raise NotImplementedError + k = 2 + while 1: + p = int(n**(1./k) + 0.5) + if p < 2: + return ctx.zero + if p ** k == n: + if ctx.isprime(p): + return ctx.ln(p) + k += 1 + +@defun +def stirling1(ctx, n, k, exact=False): + v = ctx._stirling1(int(n), int(k)) + if exact: + return int(v) + else: + return ctx.mpf(v) + +@defun +def stirling2(ctx, n, k, exact=False): + v = ctx._stirling2(int(n), int(k)) + if exact: + return int(v) + else: + return ctx.mpf(v) diff --git a/lib/python3.12/site-packages/mpmath/functions/hypergeometric.py b/lib/python3.12/site-packages/mpmath/functions/hypergeometric.py new file mode 100644 index 0000000000000000000000000000000000000000..ddb50cbf3ea6daa5982678d3c26157a67a7d7945 --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/functions/hypergeometric.py @@ -0,0 +1,1413 @@ +from ..libmp.backend import xrange +from .functions import defun, defun_wrapped + +def _check_need_perturb(ctx, terms, prec, discard_known_zeros): + perturb = recompute = False + extraprec = 0 + discard = [] + for term_index, term in enumerate(terms): + w_s, c_s, alpha_s, beta_s, a_s, b_s, z = term + have_singular_nongamma_weight = False + # Avoid division by zero in leading factors (TODO: + # also check for near division by zero?) + for k, w in enumerate(w_s): + if not w: + if ctx.re(c_s[k]) <= 0 and c_s[k]: + perturb = recompute = True + have_singular_nongamma_weight = True + pole_count = [0, 0, 0] + # Check for gamma and series poles and near-poles + for data_index, data in enumerate([alpha_s, beta_s, b_s]): + for i, x in enumerate(data): + n, d = ctx.nint_distance(x) + # Poles + if n > 0: + continue + if d == ctx.ninf: + # OK if we have a polynomial + # ------------------------------ + ok = False + if data_index == 2: + for u in a_s: + if ctx.isnpint(u) and u >= int(n): + ok = True + break + if ok: + continue + pole_count[data_index] += 1 + # ------------------------------ + #perturb = recompute = True + #return perturb, recompute, extraprec + elif d < -4: + extraprec += -d + recompute = True + if discard_known_zeros and pole_count[1] > pole_count[0] + pole_count[2] \ + and not have_singular_nongamma_weight: + discard.append(term_index) + elif sum(pole_count): + perturb = recompute = True + return perturb, recompute, extraprec, discard + +_hypercomb_msg = """ +hypercomb() failed to converge to the requested %i bits of accuracy +using a working precision of %i bits. The function value may be zero or +infinite; try passing zeroprec=N or infprec=M to bound finite values between +2^(-N) and 2^M. Otherwise try a higher maxprec or maxterms. +""" + +@defun +def hypercomb(ctx, function, params=[], discard_known_zeros=True, **kwargs): + orig = ctx.prec + sumvalue = ctx.zero + dist = ctx.nint_distance + ninf = ctx.ninf + orig_params = params[:] + verbose = kwargs.get('verbose', False) + maxprec = kwargs.get('maxprec', ctx._default_hyper_maxprec(orig)) + kwargs['maxprec'] = maxprec # For calls to hypsum + zeroprec = kwargs.get('zeroprec') + infprec = kwargs.get('infprec') + perturbed_reference_value = None + hextra = 0 + try: + while 1: + ctx.prec += 10 + if ctx.prec > maxprec: + raise ValueError(_hypercomb_msg % (orig, ctx.prec)) + orig2 = ctx.prec + params = orig_params[:] + terms = function(*params) + if verbose: + print() + print("ENTERING hypercomb main loop") + print("prec =", ctx.prec) + print("hextra", hextra) + perturb, recompute, extraprec, discard = \ + _check_need_perturb(ctx, terms, orig, discard_known_zeros) + ctx.prec += extraprec + if perturb: + if "hmag" in kwargs: + hmag = kwargs["hmag"] + elif ctx._fixed_precision: + hmag = int(ctx.prec*0.3) + else: + hmag = orig + 10 + hextra + h = ctx.ldexp(ctx.one, -hmag) + ctx.prec = orig2 + 10 + hmag + 10 + for k in range(len(params)): + params[k] += h + # Heuristically ensure that the perturbations + # are "independent" so that two perturbations + # don't accidentally cancel each other out + # in a subtraction. + h += h/(k+1) + if recompute: + terms = function(*params) + if discard_known_zeros: + terms = [term for (i, term) in enumerate(terms) if i not in discard] + if not terms: + return ctx.zero + evaluated_terms = [] + for term_index, term_data in enumerate(terms): + w_s, c_s, alpha_s, beta_s, a_s, b_s, z = term_data + if verbose: + print() + print(" Evaluating term %i/%i : %iF%i" % \ + (term_index+1, len(terms), len(a_s), len(b_s))) + print(" powers", ctx.nstr(w_s), ctx.nstr(c_s)) + print(" gamma", ctx.nstr(alpha_s), ctx.nstr(beta_s)) + print(" hyper", ctx.nstr(a_s), ctx.nstr(b_s)) + print(" z", ctx.nstr(z)) + #v = ctx.hyper(a_s, b_s, z, **kwargs) + #for a in alpha_s: v *= ctx.gamma(a) + #for b in beta_s: v *= ctx.rgamma(b) + #for w, c in zip(w_s, c_s): v *= ctx.power(w, c) + v = ctx.fprod([ctx.hyper(a_s, b_s, z, **kwargs)] + \ + [ctx.gamma(a) for a in alpha_s] + \ + [ctx.rgamma(b) for b in beta_s] + \ + [ctx.power(w,c) for (w,c) in zip(w_s,c_s)]) + if verbose: + print(" Value:", v) + evaluated_terms.append(v) + + if len(terms) == 1 and (not perturb): + sumvalue = evaluated_terms[0] + break + + if ctx._fixed_precision: + sumvalue = ctx.fsum(evaluated_terms) + break + + sumvalue = ctx.fsum(evaluated_terms) + term_magnitudes = [ctx.mag(x) for x in evaluated_terms] + max_magnitude = max(term_magnitudes) + sum_magnitude = ctx.mag(sumvalue) + cancellation = max_magnitude - sum_magnitude + if verbose: + print() + print(" Cancellation:", cancellation, "bits") + print(" Increased precision:", ctx.prec - orig, "bits") + + precision_ok = cancellation < ctx.prec - orig + + if zeroprec is None: + zero_ok = False + else: + zero_ok = max_magnitude - ctx.prec < -zeroprec + if infprec is None: + inf_ok = False + else: + inf_ok = max_magnitude > infprec + + if precision_ok and (not perturb) or ctx.isnan(cancellation): + break + elif precision_ok: + if perturbed_reference_value is None: + hextra += 20 + perturbed_reference_value = sumvalue + continue + elif ctx.mag(sumvalue - perturbed_reference_value) <= \ + ctx.mag(sumvalue) - orig: + break + elif zero_ok: + sumvalue = ctx.zero + break + elif inf_ok: + sumvalue = ctx.inf + break + elif 'hmag' in kwargs: + break + else: + hextra *= 2 + perturbed_reference_value = sumvalue + # Increase precision + else: + increment = min(max(cancellation, orig//2), max(extraprec,orig)) + ctx.prec += increment + if verbose: + print(" Must start over with increased precision") + continue + finally: + ctx.prec = orig + return +sumvalue + +@defun +def hyper(ctx, a_s, b_s, z, **kwargs): + """ + Hypergeometric function, general case. + """ + z = ctx.convert(z) + p = len(a_s) + q = len(b_s) + a_s = [ctx._convert_param(a) for a in a_s] + b_s = [ctx._convert_param(b) for b in b_s] + # Reduce degree by eliminating common parameters + if kwargs.get('eliminate', True): + elim_nonpositive = kwargs.get('eliminate_all', False) + i = 0 + while i < q and a_s: + b = b_s[i] + if b in a_s and (elim_nonpositive or not ctx.isnpint(b[0])): + a_s.remove(b) + b_s.remove(b) + p -= 1 + q -= 1 + else: + i += 1 + # Handle special cases + if p == 0: + if q == 1: return ctx._hyp0f1(b_s, z, **kwargs) + elif q == 0: return ctx.exp(z) + elif p == 1: + if q == 1: return ctx._hyp1f1(a_s, b_s, z, **kwargs) + elif q == 2: return ctx._hyp1f2(a_s, b_s, z, **kwargs) + elif q == 0: return ctx._hyp1f0(a_s[0][0], z) + elif p == 2: + if q == 1: return ctx._hyp2f1(a_s, b_s, z, **kwargs) + elif q == 2: return ctx._hyp2f2(a_s, b_s, z, **kwargs) + elif q == 3: return ctx._hyp2f3(a_s, b_s, z, **kwargs) + elif q == 0: return ctx._hyp2f0(a_s, b_s, z, **kwargs) + elif p == q+1: + return ctx._hypq1fq(p, q, a_s, b_s, z, **kwargs) + elif p > q+1 and not kwargs.get('force_series'): + return ctx._hyp_borel(p, q, a_s, b_s, z, **kwargs) + coeffs, types = zip(*(a_s+b_s)) + return ctx.hypsum(p, q, types, coeffs, z, **kwargs) + +@defun +def hyp0f1(ctx,b,z,**kwargs): + return ctx.hyper([],[b],z,**kwargs) + +@defun +def hyp1f1(ctx,a,b,z,**kwargs): + return ctx.hyper([a],[b],z,**kwargs) + +@defun +def hyp1f2(ctx,a1,b1,b2,z,**kwargs): + return ctx.hyper([a1],[b1,b2],z,**kwargs) + +@defun +def hyp2f1(ctx,a,b,c,z,**kwargs): + return ctx.hyper([a,b],[c],z,**kwargs) + +@defun +def hyp2f2(ctx,a1,a2,b1,b2,z,**kwargs): + return ctx.hyper([a1,a2],[b1,b2],z,**kwargs) + +@defun +def hyp2f3(ctx,a1,a2,b1,b2,b3,z,**kwargs): + return ctx.hyper([a1,a2],[b1,b2,b3],z,**kwargs) + +@defun +def hyp2f0(ctx,a,b,z,**kwargs): + return ctx.hyper([a,b],[],z,**kwargs) + +@defun +def hyp3f2(ctx,a1,a2,a3,b1,b2,z,**kwargs): + return ctx.hyper([a1,a2,a3],[b1,b2],z,**kwargs) + +@defun_wrapped +def _hyp1f0(ctx, a, z): + return (1-z) ** (-a) + +@defun +def _hyp0f1(ctx, b_s, z, **kwargs): + (b, btype), = b_s + if z: + magz = ctx.mag(z) + else: + magz = 0 + if magz >= 8 and not kwargs.get('force_series'): + try: + # http://functions.wolfram.com/HypergeometricFunctions/ + # Hypergeometric0F1/06/02/03/0004/ + # TODO: handle the all-real case more efficiently! + # TODO: figure out how much precision is needed (exponential growth) + orig = ctx.prec + try: + ctx.prec += 12 + magz//2 + def h(): + w = ctx.sqrt(-z) + jw = ctx.j*w + u = 1/(4*jw) + c = ctx.mpq_1_2 - b + E = ctx.exp(2*jw) + T1 = ([-jw,E], [c,-1], [], [], [b-ctx.mpq_1_2, ctx.mpq_3_2-b], [], -u) + T2 = ([jw,E], [c,1], [], [], [b-ctx.mpq_1_2, ctx.mpq_3_2-b], [], u) + return T1, T2 + v = ctx.hypercomb(h, [], force_series=True) + v = ctx.gamma(b)/(2*ctx.sqrt(ctx.pi))*v + finally: + ctx.prec = orig + if ctx._is_real_type(b) and ctx._is_real_type(z): + v = ctx._re(v) + return +v + except ctx.NoConvergence: + pass + return ctx.hypsum(0, 1, (btype,), [b], z, **kwargs) + +@defun +def _hyp1f1(ctx, a_s, b_s, z, **kwargs): + (a, atype), = a_s + (b, btype), = b_s + if not z: + return ctx.one+z + magz = ctx.mag(z) + if magz >= 7 and not (ctx.isint(a) and ctx.re(a) <= 0): + if ctx.isinf(z): + if ctx.sign(a) == ctx.sign(b) == ctx.sign(z) == 1: + return ctx.inf + return ctx.nan * z + try: + try: + ctx.prec += magz + sector = ctx._im(z) < 0 + def h(a,b): + if sector: + E = ctx.expjpi(ctx.fneg(a, exact=True)) + else: + E = ctx.expjpi(a) + rz = 1/z + T1 = ([E,z], [1,-a], [b], [b-a], [a, 1+a-b], [], -rz) + T2 = ([ctx.exp(z),z], [1,a-b], [b], [a], [b-a, 1-a], [], rz) + return T1, T2 + v = ctx.hypercomb(h, [a,b], force_series=True) + if ctx._is_real_type(a) and ctx._is_real_type(b) and ctx._is_real_type(z): + v = ctx._re(v) + return +v + except ctx.NoConvergence: + pass + finally: + ctx.prec -= magz + v = ctx.hypsum(1, 1, (atype, btype), [a, b], z, **kwargs) + return v + +def _hyp2f1_gosper(ctx,a,b,c,z,**kwargs): + # Use Gosper's recurrence + # See http://www.math.utexas.edu/pipermail/maxima/2006/000126.html + _a,_b,_c,_z = a, b, c, z + orig = ctx.prec + maxprec = kwargs.get('maxprec', 100*orig) + extra = 10 + while 1: + ctx.prec = orig + extra + #a = ctx.convert(_a) + #b = ctx.convert(_b) + #c = ctx.convert(_c) + z = ctx.convert(_z) + d = ctx.mpf(0) + e = ctx.mpf(1) + f = ctx.mpf(0) + k = 0 + # Common subexpression elimination, unfortunately making + # things a bit unreadable. The formula is quite messy to begin + # with, though... + abz = a*b*z + ch = c * ctx.mpq_1_2 + c1h = (c+1) * ctx.mpq_1_2 + nz = 1-z + g = z/nz + abg = a*b*g + cba = c-b-a + z2 = z-2 + tol = -ctx.prec - 10 + nstr = ctx.nstr + nprint = ctx.nprint + mag = ctx.mag + maxmag = ctx.ninf + while 1: + kch = k+ch + kakbz = (k+a)*(k+b)*z / (4*(k+1)*kch*(k+c1h)) + d1 = kakbz*(e-(k+cba)*d*g) + e1 = kakbz*(d*abg+(k+c)*e) + ft = d*(k*(cba*z+k*z2-c)-abz)/(2*kch*nz) + f1 = f + e - ft + maxmag = max(maxmag, mag(f1)) + if mag(f1-f) < tol: + break + d, e, f = d1, e1, f1 + k += 1 + cancellation = maxmag - mag(f1) + if cancellation < extra: + break + else: + extra += cancellation + if extra > maxprec: + raise ctx.NoConvergence + return f1 + +@defun +def _hyp2f1(ctx, a_s, b_s, z, **kwargs): + (a, atype), (b, btype) = a_s + (c, ctype), = b_s + if z == 1: + # TODO: the following logic can be simplified + convergent = ctx.re(c-a-b) > 0 + finite = (ctx.isint(a) and a <= 0) or (ctx.isint(b) and b <= 0) + zerodiv = ctx.isint(c) and c <= 0 and not \ + ((ctx.isint(a) and c <= a <= 0) or (ctx.isint(b) and c <= b <= 0)) + #print "bz", a, b, c, z, convergent, finite, zerodiv + # Gauss's theorem gives the value if convergent + if (convergent or finite) and not zerodiv: + return ctx.gammaprod([c, c-a-b], [c-a, c-b], _infsign=True) + # Otherwise, there is a pole and we take the + # sign to be that when approaching from below + # XXX: this evaluation is not necessarily correct in all cases + return ctx.hyp2f1(a,b,c,1-ctx.eps*2) * ctx.inf + + # Equal to 1 (first term), unless there is a subsequent + # division by zero + if not z: + # Division by zero but power of z is higher than + # first order so cancels + if c or a == 0 or b == 0: + return 1+z + # Indeterminate + return ctx.nan + + # Hit zero denominator unless numerator goes to 0 first + if ctx.isint(c) and c <= 0: + if (ctx.isint(a) and c <= a <= 0) or \ + (ctx.isint(b) and c <= b <= 0): + pass + else: + # Pole in series + return ctx.inf + + absz = abs(z) + + # Fast case: standard series converges rapidly, + # possibly in finitely many terms + if absz <= 0.8 or (ctx.isint(a) and a <= 0 and a >= -1000) or \ + (ctx.isint(b) and b <= 0 and b >= -1000): + return ctx.hypsum(2, 1, (atype, btype, ctype), [a, b, c], z, **kwargs) + + orig = ctx.prec + try: + ctx.prec += 10 + + # Use 1/z transformation + if absz >= 1.3: + def h(a,b): + t = ctx.mpq_1-c; ab = a-b; rz = 1/z + T1 = ([-z],[-a], [c,-ab],[b,c-a], [a,t+a],[ctx.mpq_1+ab], rz) + T2 = ([-z],[-b], [c,ab],[a,c-b], [b,t+b],[ctx.mpq_1-ab], rz) + return T1, T2 + v = ctx.hypercomb(h, [a,b], **kwargs) + + # Use 1-z transformation + elif abs(1-z) <= 0.75: + def h(a,b): + t = c-a-b; ca = c-a; cb = c-b; rz = 1-z + T1 = [], [], [c,t], [ca,cb], [a,b], [1-t], rz + T2 = [rz], [t], [c,a+b-c], [a,b], [ca,cb], [1+t], rz + return T1, T2 + v = ctx.hypercomb(h, [a,b], **kwargs) + + # Use z/(z-1) transformation + elif abs(z/(z-1)) <= 0.75: + v = ctx.hyp2f1(a, c-b, c, z/(z-1)) / (1-z)**a + + # Remaining part of unit circle + else: + v = _hyp2f1_gosper(ctx,a,b,c,z,**kwargs) + + finally: + ctx.prec = orig + return +v + +@defun +def _hypq1fq(ctx, p, q, a_s, b_s, z, **kwargs): + r""" + Evaluates 3F2, 4F3, 5F4, ... + """ + a_s, a_types = zip(*a_s) + b_s, b_types = zip(*b_s) + a_s = list(a_s) + b_s = list(b_s) + absz = abs(z) + ispoly = False + for a in a_s: + if ctx.isint(a) and a <= 0: + ispoly = True + break + # Direct summation + if absz < 1 or ispoly: + try: + return ctx.hypsum(p, q, a_types+b_types, a_s+b_s, z, **kwargs) + except ctx.NoConvergence: + if absz > 1.1 or ispoly: + raise + # Use expansion at |z-1| -> 0. + # Reference: Wolfgang Buhring, "Generalized Hypergeometric Functions at + # Unit Argument", Proc. Amer. Math. Soc., Vol. 114, No. 1 (Jan. 1992), + # pp.145-153 + # The current implementation has several problems: + # 1. We only implement it for 3F2. The expansion coefficients are + # given by extremely messy nested sums in the higher degree cases + # (see reference). Is efficient sequential generation of the coefficients + # possible in the > 3F2 case? + # 2. Although the series converges, it may do so slowly, so we need + # convergence acceleration. The acceleration implemented by + # nsum does not always help, so results returned are sometimes + # inaccurate! Can we do better? + # 3. We should check conditions for convergence, and possibly + # do a better job of cancelling out gamma poles if possible. + if z == 1: + # XXX: should also check for division by zero in the + # denominator of the series (cf. hyp2f1) + S = ctx.re(sum(b_s)-sum(a_s)) + if S <= 0: + #return ctx.hyper(a_s, b_s, 1-ctx.eps*2, **kwargs) * ctx.inf + return ctx.hyper(a_s, b_s, 0.9, **kwargs) * ctx.inf + if (p,q) == (3,2) and abs(z-1) < 0.05: # and kwargs.get('sum1') + #print "Using alternate summation (experimental)" + a1,a2,a3 = a_s + b1,b2 = b_s + u = b1+b2-a3 + initial = ctx.gammaprod([b2-a3,b1-a3,a1,a2],[b2-a3,b1-a3,1,u]) + def term(k, _cache={0:initial}): + u = b1+b2-a3+k + if k in _cache: + t = _cache[k] + else: + t = _cache[k-1] + t *= (b1+k-a3-1)*(b2+k-a3-1) + t /= k*(u-1) + _cache[k] = t + return t * ctx.hyp2f1(a1,a2,u,z) + try: + S = ctx.nsum(term, [0,ctx.inf], verbose=kwargs.get('verbose'), + strict=kwargs.get('strict', True)) + return S * ctx.gammaprod([b1,b2],[a1,a2,a3]) + except ctx.NoConvergence: + pass + # Try to use convergence acceleration on and close to the unit circle. + # Problem: the convergence acceleration degenerates as |z-1| -> 0, + # except for special cases. Everywhere else, the Shanks transformation + # is very efficient. + if absz < 1.1 and ctx._re(z) <= 1: + + def term(kk, _cache={0:ctx.one}): + k = int(kk) + if k != kk: + t = z ** ctx.mpf(kk) / ctx.fac(kk) + for a in a_s: t *= ctx.rf(a,kk) + for b in b_s: t /= ctx.rf(b,kk) + return t + if k in _cache: + return _cache[k] + t = term(k-1) + m = k-1 + for j in xrange(p): t *= (a_s[j]+m) + for j in xrange(q): t /= (b_s[j]+m) + t *= z + t /= k + _cache[k] = t + return t + + sum_method = kwargs.get('sum_method', 'r+s+e') + + try: + return ctx.nsum(term, [0,ctx.inf], verbose=kwargs.get('verbose'), + strict=kwargs.get('strict', True), + method=sum_method.replace('e','')) + except ctx.NoConvergence: + if 'e' not in sum_method: + raise + pass + + if kwargs.get('verbose'): + print("Attempting Euler-Maclaurin summation") + + + """ + Somewhat slower version (one diffs_exp for each factor). + However, this would be faster with fast direct derivatives + of the gamma function. + + def power_diffs(k0): + r = 0 + l = ctx.log(z) + while 1: + yield z**ctx.mpf(k0) * l**r + r += 1 + + def loggamma_diffs(x, reciprocal=False): + sign = (-1) ** reciprocal + yield sign * ctx.loggamma(x) + i = 0 + while 1: + yield sign * ctx.psi(i,x) + i += 1 + + def hyper_diffs(k0): + b2 = b_s + [1] + A = [ctx.diffs_exp(loggamma_diffs(a+k0)) for a in a_s] + B = [ctx.diffs_exp(loggamma_diffs(b+k0,True)) for b in b2] + Z = [power_diffs(k0)] + C = ctx.gammaprod([b for b in b2], [a for a in a_s]) + for d in ctx.diffs_prod(A + B + Z): + v = C * d + yield v + """ + + def log_diffs(k0): + b2 = b_s + [1] + yield sum(ctx.loggamma(a+k0) for a in a_s) - \ + sum(ctx.loggamma(b+k0) for b in b2) + k0*ctx.log(z) + i = 0 + while 1: + v = sum(ctx.psi(i,a+k0) for a in a_s) - \ + sum(ctx.psi(i,b+k0) for b in b2) + if i == 0: + v += ctx.log(z) + yield v + i += 1 + + def hyper_diffs(k0): + C = ctx.gammaprod([b for b in b_s], [a for a in a_s]) + for d in ctx.diffs_exp(log_diffs(k0)): + v = C * d + yield v + + tol = ctx.eps / 1024 + prec = ctx.prec + try: + trunc = 50 * ctx.dps + ctx.prec += 20 + for i in xrange(5): + head = ctx.fsum(term(k) for k in xrange(trunc)) + tail, err = ctx.sumem(term, [trunc, ctx.inf], tol=tol, + adiffs=hyper_diffs(trunc), + verbose=kwargs.get('verbose'), + error=True, + _fast_abort=True) + if err < tol: + v = head + tail + break + trunc *= 2 + # Need to increase precision because calculation of + # derivatives may be inaccurate + ctx.prec += ctx.prec//2 + if i == 4: + raise ctx.NoConvergence(\ + "Euler-Maclaurin summation did not converge") + finally: + ctx.prec = prec + return +v + + # Use 1/z transformation + # http://functions.wolfram.com/HypergeometricFunctions/ + # HypergeometricPFQ/06/01/05/02/0004/ + def h(*args): + a_s = list(args[:p]) + b_s = list(args[p:]) + Ts = [] + recz = ctx.one/z + negz = ctx.fneg(z, exact=True) + for k in range(q+1): + ak = a_s[k] + C = [negz] + Cp = [-ak] + Gn = b_s + [ak] + [a_s[j]-ak for j in range(q+1) if j != k] + Gd = a_s + [b_s[j]-ak for j in range(q)] + Fn = [ak] + [ak-b_s[j]+1 for j in range(q)] + Fd = [1-a_s[j]+ak for j in range(q+1) if j != k] + Ts.append((C, Cp, Gn, Gd, Fn, Fd, recz)) + return Ts + return ctx.hypercomb(h, a_s+b_s, **kwargs) + +@defun +def _hyp_borel(ctx, p, q, a_s, b_s, z, **kwargs): + if a_s: + a_s, a_types = zip(*a_s) + a_s = list(a_s) + else: + a_s, a_types = [], () + if b_s: + b_s, b_types = zip(*b_s) + b_s = list(b_s) + else: + b_s, b_types = [], () + kwargs['maxterms'] = kwargs.get('maxterms', ctx.prec) + try: + return ctx.hypsum(p, q, a_types+b_types, a_s+b_s, z, **kwargs) + except ctx.NoConvergence: + pass + prec = ctx.prec + try: + tol = kwargs.get('asymp_tol', ctx.eps/4) + ctx.prec += 10 + # hypsum is has a conservative tolerance. So we try again: + def term(k, cache={0:ctx.one}): + if k in cache: + return cache[k] + t = term(k-1) + for a in a_s: t *= (a+(k-1)) + for b in b_s: t /= (b+(k-1)) + t *= z + t /= k + cache[k] = t + return t + s = ctx.one + for k in xrange(1, ctx.prec): + t = term(k) + s += t + if abs(t) <= tol: + return s + finally: + ctx.prec = prec + if p <= q+3: + contour = kwargs.get('contour') + if not contour: + if ctx.arg(z) < 0.25: + u = z / max(1, abs(z)) + if ctx.arg(z) >= 0: + contour = [0, 2j, (2j+2)/u, 2/u, ctx.inf] + else: + contour = [0, -2j, (-2j+2)/u, 2/u, ctx.inf] + #contour = [0, 2j/z, 2/z, ctx.inf] + #contour = [0, 2j, 2/z, ctx.inf] + #contour = [0, 2j, ctx.inf] + else: + contour = [0, ctx.inf] + quad_kwargs = kwargs.get('quad_kwargs', {}) + def g(t): + return ctx.exp(-t)*ctx.hyper(a_s, b_s+[1], t*z) + I, err = ctx.quad(g, contour, error=True, **quad_kwargs) + if err <= abs(I)*ctx.eps*8: + return I + raise ctx.NoConvergence + + +@defun +def _hyp2f2(ctx, a_s, b_s, z, **kwargs): + (a1, a1type), (a2, a2type) = a_s + (b1, b1type), (b2, b2type) = b_s + + absz = abs(z) + magz = ctx.mag(z) + orig = ctx.prec + + # Asymptotic expansion is ~ exp(z) + asymp_extraprec = magz + + # Asymptotic series is in terms of 3F1 + can_use_asymptotic = (not kwargs.get('force_series')) and \ + (ctx.mag(absz) > 3) + + # TODO: much of the following could be shared with 2F3 instead of + # copypasted + if can_use_asymptotic: + #print "using asymp" + try: + try: + ctx.prec += asymp_extraprec + # http://functions.wolfram.com/HypergeometricFunctions/ + # Hypergeometric2F2/06/02/02/0002/ + def h(a1,a2,b1,b2): + X = a1+a2-b1-b2 + A2 = a1+a2 + B2 = b1+b2 + c = {} + c[0] = ctx.one + c[1] = (A2-1)*X+b1*b2-a1*a2 + s1 = 0 + k = 0 + tprev = 0 + while 1: + if k not in c: + uu1 = 1-B2+2*a1+a1**2+2*a2+a2**2-A2*B2+a1*a2+b1*b2+(2*B2-3*(A2+1))*k+2*k**2 + uu2 = (k-A2+b1-1)*(k-A2+b2-1)*(k-X-2) + c[k] = ctx.one/k * (uu1*c[k-1]-uu2*c[k-2]) + t1 = c[k] * z**(-k) + if abs(t1) < 0.1*ctx.eps: + #print "Convergence :)" + break + # Quit if the series doesn't converge quickly enough + if k > 5 and abs(tprev) / abs(t1) < 1.5: + #print "No convergence :(" + raise ctx.NoConvergence + s1 += t1 + tprev = t1 + k += 1 + S = ctx.exp(z)*s1 + T1 = [z,S], [X,1], [b1,b2],[a1,a2],[],[],0 + T2 = [-z],[-a1],[b1,b2,a2-a1],[a2,b1-a1,b2-a1],[a1,a1-b1+1,a1-b2+1],[a1-a2+1],-1/z + T3 = [-z],[-a2],[b1,b2,a1-a2],[a1,b1-a2,b2-a2],[a2,a2-b1+1,a2-b2+1],[-a1+a2+1],-1/z + return T1, T2, T3 + v = ctx.hypercomb(h, [a1,a2,b1,b2], force_series=True, maxterms=4*ctx.prec) + if sum(ctx._is_real_type(u) for u in [a1,a2,b1,b2,z]) == 5: + v = ctx.re(v) + return v + except ctx.NoConvergence: + pass + finally: + ctx.prec = orig + + return ctx.hypsum(2, 2, (a1type, a2type, b1type, b2type), [a1, a2, b1, b2], z, **kwargs) + + + +@defun +def _hyp1f2(ctx, a_s, b_s, z, **kwargs): + (a1, a1type), = a_s + (b1, b1type), (b2, b2type) = b_s + + absz = abs(z) + magz = ctx.mag(z) + orig = ctx.prec + + # Asymptotic expansion is ~ exp(sqrt(z)) + asymp_extraprec = z and magz//2 + + # Asymptotic series is in terms of 3F0 + can_use_asymptotic = (not kwargs.get('force_series')) and \ + (ctx.mag(absz) > 19) and \ + (ctx.sqrt(absz) > 1.5*orig) # and \ + # ctx._hyp_check_convergence([a1, a1-b1+1, a1-b2+1], [], + # 1/absz, orig+40+asymp_extraprec) + + # TODO: much of the following could be shared with 2F3 instead of + # copypasted + if can_use_asymptotic: + #print "using asymp" + try: + try: + ctx.prec += asymp_extraprec + # http://functions.wolfram.com/HypergeometricFunctions/ + # Hypergeometric1F2/06/02/03/ + def h(a1,b1,b2): + X = ctx.mpq_1_2*(a1-b1-b2+ctx.mpq_1_2) + c = {} + c[0] = ctx.one + c[1] = 2*(ctx.mpq_1_4*(3*a1+b1+b2-2)*(a1-b1-b2)+b1*b2-ctx.mpq_3_16) + c[2] = 2*(b1*b2+ctx.mpq_1_4*(a1-b1-b2)*(3*a1+b1+b2-2)-ctx.mpq_3_16)**2+\ + ctx.mpq_1_16*(-16*(2*a1-3)*b1*b2 + \ + 4*(a1-b1-b2)*(-8*a1**2+11*a1+b1+b2-2)-3) + s1 = 0 + s2 = 0 + k = 0 + tprev = 0 + while 1: + if k not in c: + uu1 = (3*k**2+(-6*a1+2*b1+2*b2-4)*k + 3*a1**2 - \ + (b1-b2)**2 - 2*a1*(b1+b2-2) + ctx.mpq_1_4) + uu2 = (k-a1+b1-b2-ctx.mpq_1_2)*(k-a1-b1+b2-ctx.mpq_1_2)*\ + (k-a1+b1+b2-ctx.mpq_5_2) + c[k] = ctx.one/(2*k)*(uu1*c[k-1]-uu2*c[k-2]) + w = c[k] * (-z)**(-0.5*k) + t1 = (-ctx.j)**k * ctx.mpf(2)**(-k) * w + t2 = ctx.j**k * ctx.mpf(2)**(-k) * w + if abs(t1) < 0.1*ctx.eps: + #print "Convergence :)" + break + # Quit if the series doesn't converge quickly enough + if k > 5 and abs(tprev) / abs(t1) < 1.5: + #print "No convergence :(" + raise ctx.NoConvergence + s1 += t1 + s2 += t2 + tprev = t1 + k += 1 + S = ctx.expj(ctx.pi*X+2*ctx.sqrt(-z))*s1 + \ + ctx.expj(-(ctx.pi*X+2*ctx.sqrt(-z)))*s2 + T1 = [0.5*S, ctx.pi, -z], [1, -0.5, X], [b1, b2], [a1],\ + [], [], 0 + T2 = [-z], [-a1], [b1,b2],[b1-a1,b2-a1], \ + [a1,a1-b1+1,a1-b2+1], [], 1/z + return T1, T2 + v = ctx.hypercomb(h, [a1,b1,b2], force_series=True, maxterms=4*ctx.prec) + if sum(ctx._is_real_type(u) for u in [a1,b1,b2,z]) == 4: + v = ctx.re(v) + return v + except ctx.NoConvergence: + pass + finally: + ctx.prec = orig + + #print "not using asymp" + return ctx.hypsum(1, 2, (a1type, b1type, b2type), [a1, b1, b2], z, **kwargs) + + + +@defun +def _hyp2f3(ctx, a_s, b_s, z, **kwargs): + (a1, a1type), (a2, a2type) = a_s + (b1, b1type), (b2, b2type), (b3, b3type) = b_s + + absz = abs(z) + magz = ctx.mag(z) + + # Asymptotic expansion is ~ exp(sqrt(z)) + asymp_extraprec = z and magz//2 + orig = ctx.prec + + # Asymptotic series is in terms of 4F1 + # The square root below empirically provides a plausible criterion + # for the leading series to converge + can_use_asymptotic = (not kwargs.get('force_series')) and \ + (ctx.mag(absz) > 19) and (ctx.sqrt(absz) > 1.5*orig) + + if can_use_asymptotic: + #print "using asymp" + try: + try: + ctx.prec += asymp_extraprec + # http://functions.wolfram.com/HypergeometricFunctions/ + # Hypergeometric2F3/06/02/03/01/0002/ + def h(a1,a2,b1,b2,b3): + X = ctx.mpq_1_2*(a1+a2-b1-b2-b3+ctx.mpq_1_2) + A2 = a1+a2 + B3 = b1+b2+b3 + A = a1*a2 + B = b1*b2+b3*b2+b1*b3 + R = b1*b2*b3 + c = {} + c[0] = ctx.one + c[1] = 2*(B - A + ctx.mpq_1_4*(3*A2+B3-2)*(A2-B3) - ctx.mpq_3_16) + c[2] = ctx.mpq_1_2*c[1]**2 + ctx.mpq_1_16*(-16*(2*A2-3)*(B-A) + 32*R +\ + 4*(-8*A2**2 + 11*A2 + 8*A + B3 - 2)*(A2-B3)-3) + s1 = 0 + s2 = 0 + k = 0 + tprev = 0 + while 1: + if k not in c: + uu1 = (k-2*X-3)*(k-2*X-2*b1-1)*(k-2*X-2*b2-1)*\ + (k-2*X-2*b3-1) + uu2 = (4*(k-1)**3 - 6*(4*X+B3)*(k-1)**2 + \ + 2*(24*X**2+12*B3*X+4*B+B3-1)*(k-1) - 32*X**3 - \ + 24*B3*X**2 - 4*B - 8*R - 4*(4*B+B3-1)*X + 2*B3-1) + uu3 = (5*(k-1)**2+2*(-10*X+A2-3*B3+3)*(k-1)+2*c[1]) + c[k] = ctx.one/(2*k)*(uu1*c[k-3]-uu2*c[k-2]+uu3*c[k-1]) + w = c[k] * ctx.power(-z, -0.5*k) + t1 = (-ctx.j)**k * ctx.mpf(2)**(-k) * w + t2 = ctx.j**k * ctx.mpf(2)**(-k) * w + if abs(t1) < 0.1*ctx.eps: + break + # Quit if the series doesn't converge quickly enough + if k > 5 and abs(tprev) / abs(t1) < 1.5: + raise ctx.NoConvergence + s1 += t1 + s2 += t2 + tprev = t1 + k += 1 + S = ctx.expj(ctx.pi*X+2*ctx.sqrt(-z))*s1 + \ + ctx.expj(-(ctx.pi*X+2*ctx.sqrt(-z)))*s2 + T1 = [0.5*S, ctx.pi, -z], [1, -0.5, X], [b1, b2, b3], [a1, a2],\ + [], [], 0 + T2 = [-z], [-a1], [b1,b2,b3,a2-a1],[a2,b1-a1,b2-a1,b3-a1], \ + [a1,a1-b1+1,a1-b2+1,a1-b3+1], [a1-a2+1], 1/z + T3 = [-z], [-a2], [b1,b2,b3,a1-a2],[a1,b1-a2,b2-a2,b3-a2], \ + [a2,a2-b1+1,a2-b2+1,a2-b3+1],[-a1+a2+1], 1/z + return T1, T2, T3 + v = ctx.hypercomb(h, [a1,a2,b1,b2,b3], force_series=True, maxterms=4*ctx.prec) + if sum(ctx._is_real_type(u) for u in [a1,a2,b1,b2,b3,z]) == 6: + v = ctx.re(v) + return v + except ctx.NoConvergence: + pass + finally: + ctx.prec = orig + + return ctx.hypsum(2, 3, (a1type, a2type, b1type, b2type, b3type), [a1, a2, b1, b2, b3], z, **kwargs) + +@defun +def _hyp2f0(ctx, a_s, b_s, z, **kwargs): + (a, atype), (b, btype) = a_s + # We want to try aggressively to use the asymptotic expansion, + # and fall back only when absolutely necessary + try: + kwargsb = kwargs.copy() + kwargsb['maxterms'] = kwargsb.get('maxterms', ctx.prec) + return ctx.hypsum(2, 0, (atype,btype), [a,b], z, **kwargsb) + except ctx.NoConvergence: + if kwargs.get('force_series'): + raise + pass + def h(a, b): + w = ctx.sinpi(b) + rz = -1/z + T1 = ([ctx.pi,w,rz],[1,-1,a],[],[a-b+1,b],[a],[b],rz) + T2 = ([-ctx.pi,w,rz],[1,-1,1+a-b],[],[a,2-b],[a-b+1],[2-b],rz) + return T1, T2 + return ctx.hypercomb(h, [a, 1+a-b], **kwargs) + +@defun +def meijerg(ctx, a_s, b_s, z, r=1, series=None, **kwargs): + an, ap = a_s + bm, bq = b_s + n = len(an) + p = n + len(ap) + m = len(bm) + q = m + len(bq) + a = an+ap + b = bm+bq + a = [ctx.convert(_) for _ in a] + b = [ctx.convert(_) for _ in b] + z = ctx.convert(z) + if series is None: + if p < q: series = 1 + if p > q: series = 2 + if p == q: + if m+n == p and abs(z) > 1: + series = 2 + else: + series = 1 + if kwargs.get('verbose'): + print("Meijer G m,n,p,q,series =", m,n,p,q,series) + if series == 1: + def h(*args): + a = args[:p] + b = args[p:] + terms = [] + for k in range(m): + bases = [z] + expts = [b[k]/r] + gn = [b[j]-b[k] for j in range(m) if j != k] + gn += [1-a[j]+b[k] for j in range(n)] + gd = [a[j]-b[k] for j in range(n,p)] + gd += [1-b[j]+b[k] for j in range(m,q)] + hn = [1-a[j]+b[k] for j in range(p)] + hd = [1-b[j]+b[k] for j in range(q) if j != k] + hz = (-ctx.one)**(p-m-n) * z**(ctx.one/r) + terms.append((bases, expts, gn, gd, hn, hd, hz)) + return terms + else: + def h(*args): + a = args[:p] + b = args[p:] + terms = [] + for k in range(n): + bases = [z] + if r == 1: + expts = [a[k]-1] + else: + expts = [(a[k]-1)/ctx.convert(r)] + gn = [a[k]-a[j] for j in range(n) if j != k] + gn += [1-a[k]+b[j] for j in range(m)] + gd = [a[k]-b[j] for j in range(m,q)] + gd += [1-a[k]+a[j] for j in range(n,p)] + hn = [1-a[k]+b[j] for j in range(q)] + hd = [1+a[j]-a[k] for j in range(p) if j != k] + hz = (-ctx.one)**(q-m-n) / z**(ctx.one/r) + terms.append((bases, expts, gn, gd, hn, hd, hz)) + return terms + return ctx.hypercomb(h, a+b, **kwargs) + +@defun_wrapped +def appellf1(ctx,a,b1,b2,c,x,y,**kwargs): + # Assume x smaller + # We will use x for the outer loop + if abs(x) > abs(y): + x, y = y, x + b1, b2 = b2, b1 + def ok(x): + return abs(x) < 0.99 + # Finite cases + if ctx.isnpint(a): + pass + elif ctx.isnpint(b1): + pass + elif ctx.isnpint(b2): + x, y, b1, b2 = y, x, b2, b1 + else: + #print x, y + # Note: ok if |y| > 1, because + # 2F1 implements analytic continuation + if not ok(x): + u1 = (x-y)/(x-1) + if not ok(u1): + raise ValueError("Analytic continuation not implemented") + #print "Using analytic continuation" + return (1-x)**(-b1)*(1-y)**(c-a-b2)*\ + ctx.appellf1(c-a,b1,c-b1-b2,c,u1,y,**kwargs) + return ctx.hyper2d({'m+n':[a],'m':[b1],'n':[b2]}, {'m+n':[c]}, x,y, **kwargs) + +@defun +def appellf2(ctx,a,b1,b2,c1,c2,x,y,**kwargs): + # TODO: continuation + return ctx.hyper2d({'m+n':[a],'m':[b1],'n':[b2]}, + {'m':[c1],'n':[c2]}, x,y, **kwargs) + +@defun +def appellf3(ctx,a1,a2,b1,b2,c,x,y,**kwargs): + outer_polynomial = ctx.isnpint(a1) or ctx.isnpint(b1) + inner_polynomial = ctx.isnpint(a2) or ctx.isnpint(b2) + if not outer_polynomial: + if inner_polynomial or abs(x) > abs(y): + x, y = y, x + a1,a2,b1,b2 = a2,a1,b2,b1 + return ctx.hyper2d({'m':[a1,b1],'n':[a2,b2]}, {'m+n':[c]},x,y,**kwargs) + +@defun +def appellf4(ctx,a,b,c1,c2,x,y,**kwargs): + # TODO: continuation + return ctx.hyper2d({'m+n':[a,b]}, {'m':[c1],'n':[c2]},x,y,**kwargs) + +@defun +def hyper2d(ctx, a, b, x, y, **kwargs): + r""" + Sums the generalized 2D hypergeometric series + + .. math :: + + \sum_{m=0}^{\infty} \sum_{n=0}^{\infty} + \frac{P((a),m,n)}{Q((b),m,n)} + \frac{x^m y^n} {m! n!} + + where `(a) = (a_1,\ldots,a_r)`, `(b) = (b_1,\ldots,b_s)` and where + `P` and `Q` are products of rising factorials such as `(a_j)_n` or + `(a_j)_{m+n}`. `P` and `Q` are specified in the form of dicts, with + the `m` and `n` dependence as keys and parameter lists as values. + The supported rising factorials are given in the following table + (note that only a few are supported in `Q`): + + +------------+-------------------+--------+ + | Key | Rising factorial | `Q` | + +============+===================+========+ + | ``'m'`` | `(a_j)_m` | Yes | + +------------+-------------------+--------+ + | ``'n'`` | `(a_j)_n` | Yes | + +------------+-------------------+--------+ + | ``'m+n'`` | `(a_j)_{m+n}` | Yes | + +------------+-------------------+--------+ + | ``'m-n'`` | `(a_j)_{m-n}` | No | + +------------+-------------------+--------+ + | ``'n-m'`` | `(a_j)_{n-m}` | No | + +------------+-------------------+--------+ + | ``'2m+n'`` | `(a_j)_{2m+n}` | No | + +------------+-------------------+--------+ + | ``'2m-n'`` | `(a_j)_{2m-n}` | No | + +------------+-------------------+--------+ + | ``'2n-m'`` | `(a_j)_{2n-m}` | No | + +------------+-------------------+--------+ + + For example, the Appell F1 and F4 functions + + .. math :: + + F_1 = \sum_{m=0}^{\infty} \sum_{n=0}^{\infty} + \frac{(a)_{m+n} (b)_m (c)_n}{(d)_{m+n}} + \frac{x^m y^n}{m! n!} + + F_4 = \sum_{m=0}^{\infty} \sum_{n=0}^{\infty} + \frac{(a)_{m+n} (b)_{m+n}}{(c)_m (d)_{n}} + \frac{x^m y^n}{m! n!} + + can be represented respectively as + + ``hyper2d({'m+n':[a], 'm':[b], 'n':[c]}, {'m+n':[d]}, x, y)`` + + ``hyper2d({'m+n':[a,b]}, {'m':[c], 'n':[d]}, x, y)`` + + More generally, :func:`~mpmath.hyper2d` can evaluate any of the 34 distinct + convergent second-order (generalized Gaussian) hypergeometric + series enumerated by Horn, as well as the Kampe de Feriet + function. + + The series is computed by rewriting it so that the inner + series (i.e. the series containing `n` and `y`) has the form of an + ordinary generalized hypergeometric series and thereby can be + evaluated efficiently using :func:`~mpmath.hyper`. If possible, + manually swapping `x` and `y` and the corresponding parameters + can sometimes give better results. + + **Examples** + + Two separable cases: a product of two geometric series, and a + product of two Gaussian hypergeometric functions:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> x, y = mpf(0.25), mpf(0.5) + >>> hyper2d({'m':1,'n':1}, {}, x,y) + 2.666666666666666666666667 + >>> 1/(1-x)/(1-y) + 2.666666666666666666666667 + >>> hyper2d({'m':[1,2],'n':[3,4]}, {'m':[5],'n':[6]}, x,y) + 4.164358531238938319669856 + >>> hyp2f1(1,2,5,x)*hyp2f1(3,4,6,y) + 4.164358531238938319669856 + + Some more series that can be done in closed form:: + + >>> hyper2d({'m':1,'n':1},{'m+n':1},x,y) + 2.013417124712514809623881 + >>> (exp(x)*x-exp(y)*y)/(x-y) + 2.013417124712514809623881 + + Six of the 34 Horn functions, G1-G3 and H1-H3:: + + >>> from mpmath import * + >>> mp.dps = 10; mp.pretty = True + >>> x, y = 0.0625, 0.125 + >>> a1,a2,b1,b2,c1,c2,d = 1.1,-1.2,-1.3,-1.4,1.5,-1.6,1.7 + >>> hyper2d({'m+n':a1,'n-m':b1,'m-n':b2},{},x,y) # G1 + 1.139090746 + >>> nsum(lambda m,n: rf(a1,m+n)*rf(b1,n-m)*rf(b2,m-n)*\ + ... x**m*y**n/fac(m)/fac(n), [0,inf], [0,inf]) + 1.139090746 + >>> hyper2d({'m':a1,'n':a2,'n-m':b1,'m-n':b2},{},x,y) # G2 + 0.9503682696 + >>> nsum(lambda m,n: rf(a1,m)*rf(a2,n)*rf(b1,n-m)*rf(b2,m-n)*\ + ... x**m*y**n/fac(m)/fac(n), [0,inf], [0,inf]) + 0.9503682696 + >>> hyper2d({'2n-m':a1,'2m-n':a2},{},x,y) # G3 + 1.029372029 + >>> nsum(lambda m,n: rf(a1,2*n-m)*rf(a2,2*m-n)*\ + ... x**m*y**n/fac(m)/fac(n), [0,inf], [0,inf]) + 1.029372029 + >>> hyper2d({'m-n':a1,'m+n':b1,'n':c1},{'m':d},x,y) # H1 + -1.605331256 + >>> nsum(lambda m,n: rf(a1,m-n)*rf(b1,m+n)*rf(c1,n)/rf(d,m)*\ + ... x**m*y**n/fac(m)/fac(n), [0,inf], [0,inf]) + -1.605331256 + >>> hyper2d({'m-n':a1,'m':b1,'n':[c1,c2]},{'m':d},x,y) # H2 + -2.35405404 + >>> nsum(lambda m,n: rf(a1,m-n)*rf(b1,m)*rf(c1,n)*rf(c2,n)/rf(d,m)*\ + ... x**m*y**n/fac(m)/fac(n), [0,inf], [0,inf]) + -2.35405404 + >>> hyper2d({'2m+n':a1,'n':b1},{'m+n':c1},x,y) # H3 + 0.974479074 + >>> nsum(lambda m,n: rf(a1,2*m+n)*rf(b1,n)/rf(c1,m+n)*\ + ... x**m*y**n/fac(m)/fac(n), [0,inf], [0,inf]) + 0.974479074 + + **References** + + 1. [SrivastavaKarlsson]_ + 2. [Weisstein]_ http://mathworld.wolfram.com/HornFunction.html + 3. [Weisstein]_ http://mathworld.wolfram.com/AppellHypergeometricFunction.html + + """ + x = ctx.convert(x) + y = ctx.convert(y) + def parse(dct, key): + args = dct.pop(key, []) + try: + args = list(args) + except TypeError: + args = [args] + return [ctx.convert(arg) for arg in args] + a_s = dict(a) + b_s = dict(b) + a_m = parse(a, 'm') + a_n = parse(a, 'n') + a_m_add_n = parse(a, 'm+n') + a_m_sub_n = parse(a, 'm-n') + a_n_sub_m = parse(a, 'n-m') + a_2m_add_n = parse(a, '2m+n') + a_2m_sub_n = parse(a, '2m-n') + a_2n_sub_m = parse(a, '2n-m') + b_m = parse(b, 'm') + b_n = parse(b, 'n') + b_m_add_n = parse(b, 'm+n') + if a: raise ValueError("unsupported key: %r" % a.keys()[0]) + if b: raise ValueError("unsupported key: %r" % b.keys()[0]) + s = 0 + outer = ctx.one + m = ctx.mpf(0) + ok_count = 0 + prec = ctx.prec + maxterms = kwargs.get('maxterms', 20*prec) + try: + ctx.prec += 10 + tol = +ctx.eps + while 1: + inner_sign = 1 + outer_sign = 1 + inner_a = list(a_n) + inner_b = list(b_n) + outer_a = [a+m for a in a_m] + outer_b = [b+m for b in b_m] + # (a)_{m+n} = (a)_m (a+m)_n + for a in a_m_add_n: + a = a+m + inner_a.append(a) + outer_a.append(a) + # (b)_{m+n} = (b)_m (b+m)_n + for b in b_m_add_n: + b = b+m + inner_b.append(b) + outer_b.append(b) + # (a)_{n-m} = (a-m)_n / (a-m)_m + for a in a_n_sub_m: + inner_a.append(a-m) + outer_b.append(a-m-1) + # (a)_{m-n} = (-1)^(m+n) (1-a-m)_m / (1-a-m)_n + for a in a_m_sub_n: + inner_sign *= (-1) + outer_sign *= (-1)**(m) + inner_b.append(1-a-m) + outer_a.append(-a-m) + # (a)_{2m+n} = (a)_{2m} (a+2m)_n + for a in a_2m_add_n: + inner_a.append(a+2*m) + outer_a.append((a+2*m)*(1+a+2*m)) + # (a)_{2m-n} = (-1)^(2m+n) (1-a-2m)_{2m} / (1-a-2m)_n + for a in a_2m_sub_n: + inner_sign *= (-1) + inner_b.append(1-a-2*m) + outer_a.append((a+2*m)*(1+a+2*m)) + # (a)_{2n-m} = 4^n ((a-m)/2)_n ((a-m+1)/2)_n / (a-m)_m + for a in a_2n_sub_m: + inner_sign *= 4 + inner_a.append(0.5*(a-m)) + inner_a.append(0.5*(a-m+1)) + outer_b.append(a-m-1) + inner = ctx.hyper(inner_a, inner_b, inner_sign*y, + zeroprec=ctx.prec, **kwargs) + term = outer * inner * outer_sign + if abs(term) < tol: + ok_count += 1 + else: + ok_count = 0 + if ok_count >= 3 or not outer: + break + s += term + for a in outer_a: outer *= a + for b in outer_b: outer /= b + m += 1 + outer = outer * x / m + if m > maxterms: + raise ctx.NoConvergence("maxterms exceeded in hyper2d") + finally: + ctx.prec = prec + return +s + +""" +@defun +def kampe_de_feriet(ctx,a,b,c,d,e,f,x,y,**kwargs): + return ctx.hyper2d({'m+n':a,'m':b,'n':c}, + {'m+n':d,'m':e,'n':f}, x,y, **kwargs) +""" + +@defun +def bihyper(ctx, a_s, b_s, z, **kwargs): + r""" + Evaluates the bilateral hypergeometric series + + .. math :: + + \,_AH_B(a_1, \ldots, a_k; b_1, \ldots, b_B; z) = + \sum_{n=-\infty}^{\infty} + \frac{(a_1)_n \ldots (a_A)_n} + {(b_1)_n \ldots (b_B)_n} \, z^n + + where, for direct convergence, `A = B` and `|z| = 1`, although a + regularized sum exists more generally by considering the + bilateral series as a sum of two ordinary hypergeometric + functions. In order for the series to make sense, none of the + parameters may be integers. + + **Examples** + + The value of `\,_2H_2` at `z = 1` is given by Dougall's formula:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> a,b,c,d = 0.5, 1.5, 2.25, 3.25 + >>> bihyper([a,b],[c,d],1) + -14.49118026212345786148847 + >>> gammaprod([c,d,1-a,1-b,c+d-a-b-1],[c-a,d-a,c-b,d-b]) + -14.49118026212345786148847 + + The regularized function `\,_1H_0` can be expressed as the + sum of one `\,_2F_0` function and one `\,_1F_1` function:: + + >>> a = mpf(0.25) + >>> z = mpf(0.75) + >>> bihyper([a], [], z) + (0.2454393389657273841385582 + 0.2454393389657273841385582j) + >>> hyper([a,1],[],z) + (hyper([1],[1-a],-1/z)-1) + (0.2454393389657273841385582 + 0.2454393389657273841385582j) + >>> hyper([a,1],[],z) + hyper([1],[2-a],-1/z)/z/(a-1) + (0.2454393389657273841385582 + 0.2454393389657273841385582j) + + **References** + + 1. [Slater]_ (chapter 6: "Bilateral Series", pp. 180-189) + 2. [Wikipedia]_ http://en.wikipedia.org/wiki/Bilateral_hypergeometric_series + + """ + z = ctx.convert(z) + c_s = a_s + b_s + p = len(a_s) + q = len(b_s) + if (p, q) == (0,0) or (p, q) == (1,1): + return ctx.zero * z + neg = (p-q) % 2 + def h(*c_s): + a_s = list(c_s[:p]) + b_s = list(c_s[p:]) + aa_s = [2-b for b in b_s] + bb_s = [2-a for a in a_s] + rp = [(-1)**neg * z] + [1-b for b in b_s] + [1-a for a in a_s] + rc = [-1] + [1]*len(b_s) + [-1]*len(a_s) + T1 = [], [], [], [], a_s + [1], b_s, z + T2 = rp, rc, [], [], aa_s + [1], bb_s, (-1)**neg / z + return T1, T2 + return ctx.hypercomb(h, c_s, **kwargs) diff --git a/lib/python3.12/site-packages/mpmath/functions/orthogonal.py b/lib/python3.12/site-packages/mpmath/functions/orthogonal.py new file mode 100644 index 0000000000000000000000000000000000000000..aa33d8bd78290f55a970e78dab7a317d5f652dee --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/functions/orthogonal.py @@ -0,0 +1,493 @@ +from .functions import defun, defun_wrapped + +def _hermite_param(ctx, n, z, parabolic_cylinder): + """ + Combined calculation of the Hermite polynomial H_n(z) (and its + generalization to complex n) and the parabolic cylinder + function D. + """ + n, ntyp = ctx._convert_param(n) + z = ctx.convert(z) + q = -ctx.mpq_1_2 + # For re(z) > 0, 2F0 -- http://functions.wolfram.com/ + # HypergeometricFunctions/HermiteHGeneral/06/02/0009/ + # Otherwise, there is a reflection formula + # 2F0 + http://functions.wolfram.com/HypergeometricFunctions/ + # HermiteHGeneral/16/01/01/0006/ + # + # TODO: + # An alternative would be to use + # http://functions.wolfram.com/HypergeometricFunctions/ + # HermiteHGeneral/06/02/0006/ + # + # Also, the 1F1 expansion + # http://functions.wolfram.com/HypergeometricFunctions/ + # HermiteHGeneral/26/01/02/0001/ + # should probably be used for tiny z + if not z: + T1 = [2, ctx.pi], [n, 0.5], [], [q*(n-1)], [], [], 0 + if parabolic_cylinder: + T1[1][0] += q*n + return T1, + can_use_2f0 = ctx.isnpint(-n) or ctx.re(z) > 0 or \ + (ctx.re(z) == 0 and ctx.im(z) > 0) + expprec = ctx.prec*4 + 20 + if parabolic_cylinder: + u = ctx.fmul(ctx.fmul(z,z,prec=expprec), -0.25, exact=True) + w = ctx.fmul(z, ctx.sqrt(0.5,prec=expprec), prec=expprec) + else: + w = z + w2 = ctx.fmul(w, w, prec=expprec) + rw2 = ctx.fdiv(1, w2, prec=expprec) + nrw2 = ctx.fneg(rw2, exact=True) + nw = ctx.fneg(w, exact=True) + if can_use_2f0: + T1 = [2, w], [n, n], [], [], [q*n, q*(n-1)], [], nrw2 + terms = [T1] + else: + T1 = [2, nw], [n, n], [], [], [q*n, q*(n-1)], [], nrw2 + T2 = [2, ctx.pi, nw], [n+2, 0.5, 1], [], [q*n], [q*(n-1)], [1-q], w2 + terms = [T1,T2] + # Multiply by prefactor for D_n + if parabolic_cylinder: + expu = ctx.exp(u) + for i in range(len(terms)): + terms[i][1][0] += q*n + terms[i][0].append(expu) + terms[i][1].append(1) + return tuple(terms) + +@defun +def hermite(ctx, n, z, **kwargs): + return ctx.hypercomb(lambda: _hermite_param(ctx, n, z, 0), [], **kwargs) + +@defun +def pcfd(ctx, n, z, **kwargs): + r""" + Gives the parabolic cylinder function in Whittaker's notation + `D_n(z) = U(-n-1/2, z)` (see :func:`~mpmath.pcfu`). + It solves the differential equation + + .. math :: + + y'' + \left(n + \frac{1}{2} - \frac{1}{4} z^2\right) y = 0. + + and can be represented in terms of Hermite polynomials + (see :func:`~mpmath.hermite`) as + + .. math :: + + D_n(z) = 2^{-n/2} e^{-z^2/4} H_n\left(\frac{z}{\sqrt{2}}\right). + + **Plots** + + .. literalinclude :: /plots/pcfd.py + .. image :: /plots/pcfd.png + + **Examples** + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> pcfd(0,0); pcfd(1,0); pcfd(2,0); pcfd(3,0) + 1.0 + 0.0 + -1.0 + 0.0 + >>> pcfd(4,0); pcfd(-3,0) + 3.0 + 0.6266570686577501256039413 + >>> pcfd('1/2', 2+3j) + (-5.363331161232920734849056 - 3.858877821790010714163487j) + >>> pcfd(2, -10) + 1.374906442631438038871515e-9 + + Verifying the differential equation:: + + >>> n = mpf(2.5) + >>> y = lambda z: pcfd(n,z) + >>> z = 1.75 + >>> chop(diff(y,z,2) + (n+0.5-0.25*z**2)*y(z)) + 0.0 + + Rational Taylor series expansion when `n` is an integer:: + + >>> taylor(lambda z: pcfd(5,z), 0, 7) + [0.0, 15.0, 0.0, -13.75, 0.0, 3.96875, 0.0, -0.6015625] + + """ + return ctx.hypercomb(lambda: _hermite_param(ctx, n, z, 1), [], **kwargs) + +@defun +def pcfu(ctx, a, z, **kwargs): + r""" + Gives the parabolic cylinder function `U(a,z)`, which may be + defined for `\Re(z) > 0` in terms of the confluent + U-function (see :func:`~mpmath.hyperu`) by + + .. math :: + + U(a,z) = 2^{-\frac{1}{4}-\frac{a}{2}} e^{-\frac{1}{4} z^2} + U\left(\frac{a}{2}+\frac{1}{4}, + \frac{1}{2}, \frac{1}{2}z^2\right) + + or, for arbitrary `z`, + + .. math :: + + e^{-\frac{1}{4}z^2} U(a,z) = + U(a,0) \,_1F_1\left(-\tfrac{a}{2}+\tfrac{1}{4}; + \tfrac{1}{2}; -\tfrac{1}{2}z^2\right) + + U'(a,0) z \,_1F_1\left(-\tfrac{a}{2}+\tfrac{3}{4}; + \tfrac{3}{2}; -\tfrac{1}{2}z^2\right). + + **Examples** + + Connection to other functions:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> z = mpf(3) + >>> pcfu(0.5,z) + 0.03210358129311151450551963 + >>> sqrt(pi/2)*exp(z**2/4)*erfc(z/sqrt(2)) + 0.03210358129311151450551963 + >>> pcfu(0.5,-z) + 23.75012332835297233711255 + >>> sqrt(pi/2)*exp(z**2/4)*erfc(-z/sqrt(2)) + 23.75012332835297233711255 + >>> pcfu(0.5,-z) + 23.75012332835297233711255 + >>> sqrt(pi/2)*exp(z**2/4)*erfc(-z/sqrt(2)) + 23.75012332835297233711255 + + """ + n, _ = ctx._convert_param(a) + return ctx.pcfd(-n-ctx.mpq_1_2, z) + +@defun +def pcfv(ctx, a, z, **kwargs): + r""" + Gives the parabolic cylinder function `V(a,z)`, which can be + represented in terms of :func:`~mpmath.pcfu` as + + .. math :: + + V(a,z) = \frac{\Gamma(a+\tfrac{1}{2}) (U(a,-z)-\sin(\pi a) U(a,z)}{\pi}. + + **Examples** + + Wronskian relation between `U` and `V`:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> a, z = 2, 3 + >>> pcfu(a,z)*diff(pcfv,(a,z),(0,1))-diff(pcfu,(a,z),(0,1))*pcfv(a,z) + 0.7978845608028653558798921 + >>> sqrt(2/pi) + 0.7978845608028653558798921 + >>> a, z = 2.5, 3 + >>> pcfu(a,z)*diff(pcfv,(a,z),(0,1))-diff(pcfu,(a,z),(0,1))*pcfv(a,z) + 0.7978845608028653558798921 + >>> a, z = 0.25, -1 + >>> pcfu(a,z)*diff(pcfv,(a,z),(0,1))-diff(pcfu,(a,z),(0,1))*pcfv(a,z) + 0.7978845608028653558798921 + >>> a, z = 2+1j, 2+3j + >>> chop(pcfu(a,z)*diff(pcfv,(a,z),(0,1))-diff(pcfu,(a,z),(0,1))*pcfv(a,z)) + 0.7978845608028653558798921 + + """ + n, ntype = ctx._convert_param(a) + z = ctx.convert(z) + q = ctx.mpq_1_2 + r = ctx.mpq_1_4 + if ntype == 'Q' and ctx.isint(n*2): + # Faster for half-integers + def h(): + jz = ctx.fmul(z, -1j, exact=True) + T1terms = _hermite_param(ctx, -n-q, z, 1) + T2terms = _hermite_param(ctx, n-q, jz, 1) + for T in T1terms: + T[0].append(1j) + T[1].append(1) + T[3].append(q-n) + u = ctx.expjpi((q*n-r)) * ctx.sqrt(2/ctx.pi) + for T in T2terms: + T[0].append(u) + T[1].append(1) + return T1terms + T2terms + v = ctx.hypercomb(h, [], **kwargs) + if ctx._is_real_type(n) and ctx._is_real_type(z): + v = ctx._re(v) + return v + else: + def h(n): + w = ctx.square_exp_arg(z, -0.25) + u = ctx.square_exp_arg(z, 0.5) + e = ctx.exp(w) + l = [ctx.pi, q, ctx.exp(w)] + Y1 = l, [-q, n*q+r, 1], [r-q*n], [], [q*n+r], [q], u + Y2 = l + [z], [-q, n*q-r, 1, 1], [1-r-q*n], [], [q*n+1-r], [1+q], u + c, s = ctx.cospi_sinpi(r+q*n) + Y1[0].append(s) + Y2[0].append(c) + for Y in (Y1, Y2): + Y[1].append(1) + Y[3].append(q-n) + return Y1, Y2 + return ctx.hypercomb(h, [n], **kwargs) + + +@defun +def pcfw(ctx, a, z, **kwargs): + r""" + Gives the parabolic cylinder function `W(a,z)` defined in (DLMF 12.14). + + **Examples** + + Value at the origin:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> a = mpf(0.25) + >>> pcfw(a,0) + 0.9722833245718180765617104 + >>> power(2,-0.75)*sqrt(abs(gamma(0.25+0.5j*a)/gamma(0.75+0.5j*a))) + 0.9722833245718180765617104 + >>> diff(pcfw,(a,0),(0,1)) + -0.5142533944210078966003624 + >>> -power(2,-0.25)*sqrt(abs(gamma(0.75+0.5j*a)/gamma(0.25+0.5j*a))) + -0.5142533944210078966003624 + + """ + n, _ = ctx._convert_param(a) + z = ctx.convert(z) + def terms(): + phi2 = ctx.arg(ctx.gamma(0.5 + ctx.j*n)) + phi2 = (ctx.loggamma(0.5+ctx.j*n) - ctx.loggamma(0.5-ctx.j*n))/2j + rho = ctx.pi/8 + 0.5*phi2 + # XXX: cancellation computing k + k = ctx.sqrt(1 + ctx.exp(2*ctx.pi*n)) - ctx.exp(ctx.pi*n) + C = ctx.sqrt(k/2) * ctx.exp(0.25*ctx.pi*n) + yield C * ctx.expj(rho) * ctx.pcfu(ctx.j*n, z*ctx.expjpi(-0.25)) + yield C * ctx.expj(-rho) * ctx.pcfu(-ctx.j*n, z*ctx.expjpi(0.25)) + v = ctx.sum_accurately(terms) + if ctx._is_real_type(n) and ctx._is_real_type(z): + v = ctx._re(v) + return v + +""" +Even/odd PCFs. Useful? + +@defun +def pcfy1(ctx, a, z, **kwargs): + a, _ = ctx._convert_param(n) + z = ctx.convert(z) + def h(): + w = ctx.square_exp_arg(z) + w1 = ctx.fmul(w, -0.25, exact=True) + w2 = ctx.fmul(w, 0.5, exact=True) + e = ctx.exp(w1) + return [e], [1], [], [], [ctx.mpq_1_2*a+ctx.mpq_1_4], [ctx.mpq_1_2], w2 + return ctx.hypercomb(h, [], **kwargs) + +@defun +def pcfy2(ctx, a, z, **kwargs): + a, _ = ctx._convert_param(n) + z = ctx.convert(z) + def h(): + w = ctx.square_exp_arg(z) + w1 = ctx.fmul(w, -0.25, exact=True) + w2 = ctx.fmul(w, 0.5, exact=True) + e = ctx.exp(w1) + return [e, z], [1, 1], [], [], [ctx.mpq_1_2*a+ctx.mpq_3_4], \ + [ctx.mpq_3_2], w2 + return ctx.hypercomb(h, [], **kwargs) +""" + +@defun_wrapped +def gegenbauer(ctx, n, a, z, **kwargs): + # Special cases: a+0.5, a*2 poles + if ctx.isnpint(a): + return 0*(z+n) + if ctx.isnpint(a+0.5): + # TODO: something else is required here + # E.g.: gegenbauer(-2, -0.5, 3) == -12 + if ctx.isnpint(n+1): + raise NotImplementedError("Gegenbauer function with two limits") + def h(a): + a2 = 2*a + T = [], [], [n+a2], [n+1, a2], [-n, n+a2], [a+0.5], 0.5*(1-z) + return [T] + return ctx.hypercomb(h, [a], **kwargs) + def h(n): + a2 = 2*a + T = [], [], [n+a2], [n+1, a2], [-n, n+a2], [a+0.5], 0.5*(1-z) + return [T] + return ctx.hypercomb(h, [n], **kwargs) + +@defun_wrapped +def jacobi(ctx, n, a, b, x, **kwargs): + if not ctx.isnpint(a): + def h(n): + return (([], [], [a+n+1], [n+1, a+1], [-n, a+b+n+1], [a+1], (1-x)*0.5),) + return ctx.hypercomb(h, [n], **kwargs) + if not ctx.isint(b): + def h(n, a): + return (([], [], [-b], [n+1, -b-n], [-n, a+b+n+1], [b+1], (x+1)*0.5),) + return ctx.hypercomb(h, [n, a], **kwargs) + # XXX: determine appropriate limit + return ctx.binomial(n+a,n) * ctx.hyp2f1(-n,1+n+a+b,a+1,(1-x)/2, **kwargs) + +@defun_wrapped +def laguerre(ctx, n, a, z, **kwargs): + # XXX: limits, poles + #if ctx.isnpint(n): + # return 0*(a+z) + def h(a): + return (([], [], [a+n+1], [a+1, n+1], [-n], [a+1], z),) + return ctx.hypercomb(h, [a], **kwargs) + +@defun_wrapped +def legendre(ctx, n, x, **kwargs): + if ctx.isint(n): + n = int(n) + # Accuracy near zeros + if (n + (n < 0)) & 1: + if not x: + return x + mag = ctx.mag(x) + if mag < -2*ctx.prec-10: + return x + if mag < -5: + ctx.prec += -mag + return ctx.hyp2f1(-n,n+1,1,(1-x)/2, **kwargs) + +@defun +def legenp(ctx, n, m, z, type=2, **kwargs): + # Legendre function, 1st kind + n = ctx.convert(n) + m = ctx.convert(m) + # Faster + if not m: + return ctx.legendre(n, z, **kwargs) + # TODO: correct evaluation at singularities + if type == 2: + def h(n,m): + g = m*0.5 + T = [1+z, 1-z], [g, -g], [], [1-m], [-n, n+1], [1-m], 0.5*(1-z) + return (T,) + return ctx.hypercomb(h, [n,m], **kwargs) + if type == 3: + def h(n,m): + g = m*0.5 + T = [z+1, z-1], [g, -g], [], [1-m], [-n, n+1], [1-m], 0.5*(1-z) + return (T,) + return ctx.hypercomb(h, [n,m], **kwargs) + raise ValueError("requires type=2 or type=3") + +@defun +def legenq(ctx, n, m, z, type=2, **kwargs): + # Legendre function, 2nd kind + n = ctx.convert(n) + m = ctx.convert(m) + z = ctx.convert(z) + if z in (1, -1): + #if ctx.isint(m): + # return ctx.nan + #return ctx.inf # unsigned + return ctx.nan + if type == 2: + def h(n, m): + cos, sin = ctx.cospi_sinpi(m) + s = 2 * sin / ctx.pi + c = cos + a = 1+z + b = 1-z + u = m/2 + w = (1-z)/2 + T1 = [s, c, a, b], [-1, 1, u, -u], [], [1-m], \ + [-n, n+1], [1-m], w + T2 = [-s, a, b], [-1, -u, u], [n+m+1], [n-m+1, m+1], \ + [-n, n+1], [m+1], w + return T1, T2 + return ctx.hypercomb(h, [n, m], **kwargs) + if type == 3: + # The following is faster when there only is a single series + # Note: not valid for -1 < z < 0 (?) + if abs(z) > 1: + def h(n, m): + T1 = [ctx.expjpi(m), 2, ctx.pi, z, z-1, z+1], \ + [1, -n-1, 0.5, -n-m-1, 0.5*m, 0.5*m], \ + [n+m+1], [n+1.5], \ + [0.5*(2+n+m), 0.5*(1+n+m)], [n+1.5], z**(-2) + return [T1] + return ctx.hypercomb(h, [n, m], **kwargs) + else: + # not valid for 1 < z < inf ? + def h(n, m): + s = 2 * ctx.sinpi(m) / ctx.pi + c = ctx.expjpi(m) + a = 1+z + b = z-1 + u = m/2 + w = (1-z)/2 + T1 = [s, c, a, b], [-1, 1, u, -u], [], [1-m], \ + [-n, n+1], [1-m], w + T2 = [-s, c, a, b], [-1, 1, -u, u], [n+m+1], [n-m+1, m+1], \ + [-n, n+1], [m+1], w + return T1, T2 + return ctx.hypercomb(h, [n, m], **kwargs) + raise ValueError("requires type=2 or type=3") + +@defun_wrapped +def chebyt(ctx, n, x, **kwargs): + if (not x) and ctx.isint(n) and int(ctx._re(n)) % 2 == 1: + return x * 0 + return ctx.hyp2f1(-n,n,(1,2),(1-x)/2, **kwargs) + +@defun_wrapped +def chebyu(ctx, n, x, **kwargs): + if (not x) and ctx.isint(n) and int(ctx._re(n)) % 2 == 1: + return x * 0 + return (n+1) * ctx.hyp2f1(-n, n+2, (3,2), (1-x)/2, **kwargs) + +@defun +def spherharm(ctx, l, m, theta, phi, **kwargs): + l = ctx.convert(l) + m = ctx.convert(m) + theta = ctx.convert(theta) + phi = ctx.convert(phi) + l_isint = ctx.isint(l) + l_natural = l_isint and l >= 0 + m_isint = ctx.isint(m) + if l_isint and l < 0 and m_isint: + return ctx.spherharm(-(l+1), m, theta, phi, **kwargs) + if theta == 0 and m_isint and m < 0: + return ctx.zero * 1j + if l_natural and m_isint: + if abs(m) > l: + return ctx.zero * 1j + # http://functions.wolfram.com/Polynomials/ + # SphericalHarmonicY/26/01/02/0004/ + def h(l,m): + absm = abs(m) + C = [-1, ctx.expj(m*phi), + (2*l+1)*ctx.fac(l+absm)/ctx.pi/ctx.fac(l-absm), + ctx.sin(theta)**2, + ctx.fac(absm), 2] + P = [0.5*m*(ctx.sign(m)+1), 1, 0.5, 0.5*absm, -1, -absm-1] + return ((C, P, [], [], [absm-l, l+absm+1], [absm+1], + ctx.sin(0.5*theta)**2),) + else: + # http://functions.wolfram.com/HypergeometricFunctions/ + # SphericalHarmonicYGeneral/26/01/02/0001/ + def h(l,m): + if ctx.isnpint(l-m+1) or ctx.isnpint(l+m+1) or ctx.isnpint(1-m): + return (([0], [-1], [], [], [], [], 0),) + cos, sin = ctx.cos_sin(0.5*theta) + C = [0.5*ctx.expj(m*phi), (2*l+1)/ctx.pi, + ctx.gamma(l-m+1), ctx.gamma(l+m+1), + cos**2, sin**2] + P = [1, 0.5, 0.5, -0.5, 0.5*m, -0.5*m] + return ((C, P, [], [1-m], [-l,l+1], [1-m], sin**2),) + return ctx.hypercomb(h, [l,m], **kwargs) diff --git a/lib/python3.12/site-packages/mpmath/functions/qfunctions.py b/lib/python3.12/site-packages/mpmath/functions/qfunctions.py new file mode 100644 index 0000000000000000000000000000000000000000..5a20e53a8b6fa0d8fbc9ad098614d2694998f49a --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/functions/qfunctions.py @@ -0,0 +1,280 @@ +from .functions import defun, defun_wrapped + +@defun +def qp(ctx, a, q=None, n=None, **kwargs): + r""" + Evaluates the q-Pochhammer symbol (or q-rising factorial) + + .. math :: + + (a; q)_n = \prod_{k=0}^{n-1} (1-a q^k) + + where `n = \infty` is permitted if `|q| < 1`. Called with two arguments, + ``qp(a,q)`` computes `(a;q)_{\infty}`; with a single argument, ``qp(q)`` + computes `(q;q)_{\infty}`. The special case + + .. math :: + + \phi(q) = (q; q)_{\infty} = \prod_{k=1}^{\infty} (1-q^k) = + \sum_{k=-\infty}^{\infty} (-1)^k q^{(3k^2-k)/2} + + is also known as the Euler function, or (up to a factor `q^{-1/24}`) + the Dedekind eta function. + + **Examples** + + If `n` is a positive integer, the function amounts to a finite product:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> qp(2,3,5) + -725305.0 + >>> fprod(1-2*3**k for k in range(5)) + -725305.0 + >>> qp(2,3,0) + 1.0 + + Complex arguments are allowed:: + + >>> qp(2-1j, 0.75j) + (0.4628842231660149089976379 + 4.481821753552703090628793j) + + The regular Pochhammer symbol `(a)_n` is obtained in the + following limit as `q \to 1`:: + + >>> a, n = 4, 7 + >>> limit(lambda q: qp(q**a,q,n) / (1-q)**n, 1) + 604800.0 + >>> rf(a,n) + 604800.0 + + The Taylor series of the reciprocal Euler function gives + the partition function `P(n)`, i.e. the number of ways of writing + `n` as a sum of positive integers:: + + >>> taylor(lambda q: 1/qp(q), 0, 10) + [1.0, 1.0, 2.0, 3.0, 5.0, 7.0, 11.0, 15.0, 22.0, 30.0, 42.0] + + Special values include:: + + >>> qp(0) + 1.0 + >>> findroot(diffun(qp), -0.4) # location of maximum + -0.4112484791779547734440257 + >>> qp(_) + 1.228348867038575112586878 + + The q-Pochhammer symbol is related to the Jacobi theta functions. + For example, the following identity holds:: + + >>> q = mpf(0.5) # arbitrary + >>> qp(q) + 0.2887880950866024212788997 + >>> root(3,-2)*root(q,-24)*jtheta(2,pi/6,root(q,6)) + 0.2887880950866024212788997 + + """ + a = ctx.convert(a) + if n is None: + n = ctx.inf + else: + n = ctx.convert(n) + if n < 0: + raise ValueError("n cannot be negative") + if q is None: + q = a + else: + q = ctx.convert(q) + if n == 0: + return ctx.one + 0*(a+q) + infinite = (n == ctx.inf) + same = (a == q) + if infinite: + if abs(q) >= 1: + if same and (q == -1 or q == 1): + return ctx.zero * q + raise ValueError("q-function only defined for |q| < 1") + elif q == 0: + return ctx.one - a + maxterms = kwargs.get('maxterms', 50*ctx.prec) + if infinite and same: + # Euler's pentagonal theorem + def terms(): + t = 1 + yield t + k = 1 + x1 = q + x2 = q**2 + while 1: + yield (-1)**k * x1 + yield (-1)**k * x2 + x1 *= q**(3*k+1) + x2 *= q**(3*k+2) + k += 1 + if k > maxterms: + raise ctx.NoConvergence + return ctx.sum_accurately(terms) + # return ctx.nprod(lambda k: 1-a*q**k, [0,n-1]) + def factors(): + k = 0 + r = ctx.one + while 1: + yield 1 - a*r + r *= q + k += 1 + if k >= n: + return + if k > maxterms: + raise ctx.NoConvergence + return ctx.mul_accurately(factors) + +@defun_wrapped +def qgamma(ctx, z, q, **kwargs): + r""" + Evaluates the q-gamma function + + .. math :: + + \Gamma_q(z) = \frac{(q; q)_{\infty}}{(q^z; q)_{\infty}} (1-q)^{1-z}. + + + **Examples** + + Evaluation for real and complex arguments:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> qgamma(4,0.75) + 4.046875 + >>> qgamma(6,6) + 121226245.0 + >>> qgamma(3+4j, 0.5j) + (0.1663082382255199834630088 + 0.01952474576025952984418217j) + + The q-gamma function satisfies a functional equation similar + to that of the ordinary gamma function:: + + >>> q = mpf(0.25) + >>> z = mpf(2.5) + >>> qgamma(z+1,q) + 1.428277424823760954685912 + >>> (1-q**z)/(1-q)*qgamma(z,q) + 1.428277424823760954685912 + + """ + if abs(q) > 1: + return ctx.qgamma(z,1/q)*q**((z-2)*(z-1)*0.5) + return ctx.qp(q, q, None, **kwargs) / \ + ctx.qp(q**z, q, None, **kwargs) * (1-q)**(1-z) + +@defun_wrapped +def qfac(ctx, z, q, **kwargs): + r""" + Evaluates the q-factorial, + + .. math :: + + [n]_q! = (1+q)(1+q+q^2)\cdots(1+q+\cdots+q^{n-1}) + + or more generally + + .. math :: + + [z]_q! = \frac{(q;q)_z}{(1-q)^z}. + + **Examples** + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> qfac(0,0) + 1.0 + >>> qfac(4,3) + 2080.0 + >>> qfac(5,6) + 121226245.0 + >>> qfac(1+1j, 2+1j) + (0.4370556551322672478613695 + 0.2609739839216039203708921j) + + """ + if ctx.isint(z) and ctx._re(z) > 0: + n = int(ctx._re(z)) + return ctx.qp(q, q, n, **kwargs) / (1-q)**n + return ctx.qgamma(z+1, q, **kwargs) + +@defun +def qhyper(ctx, a_s, b_s, q, z, **kwargs): + r""" + Evaluates the basic hypergeometric series or hypergeometric q-series + + .. math :: + + \,_r\phi_s \left[\begin{matrix} + a_1 & a_2 & \ldots & a_r \\ + b_1 & b_2 & \ldots & b_s + \end{matrix} ; q,z \right] = + \sum_{n=0}^\infty + \frac{(a_1;q)_n, \ldots, (a_r;q)_n} + {(b_1;q)_n, \ldots, (b_s;q)_n} + \left((-1)^n q^{n\choose 2}\right)^{1+s-r} + \frac{z^n}{(q;q)_n} + + where `(a;q)_n` denotes the q-Pochhammer symbol (see :func:`~mpmath.qp`). + + **Examples** + + Evaluation works for real and complex arguments:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> qhyper([0.5], [2.25], 0.25, 4) + -0.1975849091263356009534385 + >>> qhyper([0.5], [2.25], 0.25-0.25j, 4) + (2.806330244925716649839237 + 3.568997623337943121769938j) + >>> qhyper([1+j], [2,3+0.5j], 0.25, 3+4j) + (9.112885171773400017270226 - 1.272756997166375050700388j) + + Comparing with a summation of the defining series, using + :func:`~mpmath.nsum`:: + + >>> b, q, z = 3, 0.25, 0.5 + >>> qhyper([], [b], q, z) + 0.6221136748254495583228324 + >>> nsum(lambda n: z**n / qp(q,q,n)/qp(b,q,n) * q**(n*(n-1)), [0,inf]) + 0.6221136748254495583228324 + + """ + #a_s = [ctx._convert_param(a)[0] for a in a_s] + #b_s = [ctx._convert_param(b)[0] for b in b_s] + #q = ctx._convert_param(q)[0] + a_s = [ctx.convert(a) for a in a_s] + b_s = [ctx.convert(b) for b in b_s] + q = ctx.convert(q) + z = ctx.convert(z) + r = len(a_s) + s = len(b_s) + d = 1+s-r + maxterms = kwargs.get('maxterms', 50*ctx.prec) + def terms(): + t = ctx.one + yield t + qk = 1 + k = 0 + x = 1 + while 1: + for a in a_s: + p = 1 - a*qk + t *= p + for b in b_s: + p = 1 - b*qk + if not p: + raise ValueError + t /= p + t *= z + x *= (-1)**d * qk ** d + qk *= q + t /= (1 - qk) + k += 1 + yield t * x + if k > maxterms: + raise ctx.NoConvergence + return ctx.sum_accurately(terms) diff --git a/lib/python3.12/site-packages/mpmath/functions/rszeta.py b/lib/python3.12/site-packages/mpmath/functions/rszeta.py new file mode 100644 index 0000000000000000000000000000000000000000..19e2c9a251b81bafe8cf77a2b0180636b1078ee4 --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/functions/rszeta.py @@ -0,0 +1,1403 @@ +""" +--------------------------------------------------------------------- +.. sectionauthor:: Juan Arias de Reyna + +This module implements zeta-related functions using the Riemann-Siegel +expansion: zeta_offline(s,k=0) + +* coef(J, eps): Need in the computation of Rzeta(s,k) + +* Rzeta_simul(s, der=0) computes Rzeta^(k)(s) and Rzeta^(k)(1-s) simultaneously + for 0 <= k <= der. Used by zeta_offline and z_offline + +* Rzeta_set(s, derivatives) computes Rzeta^(k)(s) for given derivatives, used by + z_half(t,k) and zeta_half + +* z_offline(w,k): Z(w) and its derivatives of order k <= 4 +* z_half(t,k): Z(t) (Riemann Siegel function) and its derivatives of order k <= 4 +* zeta_offline(s): zeta(s) and its derivatives of order k<= 4 +* zeta_half(1/2+it,k): zeta(s) and its derivatives of order k<= 4 + +* rs_zeta(s,k=0) Computes zeta^(k)(s) Unifies zeta_half and zeta_offline +* rs_z(w,k=0) Computes Z^(k)(w) Unifies z_offline and z_half +---------------------------------------------------------------------- + +This program uses Riemann-Siegel expansion even to compute +zeta(s) on points s = sigma + i t with sigma arbitrary not +necessarily equal to 1/2. + +It is founded on a new deduction of the formula, with rigorous +and sharp bounds for the terms and rest of this expansion. + +More information on the papers: + + J. Arias de Reyna, High Precision Computation of Riemann's + Zeta Function by the Riemann-Siegel Formula I, II + + We refer to them as I, II. + + In them we shall find detailed explanation of all the + procedure. + +The program uses Riemann-Siegel expansion. +This is useful when t is big, ( say t > 10000 ). +The precision is limited, roughly it can compute zeta(sigma+it) +with an error less than exp(-c t) for some constant c depending +on sigma. The program gives an error when the Riemann-Siegel +formula can not compute to the wanted precision. + +""" + +import math + +class RSCache(object): + def __init__(ctx): + ctx._rs_cache = [0, 10, {}, {}] + +from .functions import defun + +#-------------------------------------------------------------------------------# +# # +# coef(ctx, J, eps, _cache=[0, 10, {} ] ) # +# # +#-------------------------------------------------------------------------------# + +# This function computes the coefficients c[n] defined on (I, equation (47)) +# but see also (II, section 3.14). +# +# Since these coefficients are very difficult to compute we save the values +# in a cache. So if we compute several values of the functions Rzeta(s) for +# near values of s, we do not recompute these coefficients. +# +# c[n] are the Taylor coefficients of the function: +# +# F(z):= (exp(pi*j*(z*z/2+3/8))-j* sqrt(2) cos(pi*z/2))/(2*cos(pi *z)) +# +# + +def _coef(ctx, J, eps): + r""" + Computes the coefficients `c_n` for `0\le n\le 2J` with error less than eps + + **Definition** + + The coefficients c_n are defined by + + .. math :: + + \begin{equation} + F(z)=\frac{e^{\pi i + \bigl(\frac{z^2}{2}+\frac38\bigr)}-i\sqrt{2}\cos\frac{\pi}{2}z}{2\cos\pi + z}=\sum_{n=0}^\infty c_{2n} z^{2n} + \end{equation} + + they are computed applying the relation + + .. math :: + + \begin{multline} + c_{2n}=-\frac{i}{\sqrt{2}}\Bigl(\frac{\pi}{2}\Bigr)^{2n} + \sum_{k=0}^n\frac{(-1)^k}{(2k)!} + 2^{2n-2k}\frac{(-1)^{n-k}E_{2n-2k}}{(2n-2k)!}+\\ + +e^{3\pi i/8}\sum_{j=0}^n(-1)^j\frac{ + E_{2j}}{(2j)!}\frac{i^{n-j}\pi^{n+j}}{(n-j)!2^{n-j+1}}. + \end{multline} + """ + + newJ = J+2 # compute more coefficients that are needed + neweps6 = eps/2. # compute with a slight more precision that are needed + + # PREPARATION FOR THE COMPUTATION OF V(N) AND W(N) + # See II Section 3.16 + # + # Computing the exponent wpvw of the error II equation (81) + wpvw = max(ctx.mag(10*(newJ+3)), 4*newJ+5-ctx.mag(neweps6)) + + # Preparation of Euler numbers (we need until the 2*RS_NEWJ) + E = ctx._eulernum(2*newJ) + + # Now we have in the cache all the needed Euler numbers. + # + # Computing the powers of pi + # + # We need to compute the powers pi**n for 1<= n <= 2*J + # with relative error less than 2**(-wpvw) + # it is easy to show that this is obtained + # taking wppi as the least d with + # 2**d>40*J and 2**d> 4.24 *newJ + 2**wpvw + # In II Section 3.9 we need also that + # wppi > wptcoef[0], and that the powers + # here computed 0<= k <= 2*newJ are more + # than those needed there that are 2*L-2. + # so we need J >= L this will be checked + # before computing tcoef[] + wppi = max(ctx.mag(40*newJ), ctx.mag(newJ)+3 +wpvw) + ctx.prec = wppi + pipower = {} + pipower[0] = ctx.one + pipower[1] = ctx.pi + for n in range(2,2*newJ+1): + pipower[n] = pipower[n-1]*ctx.pi + + # COMPUTING THE COEFFICIENTS v(n) AND w(n) + # see II equation (61) and equations (81) and (82) + ctx.prec = wpvw+2 + v={} + w={} + for n in range(0,newJ+1): + va = (-1)**n * ctx._eulernum(2*n) + va = ctx.mpf(va)/ctx.fac(2*n) + v[n]=va*pipower[2*n] + for n in range(0,2*newJ+1): + wa = ctx.one/ctx.fac(n) + wa=wa/(2**n) + w[n]=wa*pipower[n] + + # COMPUTATION OF THE CONVOLUTIONS RS_P1 AND RS_P2 + # See II Section 3.16 + ctx.prec = 15 + wpp1a = 9 - ctx.mag(neweps6) + P1 = {} + for n in range(0,newJ+1): + ctx.prec = 15 + wpp1 = max(ctx.mag(10*(n+4)),4*n+wpp1a) + ctx.prec = wpp1 + sump = 0 + for k in range(0,n+1): + sump += ((-1)**k) * v[k]*w[2*n-2*k] + P1[n]=((-1)**(n+1))*ctx.j*sump + P2={} + for n in range(0,newJ+1): + ctx.prec = 15 + wpp2 = max(ctx.mag(10*(n+4)),4*n+wpp1a) + ctx.prec = wpp2 + sump = 0 + for k in range(0,n+1): + sump += (ctx.j**(n-k)) * v[k]*w[n-k] + P2[n]=sump + # COMPUTING THE COEFFICIENTS c[2n] + # See II Section 3.14 + ctx.prec = 15 + wpc0 = 5 - ctx.mag(neweps6) + wpc = max(6,4*newJ+wpc0) + ctx.prec = wpc + mu = ctx.sqrt(ctx.mpf('2'))/2 + nu = ctx.expjpi(3./8)/2 + c={} + for n in range(0,newJ): + ctx.prec = 15 + wpc = max(6,4*n+wpc0) + ctx.prec = wpc + c[2*n] = mu*P1[n]+nu*P2[n] + for n in range(1,2*newJ,2): + c[n] = 0 + return [newJ, neweps6, c, pipower] + +def coef(ctx, J, eps): + _cache = ctx._rs_cache + if J <= _cache[0] and eps >= _cache[1]: + return _cache[2], _cache[3] + orig = ctx._mp.prec + try: + data = _coef(ctx._mp, J, eps) + finally: + ctx._mp.prec = orig + if ctx is not ctx._mp: + data[2] = dict((k,ctx.convert(v)) for (k,v) in data[2].items()) + data[3] = dict((k,ctx.convert(v)) for (k,v) in data[3].items()) + ctx._rs_cache[:] = data + return ctx._rs_cache[2], ctx._rs_cache[3] + +#-------------------------------------------------------------------------------# +# # +# Rzeta_simul(s,k=0) # +# # +#-------------------------------------------------------------------------------# +# This function return a list with the values: +# Rzeta(sigma+it), conj(Rzeta(1-sigma+it)),Rzeta'(sigma+it), conj(Rzeta'(1-sigma+it)), +# .... , Rzeta^{(k)}(sigma+it), conj(Rzeta^{(k)}(1-sigma+it)) +# +# Useful to compute the function zeta(s) and Z(w) or its derivatives. +# + +def aux_M_Fp(ctx, xA, xeps4, a, xB1, xL): + # COMPUTING M NUMBER OF DERIVATIVES Fp[m] TO COMPUTE + # See II Section 3.11 equations (47) and (48) + aux1 = 126.0657606*xA/xeps4 # 126.06.. = 316/sqrt(2*pi) + aux1 = ctx.ln(aux1) + aux2 = (2*ctx.ln(ctx.pi)+ctx.ln(xB1)+ctx.ln(a))/3 -ctx.ln(2*ctx.pi)/2 + m = 3*xL-3 + aux3= (ctx.loggamma(m+1)-ctx.loggamma(m/3.0+2))/2 -ctx.loggamma((m+1)/2.) + while((aux1 < m*aux2+ aux3)and (m>1)): + m = m - 1 + aux3 = (ctx.loggamma(m+1)-ctx.loggamma(m/3.0+2))/2 -ctx.loggamma((m+1)/2.) + xM = m + return xM + +def aux_J_needed(ctx, xA, xeps4, a, xB1, xM): + # DETERMINATION OF J THE NUMBER OF TERMS NEEDED + # IN THE TAYLOR SERIES OF F. + # See II Section 3.11 equation (49)) + # Only determine one + h1 = xeps4/(632*xA) + h2 = xB1*a * 126.31337419529260248 # = pi^2*e^2*sqrt(3) + h2 = h1 * ctx.power((h2/xM**2),(xM-1)/3) / xM + h3 = min(h1,h2) + return h3 + +def Rzeta_simul(ctx, s, der=0): + # First we take the value of ctx.prec + wpinitial = ctx.prec + + # INITIALIZATION + # Take the real and imaginary part of s + t = ctx._im(s) + xsigma = ctx._re(s) + ysigma = 1 - xsigma + + # Now compute several parameter that appear on the program + ctx.prec = 15 + a = ctx.sqrt(t/(2*ctx.pi)) + xasigma = a ** xsigma + yasigma = a ** ysigma + + # We need a simple bound A1 < asigma (see II Section 3.1 and 3.3) + xA1=ctx.power(2, ctx.mag(xasigma)-1) + yA1=ctx.power(2, ctx.mag(yasigma)-1) + + # We compute various epsilon's (see II end of Section 3.1) + eps = ctx.power(2, -wpinitial) + eps1 = eps/6. + xeps2 = eps * xA1/3. + yeps2 = eps * yA1/3. + + # COMPUTING SOME COEFFICIENTS THAT DEPENDS + # ON sigma + # constant b and c (see I Theorem 2 formula (26) ) + # coefficients A and B1 (see I Section 6.1 equation (50)) + # + # here we not need high precision + ctx.prec = 15 + if xsigma > 0: + xb = 2. + xc = math.pow(9,xsigma)/4.44288 + # 4.44288 =(math.sqrt(2)*math.pi) + xA = math.pow(9,xsigma) + xB1 = 1 + else: + xb = 2.25158 # math.sqrt( (3-2* math.log(2))*math.pi ) + xc = math.pow(2,-xsigma)/4.44288 + xA = math.pow(2,-xsigma) + xB1 = 1.10789 # = 2*sqrt(1-log(2)) + + if(ysigma > 0): + yb = 2. + yc = math.pow(9,ysigma)/4.44288 + # 4.44288 =(math.sqrt(2)*math.pi) + yA = math.pow(9,ysigma) + yB1 = 1 + else: + yb = 2.25158 # math.sqrt( (3-2* math.log(2))*math.pi ) + yc = math.pow(2,-ysigma)/4.44288 + yA = math.pow(2,-ysigma) + yB1 = 1.10789 # = 2*sqrt(1-log(2)) + + # COMPUTING L THE NUMBER OF TERMS NEEDED IN THE RIEMANN-SIEGEL + # CORRECTION + # See II Section 3.2 + ctx.prec = 15 + xL = 1 + while 3*xc*ctx.gamma(xL*0.5) * ctx.power(xb*a,-xL) >= xeps2: + xL = xL+1 + xL = max(2,xL) + yL = 1 + while 3*yc*ctx.gamma(yL*0.5) * ctx.power(yb*a,-yL) >= yeps2: + yL = yL+1 + yL = max(2,yL) + + # The number L has to satify some conditions. + # If not RS can not compute Rzeta(s) with the prescribed precision + # (see II, Section 3.2 condition (20) ) and + # (II, Section 3.3 condition (22) ). Also we have added + # an additional technical condition in Section 3.17 Proposition 17 + if ((3*xL >= 2*a*a/25.) or (3*xL+2+xsigma<0) or (abs(xsigma) > a/2.) or \ + (3*yL >= 2*a*a/25.) or (3*yL+2+ysigma<0) or (abs(ysigma) > a/2.)): + ctx.prec = wpinitial + raise NotImplementedError("Riemann-Siegel can not compute with such precision") + + # We take the maximum of the two values + L = max(xL, yL) + + # INITIALIZATION (CONTINUATION) + # + # eps3 is the constant defined on (II, Section 3.5 equation (27) ) + # each term of the RS correction must be computed with error <= eps3 + xeps3 = xeps2/(4*xL) + yeps3 = yeps2/(4*yL) + + # eps4 is defined on (II Section 3.6 equation (30) ) + # each component of the formula (II Section 3.6 equation (29) ) + # must be computed with error <= eps4 + xeps4 = xeps3/(3*xL) + yeps4 = yeps3/(3*yL) + + # COMPUTING M NUMBER OF DERIVATIVES Fp[m] TO COMPUTE + xM = aux_M_Fp(ctx, xA, xeps4, a, xB1, xL) + yM = aux_M_Fp(ctx, yA, yeps4, a, yB1, yL) + M = max(xM, yM) + + # COMPUTING NUMBER OF TERMS J NEEDED + h3 = aux_J_needed(ctx, xA, xeps4, a, xB1, xM) + h4 = aux_J_needed(ctx, yA, yeps4, a, yB1, yM) + h3 = min(h3,h4) + J = 12 + jvalue = (2*ctx.pi)**J / ctx.gamma(J+1) + while jvalue > h3: + J = J+1 + jvalue = (2*ctx.pi)*jvalue/J + + # COMPUTING eps5[m] for 1 <= m <= 21 + # See II Section 10 equation (43) + # We choose the minimum of the two possibilities + eps5={} + xforeps5 = math.pi*math.pi*xB1*a + yforeps5 = math.pi*math.pi*yB1*a + for m in range(0,22): + xaux1 = math.pow(xforeps5, m/3)/(316.*xA) + yaux1 = math.pow(yforeps5, m/3)/(316.*yA) + aux1 = min(xaux1, yaux1) + aux2 = ctx.gamma(m+1)/ctx.gamma(m/3.0+0.5) + aux2 = math.sqrt(aux2) + eps5[m] = (aux1*aux2*min(xeps4,yeps4)) + + # COMPUTING wpfp + # See II Section 3.13 equation (59) + twenty = min(3*L-3, 21)+1 + aux = 6812*J + wpfp = ctx.mag(44*J) + for m in range(0,twenty): + wpfp = max(wpfp, ctx.mag(aux*ctx.gamma(m+1)/eps5[m])) + + # COMPUTING N AND p + # See II Section + ctx.prec = wpfp + ctx.mag(t)+20 + a = ctx.sqrt(t/(2*ctx.pi)) + N = ctx.floor(a) + p = 1-2*(a-N) + + # now we get a rounded version of p + # to the precision wpfp + # this possibly is not necessary + num=ctx.floor(p*(ctx.mpf('2')**wpfp)) + difference = p * (ctx.mpf('2')**wpfp)-num + if (difference < 0.5): + num = num + else: + num = num+1 + p = ctx.convert(num * (ctx.mpf('2')**(-wpfp))) + + # COMPUTING THE COEFFICIENTS c[n] = cc[n] + # We shall use the notation cc[n], since there is + # a constant that is called c + # See II Section 3.14 + # We compute the coefficients and also save then in a + # cache. The bulk of the computation is passed to + # the function coef() + # + # eps6 is defined in II Section 3.13 equation (58) + eps6 = ctx.power(ctx.convert(2*ctx.pi), J)/(ctx.gamma(J+1)*3*J) + + # Now we compute the coefficients + cc = {} + cont = {} + cont, pipowers = coef(ctx, J, eps6) + cc=cont.copy() # we need a copy since we have to change his values. + Fp={} # this is the adequate locus of this + for n in range(M, 3*L-2): + Fp[n] = 0 + Fp={} + ctx.prec = wpfp + for m in range(0,M+1): + sumP = 0 + for k in range(2*J-m-1,-1,-1): + sumP = (sumP * p)+ cc[k] + Fp[m] = sumP + # preparation of the new coefficients + for k in range(0,2*J-m-1): + cc[k] = (k+1)* cc[k+1] + + # COMPUTING THE NUMBERS xd[u,n,k], yd[u,n,k] + # See II Section 3.17 + # + # First we compute the working precisions xwpd[k] + # Se II equation (92) + xwpd={} + d1 = max(6,ctx.mag(40*L*L)) + xd2 = 13+ctx.mag((1+abs(xsigma))*xA)-ctx.mag(xeps4)-1 + xconst = ctx.ln(8/(ctx.pi*ctx.pi*a*a*xB1*xB1)) /2 + for n in range(0,L): + xd3 = ctx.mag(ctx.sqrt(ctx.gamma(n-0.5)))-ctx.floor(n*xconst)+xd2 + xwpd[n]=max(xd3,d1) + + # procedure of II Section 3.17 + ctx.prec = xwpd[1]+10 + xpsigma = 1-(2*xsigma) + xd = {} + xd[0,0,-2]=0; xd[0,0,-1]=0; xd[0,0,0]=1; xd[0,0,1]=0 + xd[0,-1,-2]=0; xd[0,-1,-1]=0; xd[0,-1,0]=1; xd[0,-1,1]=0 + for n in range(1,L): + ctx.prec = xwpd[n]+10 + for k in range(0,3*n//2+1): + m = 3*n-2*k + if(m!=0): + m1 = ctx.one/m + c1= m1/4 + c2=(xpsigma*m1)/2 + c3=-(m+1) + xd[0,n,k]=c3*xd[0,n-1,k-2]+c1*xd[0,n-1,k]+c2*xd[0,n-1,k-1] + else: + xd[0,n,k]=0 + for r in range(0,k): + add=xd[0,n,r]*(ctx.mpf('1.0')*ctx.fac(2*k-2*r)/ctx.fac(k-r)) + xd[0,n,k] -= ((-1)**(k-r))*add + xd[0,n,-2]=0; xd[0,n,-1]=0; xd[0,n,3*n//2+1]=0 + for mu in range(-2,der+1): + for n in range(-2,L): + for k in range(-3,max(1,3*n//2+2)): + if( (mu<0)or (n<0) or(k<0)or (k>3*n//2)): + xd[mu,n,k] = 0 + for mu in range(1,der+1): + for n in range(0,L): + ctx.prec = xwpd[n]+10 + for k in range(0,3*n//2+1): + aux=(2*mu-2)*xd[mu-2,n-2,k-3]+2*(xsigma+n-2)*xd[mu-1,n-2,k-3] + xd[mu,n,k] = aux - xd[mu-1,n-1,k-1] + + # Now we compute the working precisions ywpd[k] + # Se II equation (92) + ywpd={} + d1 = max(6,ctx.mag(40*L*L)) + yd2 = 13+ctx.mag((1+abs(ysigma))*yA)-ctx.mag(yeps4)-1 + yconst = ctx.ln(8/(ctx.pi*ctx.pi*a*a*yB1*yB1)) /2 + for n in range(0,L): + yd3 = ctx.mag(ctx.sqrt(ctx.gamma(n-0.5)))-ctx.floor(n*yconst)+yd2 + ywpd[n]=max(yd3,d1) + + # procedure of II Section 3.17 + ctx.prec = ywpd[1]+10 + ypsigma = 1-(2*ysigma) + yd = {} + yd[0,0,-2]=0; yd[0,0,-1]=0; yd[0,0,0]=1; yd[0,0,1]=0 + yd[0,-1,-2]=0; yd[0,-1,-1]=0; yd[0,-1,0]=1; yd[0,-1,1]=0 + for n in range(1,L): + ctx.prec = ywpd[n]+10 + for k in range(0,3*n//2+1): + m = 3*n-2*k + if(m!=0): + m1 = ctx.one/m + c1= m1/4 + c2=(ypsigma*m1)/2 + c3=-(m+1) + yd[0,n,k]=c3*yd[0,n-1,k-2]+c1*yd[0,n-1,k]+c2*yd[0,n-1,k-1] + else: + yd[0,n,k]=0 + for r in range(0,k): + add=yd[0,n,r]*(ctx.mpf('1.0')*ctx.fac(2*k-2*r)/ctx.fac(k-r)) + yd[0,n,k] -= ((-1)**(k-r))*add + yd[0,n,-2]=0; yd[0,n,-1]=0; yd[0,n,3*n//2+1]=0 + + for mu in range(-2,der+1): + for n in range(-2,L): + for k in range(-3,max(1,3*n//2+2)): + if( (mu<0)or (n<0) or(k<0)or (k>3*n//2)): + yd[mu,n,k] = 0 + for mu in range(1,der+1): + for n in range(0,L): + ctx.prec = ywpd[n]+10 + for k in range(0,3*n//2+1): + aux=(2*mu-2)*yd[mu-2,n-2,k-3]+2*(ysigma+n-2)*yd[mu-1,n-2,k-3] + yd[mu,n,k] = aux - yd[mu-1,n-1,k-1] + + # COMPUTING THE COEFFICIENTS xtcoef[k,l] + # See II Section 3.9 + # + # computing the needed wp + xwptcoef={} + xwpterm={} + ctx.prec = 15 + c1 = ctx.mag(40*(L+2)) + xc2 = ctx.mag(68*(L+2)*xA) + xc4 = ctx.mag(xB1*a*math.sqrt(ctx.pi))-1 + for k in range(0,L): + xc3 = xc2 - k*xc4+ctx.mag(ctx.fac(k+0.5))/2. + xwptcoef[k] = (max(c1,xc3-ctx.mag(xeps4)+1)+1 +20)*1.5 + xwpterm[k] = (max(c1,ctx.mag(L+2)+xc3-ctx.mag(xeps3)+1)+1 +20) + ywptcoef={} + ywpterm={} + ctx.prec = 15 + c1 = ctx.mag(40*(L+2)) + yc2 = ctx.mag(68*(L+2)*yA) + yc4 = ctx.mag(yB1*a*math.sqrt(ctx.pi))-1 + for k in range(0,L): + yc3 = yc2 - k*yc4+ctx.mag(ctx.fac(k+0.5))/2. + ywptcoef[k] = ((max(c1,yc3-ctx.mag(yeps4)+1))+10)*1.5 + ywpterm[k] = (max(c1,ctx.mag(L+2)+yc3-ctx.mag(yeps3)+1)+1)+10 + + # check of power of pi + # computing the fortcoef[mu,k,ell] + xfortcoef={} + for mu in range(0,der+1): + for k in range(0,L): + for ell in range(-2,3*k//2+1): + xfortcoef[mu,k,ell]=0 + for mu in range(0,der+1): + for k in range(0,L): + ctx.prec = xwptcoef[k] + for ell in range(0,3*k//2+1): + xfortcoef[mu,k,ell]=xd[mu,k,ell]*Fp[3*k-2*ell]/pipowers[2*k-ell] + xfortcoef[mu,k,ell]=xfortcoef[mu,k,ell]/((2*ctx.j)**ell) + + def trunc_a(t): + wp = ctx.prec + ctx.prec = wp + 2 + aa = ctx.sqrt(t/(2*ctx.pi)) + ctx.prec = wp + return aa + + # computing the tcoef[k,ell] + xtcoef={} + for mu in range(0,der+1): + for k in range(0,L): + for ell in range(-2,3*k//2+1): + xtcoef[mu,k,ell]=0 + ctx.prec = max(xwptcoef[0],ywptcoef[0])+3 + aa= trunc_a(t) + la = -ctx.ln(aa) + + for chi in range(0,der+1): + for k in range(0,L): + ctx.prec = xwptcoef[k] + for ell in range(0,3*k//2+1): + xtcoef[chi,k,ell] =0 + for mu in range(0, chi+1): + tcoefter=ctx.binomial(chi,mu)*ctx.power(la,mu)*xfortcoef[chi-mu,k,ell] + xtcoef[chi,k,ell] += tcoefter + + # COMPUTING THE COEFFICIENTS ytcoef[k,l] + # See II Section 3.9 + # + # computing the needed wp + # check of power of pi + # computing the fortcoef[mu,k,ell] + yfortcoef={} + for mu in range(0,der+1): + for k in range(0,L): + for ell in range(-2,3*k//2+1): + yfortcoef[mu,k,ell]=0 + for mu in range(0,der+1): + for k in range(0,L): + ctx.prec = ywptcoef[k] + for ell in range(0,3*k//2+1): + yfortcoef[mu,k,ell]=yd[mu,k,ell]*Fp[3*k-2*ell]/pipowers[2*k-ell] + yfortcoef[mu,k,ell]=yfortcoef[mu,k,ell]/((2*ctx.j)**ell) + # computing the tcoef[k,ell] + ytcoef={} + for chi in range(0,der+1): + for k in range(0,L): + for ell in range(-2,3*k//2+1): + ytcoef[chi,k,ell]=0 + for chi in range(0,der+1): + for k in range(0,L): + ctx.prec = ywptcoef[k] + for ell in range(0,3*k//2+1): + ytcoef[chi,k,ell] =0 + for mu in range(0, chi+1): + tcoefter=ctx.binomial(chi,mu)*ctx.power(la,mu)*yfortcoef[chi-mu,k,ell] + ytcoef[chi,k,ell] += tcoefter + + # COMPUTING tv[k,ell] + # See II Section 3.8 + # + # a has a good value + ctx.prec = max(xwptcoef[0], ywptcoef[0])+2 + av = {} + av[0] = 1 + av[1] = av[0]/a + + ctx.prec = max(xwptcoef[0],ywptcoef[0]) + for k in range(2,L): + av[k] = av[k-1] * av[1] + + # Computing the quotients + xtv = {} + for chi in range(0,der+1): + for k in range(0,L): + ctx.prec = xwptcoef[k] + for ell in range(0,3*k//2+1): + xtv[chi,k,ell] = xtcoef[chi,k,ell]* av[k] + # Computing the quotients + ytv = {} + for chi in range(0,der+1): + for k in range(0,L): + ctx.prec = ywptcoef[k] + for ell in range(0,3*k//2+1): + ytv[chi,k,ell] = ytcoef[chi,k,ell]* av[k] + + # COMPUTING THE TERMS xterm[k] + # See II Section 3.6 + xterm = {} + for chi in range(0,der+1): + for n in range(0,L): + ctx.prec = xwpterm[n] + te = 0 + for k in range(0, 3*n//2+1): + te += xtv[chi,n,k] + xterm[chi,n] = te + + # COMPUTING THE TERMS yterm[k] + # See II Section 3.6 + yterm = {} + for chi in range(0,der+1): + for n in range(0,L): + ctx.prec = ywpterm[n] + te = 0 + for k in range(0, 3*n//2+1): + te += ytv[chi,n,k] + yterm[chi,n] = te + + # COMPUTING rssum + # See II Section 3.5 + xrssum={} + ctx.prec=15 + xrsbound = math.sqrt(ctx.pi) * xc /(xb*a) + ctx.prec=15 + xwprssum = ctx.mag(4.4*((L+3)**2)*xrsbound / xeps2) + xwprssum = max(xwprssum, ctx.mag(10*(L+1))) + ctx.prec = xwprssum + for chi in range(0,der+1): + xrssum[chi] = 0 + for k in range(1,L+1): + xrssum[chi] += xterm[chi,L-k] + yrssum={} + ctx.prec=15 + yrsbound = math.sqrt(ctx.pi) * yc /(yb*a) + ctx.prec=15 + ywprssum = ctx.mag(4.4*((L+3)**2)*yrsbound / yeps2) + ywprssum = max(ywprssum, ctx.mag(10*(L+1))) + ctx.prec = ywprssum + for chi in range(0,der+1): + yrssum[chi] = 0 + for k in range(1,L+1): + yrssum[chi] += yterm[chi,L-k] + + # COMPUTING S3 + # See II Section 3.19 + ctx.prec = 15 + A2 = 2**(max(ctx.mag(abs(xrssum[0])), ctx.mag(abs(yrssum[0])))) + eps8 = eps/(3*A2) + T = t *ctx.ln(t/(2*ctx.pi)) + xwps3 = 5 + ctx.mag((1+(2/eps8)*ctx.power(a,-xsigma))*T) + ywps3 = 5 + ctx.mag((1+(2/eps8)*ctx.power(a,-ysigma))*T) + + ctx.prec = max(xwps3, ywps3) + + tpi = t/(2*ctx.pi) + arg = (t/2)*ctx.ln(tpi)-(t/2)-ctx.pi/8 + U = ctx.expj(-arg) + a = trunc_a(t) + xasigma = ctx.power(a, -xsigma) + yasigma = ctx.power(a, -ysigma) + xS3 = ((-1)**(N-1)) * xasigma * U + yS3 = ((-1)**(N-1)) * yasigma * U + + # COMPUTING S1 the zetasum + # See II Section 3.18 + ctx.prec = 15 + xwpsum = 4+ ctx.mag((N+ctx.power(N,1-xsigma))*ctx.ln(N) /eps1) + ywpsum = 4+ ctx.mag((N+ctx.power(N,1-ysigma))*ctx.ln(N) /eps1) + wpsum = max(xwpsum, ywpsum) + + ctx.prec = wpsum +10 + ''' + # This can be improved + xS1={} + yS1={} + for chi in range(0,der+1): + xS1[chi] = 0 + yS1[chi] = 0 + for n in range(1,int(N)+1): + ln = ctx.ln(n) + xexpn = ctx.exp(-ln*(xsigma+ctx.j*t)) + yexpn = ctx.conj(1/(n*xexpn)) + for chi in range(0,der+1): + pown = ctx.power(-ln, chi) + xterm = pown*xexpn + yterm = pown*yexpn + xS1[chi] += xterm + yS1[chi] += yterm + ''' + xS1, yS1 = ctx._zetasum(s, 1, int(N)-1, range(0,der+1), True) + + # END OF COMPUTATION of xrz, yrz + # See II Section 3.1 + ctx.prec = 15 + xabsS1 = abs(xS1[der]) + xabsS2 = abs(xrssum[der] * xS3) + xwpend = max(6, wpinitial+ctx.mag(6*(3*xabsS1+7*xabsS2) ) ) + + ctx.prec = xwpend + xrz={} + for chi in range(0,der+1): + xrz[chi] = xS1[chi]+xrssum[chi]*xS3 + + ctx.prec = 15 + yabsS1 = abs(yS1[der]) + yabsS2 = abs(yrssum[der] * yS3) + ywpend = max(6, wpinitial+ctx.mag(6*(3*yabsS1+7*yabsS2) ) ) + + ctx.prec = ywpend + yrz={} + for chi in range(0,der+1): + yrz[chi] = yS1[chi]+yrssum[chi]*yS3 + yrz[chi] = ctx.conj(yrz[chi]) + ctx.prec = wpinitial + return xrz, yrz + +def Rzeta_set(ctx, s, derivatives=[0]): + r""" + Computes several derivatives of the auxiliary function of Riemann `R(s)`. + + **Definition** + + The function is defined by + + .. math :: + + \begin{equation} + {\mathop{\mathcal R }\nolimits}(s)= + \int_{0\swarrow1}\frac{x^{-s} e^{\pi i x^2}}{e^{\pi i x}- + e^{-\pi i x}}\,dx + \end{equation} + + To this function we apply the Riemann-Siegel expansion. + """ + der = max(derivatives) + # First we take the value of ctx.prec + # During the computation we will change ctx.prec, and finally we will + # restaurate the initial value + wpinitial = ctx.prec + # Take the real and imaginary part of s + t = ctx._im(s) + sigma = ctx._re(s) + # Now compute several parameter that appear on the program + ctx.prec = 15 + a = ctx.sqrt(t/(2*ctx.pi)) # Careful + asigma = ctx.power(a, sigma) # Careful + # We need a simple bound A1 < asigma (see II Section 3.1 and 3.3) + A1 = ctx.power(2, ctx.mag(asigma)-1) + # We compute various epsilon's (see II end of Section 3.1) + eps = ctx.power(2, -wpinitial) + eps1 = eps/6. + eps2 = eps * A1/3. + # COMPUTING SOME COEFFICIENTS THAT DEPENDS + # ON sigma + # constant b and c (see I Theorem 2 formula (26) ) + # coefficients A and B1 (see I Section 6.1 equation (50)) + # here we not need high precision + ctx.prec = 15 + if sigma > 0: + b = 2. + c = math.pow(9,sigma)/4.44288 + # 4.44288 =(math.sqrt(2)*math.pi) + A = math.pow(9,sigma) + B1 = 1 + else: + b = 2.25158 # math.sqrt( (3-2* math.log(2))*math.pi ) + c = math.pow(2,-sigma)/4.44288 + A = math.pow(2,-sigma) + B1 = 1.10789 # = 2*sqrt(1-log(2)) + # COMPUTING L THE NUMBER OF TERMS NEEDED IN THE RIEMANN-SIEGEL + # CORRECTION + # See II Section 3.2 + ctx.prec = 15 + L = 1 + while 3*c*ctx.gamma(L*0.5) * ctx.power(b*a,-L) >= eps2: + L = L+1 + L = max(2,L) + # The number L has to satify some conditions. + # If not RS can not compute Rzeta(s) with the prescribed precision + # (see II, Section 3.2 condition (20) ) and + # (II, Section 3.3 condition (22) ). Also we have added + # an additional technical condition in Section 3.17 Proposition 17 + if ((3*L >= 2*a*a/25.) or (3*L+2+sigma<0) or (abs(sigma)> a/2.)): + #print 'Error Riemann-Siegel can not compute with such precision' + ctx.prec = wpinitial + raise NotImplementedError("Riemann-Siegel can not compute with such precision") + + # INITIALIZATION (CONTINUATION) + # + # eps3 is the constant defined on (II, Section 3.5 equation (27) ) + # each term of the RS correction must be computed with error <= eps3 + eps3 = eps2/(4*L) + + # eps4 is defined on (II Section 3.6 equation (30) ) + # each component of the formula (II Section 3.6 equation (29) ) + # must be computed with error <= eps4 + eps4 = eps3/(3*L) + + # COMPUTING M. NUMBER OF DERIVATIVES Fp[m] TO COMPUTE + M = aux_M_Fp(ctx, A, eps4, a, B1, L) + Fp = {} + for n in range(M, 3*L-2): + Fp[n] = 0 + + # But I have not seen an instance of M != 3*L-3 + # + # DETERMINATION OF J THE NUMBER OF TERMS NEEDED + # IN THE TAYLOR SERIES OF F. + # See II Section 3.11 equation (49)) + h1 = eps4/(632*A) + h2 = ctx.pi*ctx.pi*B1*a *ctx.sqrt(3)*math.e*math.e + h2 = h1 * ctx.power((h2/M**2),(M-1)/3) / M + h3 = min(h1,h2) + J=12 + jvalue = (2*ctx.pi)**J / ctx.gamma(J+1) + while jvalue > h3: + J = J+1 + jvalue = (2*ctx.pi)*jvalue/J + + # COMPUTING eps5[m] for 1 <= m <= 21 + # See II Section 10 equation (43) + eps5={} + foreps5 = math.pi*math.pi*B1*a + for m in range(0,22): + aux1 = math.pow(foreps5, m/3)/(316.*A) + aux2 = ctx.gamma(m+1)/ctx.gamma(m/3.0+0.5) + aux2 = math.sqrt(aux2) + eps5[m] = aux1*aux2*eps4 + + # COMPUTING wpfp + # See II Section 3.13 equation (59) + twenty = min(3*L-3, 21)+1 + aux = 6812*J + wpfp = ctx.mag(44*J) + for m in range(0, twenty): + wpfp = max(wpfp, ctx.mag(aux*ctx.gamma(m+1)/eps5[m])) + # COMPUTING N AND p + # See II Section + ctx.prec = wpfp + ctx.mag(t) + 20 + a = ctx.sqrt(t/(2*ctx.pi)) + N = ctx.floor(a) + p = 1-2*(a-N) + + # now we get a rounded version of p to the precision wpfp + # this possibly is not necessary + num = ctx.floor(p*(ctx.mpf(2)**wpfp)) + difference = p * (ctx.mpf(2)**wpfp)-num + if difference < 0.5: + num = num + else: + num = num+1 + p = ctx.convert(num * (ctx.mpf(2)**(-wpfp))) + + # COMPUTING THE COEFFICIENTS c[n] = cc[n] + # We shall use the notation cc[n], since there is + # a constant that is called c + # See II Section 3.14 + # We compute the coefficients and also save then in a + # cache. The bulk of the computation is passed to + # the function coef() + # + # eps6 is defined in II Section 3.13 equation (58) + eps6 = ctx.power(2*ctx.pi, J)/(ctx.gamma(J+1)*3*J) + + # Now we compute the coefficients + cc={} + cont={} + cont, pipowers = coef(ctx, J, eps6) + cc = cont.copy() # we need a copy since we have + Fp={} + for n in range(M, 3*L-2): + Fp[n] = 0 + ctx.prec = wpfp + for m in range(0,M+1): + sumP = 0 + for k in range(2*J-m-1,-1,-1): + sumP = (sumP * p) + cc[k] + Fp[m] = sumP + # preparation of the new coefficients + for k in range(0, 2*J-m-1): + cc[k] = (k+1) * cc[k+1] + + # COMPUTING THE NUMBERS d[n,k] + # See II Section 3.17 + + # First we compute the working precisions wpd[k] + # Se II equation (92) + wpd = {} + d1 = max(6, ctx.mag(40*L*L)) + d2 = 13+ctx.mag((1+abs(sigma))*A)-ctx.mag(eps4)-1 + const = ctx.ln(8/(ctx.pi*ctx.pi*a*a*B1*B1)) /2 + for n in range(0,L): + d3 = ctx.mag(ctx.sqrt(ctx.gamma(n-0.5)))-ctx.floor(n*const)+d2 + wpd[n] = max(d3,d1) + + # procedure of II Section 3.17 + ctx.prec = wpd[1]+10 + psigma = 1-(2*sigma) + d = {} + d[0,0,-2]=0; d[0,0,-1]=0; d[0,0,0]=1; d[0,0,1]=0 + d[0,-1,-2]=0; d[0,-1,-1]=0; d[0,-1,0]=1; d[0,-1,1]=0 + for n in range(1,L): + ctx.prec = wpd[n]+10 + for k in range(0,3*n//2+1): + m = 3*n-2*k + if (m!=0): + m1 = ctx.one/m + c1 = m1/4 + c2 = (psigma*m1)/2 + c3 = -(m+1) + d[0,n,k] = c3*d[0,n-1,k-2]+c1*d[0,n-1,k]+c2*d[0,n-1,k-1] + else: + d[0,n,k]=0 + for r in range(0,k): + add = d[0,n,r]*(ctx.one*ctx.fac(2*k-2*r)/ctx.fac(k-r)) + d[0,n,k] -= ((-1)**(k-r))*add + d[0,n,-2]=0; d[0,n,-1]=0; d[0,n,3*n//2+1]=0 + + for mu in range(-2,der+1): + for n in range(-2,L): + for k in range(-3,max(1,3*n//2+2)): + if ((mu<0)or (n<0) or(k<0)or (k>3*n//2)): + d[mu,n,k] = 0 + + for mu in range(1,der+1): + for n in range(0,L): + ctx.prec = wpd[n]+10 + for k in range(0,3*n//2+1): + aux=(2*mu-2)*d[mu-2,n-2,k-3]+2*(sigma+n-2)*d[mu-1,n-2,k-3] + d[mu,n,k] = aux - d[mu-1,n-1,k-1] + + # COMPUTING THE COEFFICIENTS t[k,l] + # See II Section 3.9 + # + # computing the needed wp + wptcoef = {} + wpterm = {} + ctx.prec = 15 + c1 = ctx.mag(40*(L+2)) + c2 = ctx.mag(68*(L+2)*A) + c4 = ctx.mag(B1*a*math.sqrt(ctx.pi))-1 + for k in range(0,L): + c3 = c2 - k*c4+ctx.mag(ctx.fac(k+0.5))/2. + wptcoef[k] = max(c1,c3-ctx.mag(eps4)+1)+1 +10 + wpterm[k] = max(c1,ctx.mag(L+2)+c3-ctx.mag(eps3)+1)+1 +10 + + # check of power of pi + + # computing the fortcoef[mu,k,ell] + fortcoef={} + for mu in derivatives: + for k in range(0,L): + for ell in range(-2,3*k//2+1): + fortcoef[mu,k,ell]=0 + + for mu in derivatives: + for k in range(0,L): + ctx.prec = wptcoef[k] + for ell in range(0,3*k//2+1): + fortcoef[mu,k,ell]=d[mu,k,ell]*Fp[3*k-2*ell]/pipowers[2*k-ell] + fortcoef[mu,k,ell]=fortcoef[mu,k,ell]/((2*ctx.j)**ell) + + def trunc_a(t): + wp = ctx.prec + ctx.prec = wp + 2 + aa = ctx.sqrt(t/(2*ctx.pi)) + ctx.prec = wp + return aa + + # computing the tcoef[chi,k,ell] + tcoef={} + for chi in derivatives: + for k in range(0,L): + for ell in range(-2,3*k//2+1): + tcoef[chi,k,ell]=0 + ctx.prec = wptcoef[0]+3 + aa = trunc_a(t) + la = -ctx.ln(aa) + + for chi in derivatives: + for k in range(0,L): + ctx.prec = wptcoef[k] + for ell in range(0,3*k//2+1): + tcoef[chi,k,ell] = 0 + for mu in range(0, chi+1): + tcoefter = ctx.binomial(chi,mu) * la**mu * \ + fortcoef[chi-mu,k,ell] + tcoef[chi,k,ell] += tcoefter + + # COMPUTING tv[k,ell] + # See II Section 3.8 + + # Computing the powers av[k] = a**(-k) + ctx.prec = wptcoef[0] + 2 + + # a has a good value of a. + # See II Section 3.6 + av = {} + av[0] = 1 + av[1] = av[0]/a + + ctx.prec = wptcoef[0] + for k in range(2,L): + av[k] = av[k-1] * av[1] + + # Computing the quotients + tv = {} + for chi in derivatives: + for k in range(0,L): + ctx.prec = wptcoef[k] + for ell in range(0,3*k//2+1): + tv[chi,k,ell] = tcoef[chi,k,ell]* av[k] + + # COMPUTING THE TERMS term[k] + # See II Section 3.6 + term = {} + for chi in derivatives: + for n in range(0,L): + ctx.prec = wpterm[n] + te = 0 + for k in range(0, 3*n//2+1): + te += tv[chi,n,k] + term[chi,n] = te + + # COMPUTING rssum + # See II Section 3.5 + rssum={} + ctx.prec=15 + rsbound = math.sqrt(ctx.pi) * c /(b*a) + ctx.prec=15 + wprssum = ctx.mag(4.4*((L+3)**2)*rsbound / eps2) + wprssum = max(wprssum, ctx.mag(10*(L+1))) + ctx.prec = wprssum + for chi in derivatives: + rssum[chi] = 0 + for k in range(1,L+1): + rssum[chi] += term[chi,L-k] + + # COMPUTING S3 + # See II Section 3.19 + ctx.prec = 15 + A2 = 2**(ctx.mag(rssum[0])) + eps8 = eps/(3* A2) + T = t * ctx.ln(t/(2*ctx.pi)) + wps3 = 5 + ctx.mag((1+(2/eps8)*ctx.power(a,-sigma))*T) + + ctx.prec = wps3 + tpi = t/(2*ctx.pi) + arg = (t/2)*ctx.ln(tpi)-(t/2)-ctx.pi/8 + U = ctx.expj(-arg) + a = trunc_a(t) + asigma = ctx.power(a, -sigma) + S3 = ((-1)**(N-1)) * asigma * U + + # COMPUTING S1 the zetasum + # See II Section 3.18 + ctx.prec = 15 + wpsum = 4 + ctx.mag((N+ctx.power(N,1-sigma))*ctx.ln(N)/eps1) + + ctx.prec = wpsum + 10 + ''' + # This can be improved + S1 = {} + for chi in derivatives: + S1[chi] = 0 + for n in range(1,int(N)+1): + ln = ctx.ln(n) + expn = ctx.exp(-ln*(sigma+ctx.j*t)) + for chi in derivatives: + term = ctx.power(-ln, chi)*expn + S1[chi] += term + ''' + S1 = ctx._zetasum(s, 1, int(N)-1, derivatives)[0] + + # END OF COMPUTATION + # See II Section 3.1 + ctx.prec = 15 + absS1 = abs(S1[der]) + absS2 = abs(rssum[der] * S3) + wpend = max(6, wpinitial + ctx.mag(6*(3*absS1+7*absS2))) + ctx.prec = wpend + rz = {} + for chi in derivatives: + rz[chi] = S1[chi]+rssum[chi]*S3 + ctx.prec = wpinitial + return rz + + +def z_half(ctx,t,der=0): + r""" + z_half(t,der=0) Computes Z^(der)(t) + """ + s=ctx.mpf('0.5')+ctx.j*t + wpinitial = ctx.prec + ctx.prec = 15 + tt = t/(2*ctx.pi) + wptheta = wpinitial +1 + ctx.mag(3*(tt**1.5)*ctx.ln(tt)) + wpz = wpinitial + 1 + ctx.mag(12*tt*ctx.ln(tt)) + ctx.prec = wptheta + theta = ctx.siegeltheta(t) + ctx.prec = wpz + rz = Rzeta_set(ctx,s, range(der+1)) + if der > 0: ps1 = ctx._re(ctx.psi(0,s/2)/2 - ctx.ln(ctx.pi)/2) + if der > 1: ps2 = ctx._re(ctx.j*ctx.psi(1,s/2)/4) + if der > 2: ps3 = ctx._re(-ctx.psi(2,s/2)/8) + if der > 3: ps4 = ctx._re(-ctx.j*ctx.psi(3,s/2)/16) + exptheta = ctx.expj(theta) + if der == 0: + z = 2*exptheta*rz[0] + if der == 1: + zf = 2j*exptheta + z = zf*(ps1*rz[0]+rz[1]) + if der == 2: + zf = 2 * exptheta + z = -zf*(2*rz[1]*ps1+rz[0]*ps1**2+rz[2]-ctx.j*rz[0]*ps2) + if der == 3: + zf = -2j*exptheta + z = 3*rz[1]*ps1**2+rz[0]*ps1**3+3*ps1*rz[2] + z = zf*(z-3j*rz[1]*ps2-3j*rz[0]*ps1*ps2+rz[3]-rz[0]*ps3) + if der == 4: + zf = 2*exptheta + z = 4*rz[1]*ps1**3+rz[0]*ps1**4+6*ps1**2*rz[2] + z = z-12j*rz[1]*ps1*ps2-6j*rz[0]*ps1**2*ps2-6j*rz[2]*ps2-3*rz[0]*ps2*ps2 + z = z + 4*ps1*rz[3]-4*rz[1]*ps3-4*rz[0]*ps1*ps3+rz[4]+ctx.j*rz[0]*ps4 + z = zf*z + ctx.prec = wpinitial + return ctx._re(z) + +def zeta_half(ctx, s, k=0): + """ + zeta_half(s,k=0) Computes zeta^(k)(s) when Re s = 0.5 + """ + wpinitial = ctx.prec + sigma = ctx._re(s) + t = ctx._im(s) + #--- compute wptheta, wpR, wpbasic --- + ctx.prec = 53 + # X see II Section 3.21 (109) and (110) + if sigma > 0: + X = ctx.sqrt(abs(s)) + else: + X = (2*ctx.pi)**(sigma-1) * abs(1-s)**(0.5-sigma) + # M1 see II Section 3.21 (111) and (112) + if sigma > 0: + M1 = 2*ctx.sqrt(t/(2*ctx.pi)) + else: + M1 = 4 * t * X + # T see II Section 3.21 (113) + abst = abs(0.5-s) + T = 2* abst*math.log(abst) + # computing wpbasic, wptheta, wpR see II Section 3.21 + wpbasic = max(6,3+ctx.mag(t)) + wpbasic2 = 2+ctx.mag(2.12*M1+21.2*M1*X+1.3*M1*X*T)+wpinitial+1 + wpbasic = max(wpbasic, wpbasic2) + wptheta = max(4, 3+ctx.mag(2.7*M1*X)+wpinitial+1) + wpR = 3+ctx.mag(1.1+2*X)+wpinitial+1 + ctx.prec = wptheta + theta = ctx.siegeltheta(t-ctx.j*(sigma-ctx.mpf('0.5'))) + if k > 0: ps1 = (ctx._re(ctx.psi(0,s/2)))/2 - ctx.ln(ctx.pi)/2 + if k > 1: ps2 = -(ctx._im(ctx.psi(1,s/2)))/4 + if k > 2: ps3 = -(ctx._re(ctx.psi(2,s/2)))/8 + if k > 3: ps4 = (ctx._im(ctx.psi(3,s/2)))/16 + ctx.prec = wpR + xrz = Rzeta_set(ctx,s,range(k+1)) + yrz={} + for chi in range(0,k+1): + yrz[chi] = ctx.conj(xrz[chi]) + ctx.prec = wpbasic + exptheta = ctx.expj(-2*theta) + if k==0: + zv = xrz[0]+exptheta*yrz[0] + if k==1: + zv1 = -yrz[1] - 2*yrz[0]*ps1 + zv = xrz[1] + exptheta*zv1 + if k==2: + zv1 = 4*yrz[1]*ps1+4*yrz[0]*(ps1**2)+yrz[2]+2j*yrz[0]*ps2 + zv = xrz[2]+exptheta*zv1 + if k==3: + zv1 = -12*yrz[1]*ps1**2-8*yrz[0]*ps1**3-6*yrz[2]*ps1-6j*yrz[1]*ps2 + zv1 = zv1 - 12j*yrz[0]*ps1*ps2-yrz[3]+2*yrz[0]*ps3 + zv = xrz[3]+exptheta*zv1 + if k == 4: + zv1 = 32*yrz[1]*ps1**3 +16*yrz[0]*ps1**4+24*yrz[2]*ps1**2 + zv1 = zv1 +48j*yrz[1]*ps1*ps2+48j*yrz[0]*(ps1**2)*ps2 + zv1 = zv1+12j*yrz[2]*ps2-12*yrz[0]*ps2**2+8*yrz[3]*ps1-8*yrz[1]*ps3 + zv1 = zv1-16*yrz[0]*ps1*ps3+yrz[4]-2j*yrz[0]*ps4 + zv = xrz[4]+exptheta*zv1 + ctx.prec = wpinitial + return zv + +def zeta_offline(ctx, s, k=0): + """ + Computes zeta^(k)(s) off the line + """ + wpinitial = ctx.prec + sigma = ctx._re(s) + t = ctx._im(s) + #--- compute wptheta, wpR, wpbasic --- + ctx.prec = 53 + # X see II Section 3.21 (109) and (110) + if sigma > 0: + X = ctx.power(abs(s), 0.5) + else: + X = ctx.power(2*ctx.pi, sigma-1)*ctx.power(abs(1-s),0.5-sigma) + # M1 see II Section 3.21 (111) and (112) + if (sigma > 0): + M1 = 2*ctx.sqrt(t/(2*ctx.pi)) + else: + M1 = 4 * t * X + # M2 see II Section 3.21 (111) and (112) + if (1-sigma > 0): + M2 = 2*ctx.sqrt(t/(2*ctx.pi)) + else: + M2 = 4*t*ctx.power(2*ctx.pi, -sigma)*ctx.power(abs(s),sigma-0.5) + # T see II Section 3.21 (113) + abst = abs(0.5-s) + T = 2* abst*math.log(abst) + # computing wpbasic, wptheta, wpR see II Section 3.21 + wpbasic = max(6,3+ctx.mag(t)) + wpbasic2 = 2+ctx.mag(2.12*M1+21.2*M2*X+1.3*M2*X*T)+wpinitial+1 + wpbasic = max(wpbasic, wpbasic2) + wptheta = max(4, 3+ctx.mag(2.7*M2*X)+wpinitial+1) + wpR = 3+ctx.mag(1.1+2*X)+wpinitial+1 + ctx.prec = wptheta + theta = ctx.siegeltheta(t-ctx.j*(sigma-ctx.mpf('0.5'))) + s1 = s + s2 = ctx.conj(1-s1) + ctx.prec = wpR + xrz, yrz = Rzeta_simul(ctx, s, k) + if k > 0: ps1 = (ctx.psi(0,s1/2)+ctx.psi(0,(1-s1)/2))/4 - ctx.ln(ctx.pi)/2 + if k > 1: ps2 = ctx.j*(ctx.psi(1,s1/2)-ctx.psi(1,(1-s1)/2))/8 + if k > 2: ps3 = -(ctx.psi(2,s1/2)+ctx.psi(2,(1-s1)/2))/16 + if k > 3: ps4 = -ctx.j*(ctx.psi(3,s1/2)-ctx.psi(3,(1-s1)/2))/32 + ctx.prec = wpbasic + exptheta = ctx.expj(-2*theta) + if k == 0: + zv = xrz[0]+exptheta*yrz[0] + if k == 1: + zv1 = -yrz[1]-2*yrz[0]*ps1 + zv = xrz[1]+exptheta*zv1 + if k == 2: + zv1 = 4*yrz[1]*ps1+4*yrz[0]*(ps1**2) +yrz[2]+2j*yrz[0]*ps2 + zv = xrz[2]+exptheta*zv1 + if k == 3: + zv1 = -12*yrz[1]*ps1**2 -8*yrz[0]*ps1**3-6*yrz[2]*ps1-6j*yrz[1]*ps2 + zv1 = zv1 - 12j*yrz[0]*ps1*ps2-yrz[3]+2*yrz[0]*ps3 + zv = xrz[3]+exptheta*zv1 + if k == 4: + zv1 = 32*yrz[1]*ps1**3 +16*yrz[0]*ps1**4+24*yrz[2]*ps1**2 + zv1 = zv1 +48j*yrz[1]*ps1*ps2+48j*yrz[0]*(ps1**2)*ps2 + zv1 = zv1+12j*yrz[2]*ps2-12*yrz[0]*ps2**2+8*yrz[3]*ps1-8*yrz[1]*ps3 + zv1 = zv1-16*yrz[0]*ps1*ps3+yrz[4]-2j*yrz[0]*ps4 + zv = xrz[4]+exptheta*zv1 + ctx.prec = wpinitial + return zv + +def z_offline(ctx, w, k=0): + r""" + Computes Z(w) and its derivatives off the line + """ + s = ctx.mpf('0.5')+ctx.j*w + s1 = s + s2 = ctx.conj(1-s1) + wpinitial = ctx.prec + ctx.prec = 35 + # X see II Section 3.21 (109) and (110) + # M1 see II Section 3.21 (111) and (112) + if (ctx._re(s1) >= 0): + M1 = 2*ctx.sqrt(ctx._im(s1)/(2 * ctx.pi)) + X = ctx.sqrt(abs(s1)) + else: + X = (2*ctx.pi)**(ctx._re(s1)-1) * abs(1-s1)**(0.5-ctx._re(s1)) + M1 = 4 * ctx._im(s1)*X + # M2 see II Section 3.21 (111) and (112) + if (ctx._re(s2) >= 0): + M2 = 2*ctx.sqrt(ctx._im(s2)/(2 * ctx.pi)) + else: + M2 = 4 * ctx._im(s2)*(2*ctx.pi)**(ctx._re(s2)-1)*abs(1-s2)**(0.5-ctx._re(s2)) + # T see II Section 3.21 Prop. 27 + T = 2*abs(ctx.siegeltheta(w)) + # defining some precisions + # see II Section 3.22 (115), (116), (117) + aux1 = ctx.sqrt(X) + aux2 = aux1*(M1+M2) + aux3 = 3 +wpinitial + wpbasic = max(6, 3+ctx.mag(T), ctx.mag(aux2*(26+2*T))+aux3) + wptheta = max(4,ctx.mag(2.04*aux2)+aux3) + wpR = ctx.mag(4*aux1)+aux3 + # now the computations + ctx.prec = wptheta + theta = ctx.siegeltheta(w) + ctx.prec = wpR + xrz, yrz = Rzeta_simul(ctx,s,k) + pta = 0.25 + 0.5j*w + ptb = 0.25 - 0.5j*w + if k > 0: ps1 = 0.25*(ctx.psi(0,pta)+ctx.psi(0,ptb)) - ctx.ln(ctx.pi)/2 + if k > 1: ps2 = (1j/8)*(ctx.psi(1,pta)-ctx.psi(1,ptb)) + if k > 2: ps3 = (-1./16)*(ctx.psi(2,pta)+ctx.psi(2,ptb)) + if k > 3: ps4 = (-1j/32)*(ctx.psi(3,pta)-ctx.psi(3,ptb)) + ctx.prec = wpbasic + exptheta = ctx.expj(theta) + if k == 0: + zv = exptheta*xrz[0]+yrz[0]/exptheta + j = ctx.j + if k == 1: + zv = j*exptheta*(xrz[1]+xrz[0]*ps1)-j*(yrz[1]+yrz[0]*ps1)/exptheta + if k == 2: + zv = exptheta*(-2*xrz[1]*ps1-xrz[0]*ps1**2-xrz[2]+j*xrz[0]*ps2) + zv =zv + (-2*yrz[1]*ps1-yrz[0]*ps1**2-yrz[2]-j*yrz[0]*ps2)/exptheta + if k == 3: + zv1 = -3*xrz[1]*ps1**2-xrz[0]*ps1**3-3*xrz[2]*ps1+j*3*xrz[1]*ps2 + zv1 = (zv1+ 3j*xrz[0]*ps1*ps2-xrz[3]+xrz[0]*ps3)*j*exptheta + zv2 = 3*yrz[1]*ps1**2+yrz[0]*ps1**3+3*yrz[2]*ps1+j*3*yrz[1]*ps2 + zv2 = j*(zv2 + 3j*yrz[0]*ps1*ps2+ yrz[3]-yrz[0]*ps3)/exptheta + zv = zv1+zv2 + if k == 4: + zv1 = 4*xrz[1]*ps1**3+xrz[0]*ps1**4 + 6*xrz[2]*ps1**2 + zv1 = zv1-12j*xrz[1]*ps1*ps2-6j*xrz[0]*ps1**2*ps2-6j*xrz[2]*ps2 + zv1 = zv1-3*xrz[0]*ps2*ps2+4*xrz[3]*ps1-4*xrz[1]*ps3-4*xrz[0]*ps1*ps3 + zv1 = zv1+xrz[4]+j*xrz[0]*ps4 + zv2 = 4*yrz[1]*ps1**3+yrz[0]*ps1**4 + 6*yrz[2]*ps1**2 + zv2 = zv2+12j*yrz[1]*ps1*ps2+6j*yrz[0]*ps1**2*ps2+6j*yrz[2]*ps2 + zv2 = zv2-3*yrz[0]*ps2*ps2+4*yrz[3]*ps1-4*yrz[1]*ps3-4*yrz[0]*ps1*ps3 + zv2 = zv2+yrz[4]-j*yrz[0]*ps4 + zv = exptheta*zv1+zv2/exptheta + ctx.prec = wpinitial + return zv + +@defun +def rs_zeta(ctx, s, derivative=0, **kwargs): + if derivative > 4: + raise NotImplementedError + s = ctx.convert(s) + re = ctx._re(s); im = ctx._im(s) + if im < 0: + z = ctx.conj(ctx.rs_zeta(ctx.conj(s), derivative)) + return z + critical_line = (re == 0.5) + if critical_line: + return zeta_half(ctx, s, derivative) + else: + return zeta_offline(ctx, s, derivative) + +@defun +def rs_z(ctx, w, derivative=0): + w = ctx.convert(w) + re = ctx._re(w); im = ctx._im(w) + if re < 0: + return rs_z(ctx, -w, derivative) + critical_line = (im == 0) + if critical_line : + return z_half(ctx, w, derivative) + else: + return z_offline(ctx, w, derivative) diff --git a/lib/python3.12/site-packages/mpmath/functions/signals.py b/lib/python3.12/site-packages/mpmath/functions/signals.py new file mode 100644 index 0000000000000000000000000000000000000000..6fadafb2dbb44fe19a2defa8d807d81d7c8e2789 --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/functions/signals.py @@ -0,0 +1,32 @@ +from .functions import defun_wrapped + +@defun_wrapped +def squarew(ctx, t, amplitude=1, period=1): + P = period + A = amplitude + return A*((-1)**ctx.floor(2*t/P)) + +@defun_wrapped +def trianglew(ctx, t, amplitude=1, period=1): + A = amplitude + P = period + + return 2*A*(0.5 - ctx.fabs(1 - 2*ctx.frac(t/P + 0.25))) + +@defun_wrapped +def sawtoothw(ctx, t, amplitude=1, period=1): + A = amplitude + P = period + return A*ctx.frac(t/P) + +@defun_wrapped +def unit_triangle(ctx, t, amplitude=1): + A = amplitude + if t <= -1 or t >= 1: + return ctx.zero + return A*(-ctx.fabs(t) + 1) + +@defun_wrapped +def sigmoid(ctx, t, amplitude=1): + A = amplitude + return A / (1 + ctx.exp(-t)) diff --git a/lib/python3.12/site-packages/mpmath/functions/theta.py b/lib/python3.12/site-packages/mpmath/functions/theta.py new file mode 100644 index 0000000000000000000000000000000000000000..2b3d8323a163a43186b85417a1b40f3b656c30d0 --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/functions/theta.py @@ -0,0 +1,1049 @@ +from .functions import defun, defun_wrapped + +@defun +def _jacobi_theta2(ctx, z, q): + extra1 = 10 + extra2 = 20 + # the loops below break when the fixed precision quantities + # a and b go to zero; + # right shifting small negative numbers by wp one obtains -1, not zero, + # so the condition a**2 + b**2 > MIN is used to break the loops. + MIN = 2 + if z == ctx.zero: + if (not ctx._im(q)): + wp = ctx.prec + extra1 + x = ctx.to_fixed(ctx._re(q), wp) + x2 = (x*x) >> wp + a = b = x2 + s = x2 + while abs(a) > MIN: + b = (b*x2) >> wp + a = (a*b) >> wp + s += a + s = (1 << (wp+1)) + (s << 1) + s = ctx.ldexp(s, -wp) + else: + wp = ctx.prec + extra1 + xre = ctx.to_fixed(ctx._re(q), wp) + xim = ctx.to_fixed(ctx._im(q), wp) + x2re = (xre*xre - xim*xim) >> wp + x2im = (xre*xim) >> (wp-1) + are = bre = x2re + aim = bim = x2im + sre = (1< MIN: + bre, bim = (bre * x2re - bim * x2im) >> wp, \ + (bre * x2im + bim * x2re) >> wp + are, aim = (are * bre - aim * bim) >> wp, \ + (are * bim + aim * bre) >> wp + sre += are + sim += aim + sre = (sre << 1) + sim = (sim << 1) + sre = ctx.ldexp(sre, -wp) + sim = ctx.ldexp(sim, -wp) + s = ctx.mpc(sre, sim) + else: + if (not ctx._im(q)) and (not ctx._im(z)): + wp = ctx.prec + extra1 + x = ctx.to_fixed(ctx._re(q), wp) + x2 = (x*x) >> wp + a = b = x2 + c1, s1 = ctx.cos_sin(ctx._re(z), prec=wp) + cn = c1 = ctx.to_fixed(c1, wp) + sn = s1 = ctx.to_fixed(s1, wp) + c2 = (c1*c1 - s1*s1) >> wp + s2 = (c1 * s1) >> (wp - 1) + cn, sn = (cn*c2 - sn*s2) >> wp, (sn*c2 + cn*s2) >> wp + s = c1 + ((a * cn) >> wp) + while abs(a) > MIN: + b = (b*x2) >> wp + a = (a*b) >> wp + cn, sn = (cn*c2 - sn*s2) >> wp, (sn*c2 + cn*s2) >> wp + s += (a * cn) >> wp + s = (s << 1) + s = ctx.ldexp(s, -wp) + s *= ctx.nthroot(q, 4) + return s + # case z real, q complex + elif not ctx._im(z): + wp = ctx.prec + extra2 + xre = ctx.to_fixed(ctx._re(q), wp) + xim = ctx.to_fixed(ctx._im(q), wp) + x2re = (xre*xre - xim*xim) >> wp + x2im = (xre*xim) >> (wp - 1) + are = bre = x2re + aim = bim = x2im + c1, s1 = ctx.cos_sin(ctx._re(z), prec=wp) + cn = c1 = ctx.to_fixed(c1, wp) + sn = s1 = ctx.to_fixed(s1, wp) + c2 = (c1*c1 - s1*s1) >> wp + s2 = (c1 * s1) >> (wp - 1) + cn, sn = (cn*c2 - sn*s2) >> wp, (sn*c2 + cn*s2) >> wp + sre = c1 + ((are * cn) >> wp) + sim = ((aim * cn) >> wp) + while are**2 + aim**2 > MIN: + bre, bim = (bre * x2re - bim * x2im) >> wp, \ + (bre * x2im + bim * x2re) >> wp + are, aim = (are * bre - aim * bim) >> wp, \ + (are * bim + aim * bre) >> wp + cn, sn = (cn*c2 - sn*s2) >> wp, (sn*c2 + cn*s2) >> wp + sre += ((are * cn) >> wp) + sim += ((aim * cn) >> wp) + sre = (sre << 1) + sim = (sim << 1) + sre = ctx.ldexp(sre, -wp) + sim = ctx.ldexp(sim, -wp) + s = ctx.mpc(sre, sim) + #case z complex, q real + elif not ctx._im(q): + wp = ctx.prec + extra2 + x = ctx.to_fixed(ctx._re(q), wp) + x2 = (x*x) >> wp + a = b = x2 + prec0 = ctx.prec + ctx.prec = wp + c1, s1 = ctx.cos_sin(z) + ctx.prec = prec0 + cnre = c1re = ctx.to_fixed(ctx._re(c1), wp) + cnim = c1im = ctx.to_fixed(ctx._im(c1), wp) + snre = s1re = ctx.to_fixed(ctx._re(s1), wp) + snim = s1im = ctx.to_fixed(ctx._im(s1), wp) + #c2 = (c1*c1 - s1*s1) >> wp + c2re = (c1re*c1re - c1im*c1im - s1re*s1re + s1im*s1im) >> wp + c2im = (c1re*c1im - s1re*s1im) >> (wp - 1) + #s2 = (c1 * s1) >> (wp - 1) + s2re = (c1re*s1re - c1im*s1im) >> (wp - 1) + s2im = (c1re*s1im + c1im*s1re) >> (wp - 1) + #cn, sn = (cn*c2 - sn*s2) >> wp, (sn*c2 + cn*s2) >> wp + t1 = (cnre*c2re - cnim*c2im - snre*s2re + snim*s2im) >> wp + t2 = (cnre*c2im + cnim*c2re - snre*s2im - snim*s2re) >> wp + t3 = (snre*c2re - snim*c2im + cnre*s2re - cnim*s2im) >> wp + t4 = (snre*c2im + snim*c2re + cnre*s2im + cnim*s2re) >> wp + cnre = t1 + cnim = t2 + snre = t3 + snim = t4 + sre = c1re + ((a * cnre) >> wp) + sim = c1im + ((a * cnim) >> wp) + while abs(a) > MIN: + b = (b*x2) >> wp + a = (a*b) >> wp + t1 = (cnre*c2re - cnim*c2im - snre*s2re + snim*s2im) >> wp + t2 = (cnre*c2im + cnim*c2re - snre*s2im - snim*s2re) >> wp + t3 = (snre*c2re - snim*c2im + cnre*s2re - cnim*s2im) >> wp + t4 = (snre*c2im + snim*c2re + cnre*s2im + cnim*s2re) >> wp + cnre = t1 + cnim = t2 + snre = t3 + snim = t4 + sre += ((a * cnre) >> wp) + sim += ((a * cnim) >> wp) + sre = (sre << 1) + sim = (sim << 1) + sre = ctx.ldexp(sre, -wp) + sim = ctx.ldexp(sim, -wp) + s = ctx.mpc(sre, sim) + # case z and q complex + else: + wp = ctx.prec + extra2 + xre = ctx.to_fixed(ctx._re(q), wp) + xim = ctx.to_fixed(ctx._im(q), wp) + x2re = (xre*xre - xim*xim) >> wp + x2im = (xre*xim) >> (wp - 1) + are = bre = x2re + aim = bim = x2im + prec0 = ctx.prec + ctx.prec = wp + # cos(z), sin(z) with z complex + c1, s1 = ctx.cos_sin(z) + ctx.prec = prec0 + cnre = c1re = ctx.to_fixed(ctx._re(c1), wp) + cnim = c1im = ctx.to_fixed(ctx._im(c1), wp) + snre = s1re = ctx.to_fixed(ctx._re(s1), wp) + snim = s1im = ctx.to_fixed(ctx._im(s1), wp) + c2re = (c1re*c1re - c1im*c1im - s1re*s1re + s1im*s1im) >> wp + c2im = (c1re*c1im - s1re*s1im) >> (wp - 1) + s2re = (c1re*s1re - c1im*s1im) >> (wp - 1) + s2im = (c1re*s1im + c1im*s1re) >> (wp - 1) + t1 = (cnre*c2re - cnim*c2im - snre*s2re + snim*s2im) >> wp + t2 = (cnre*c2im + cnim*c2re - snre*s2im - snim*s2re) >> wp + t3 = (snre*c2re - snim*c2im + cnre*s2re - cnim*s2im) >> wp + t4 = (snre*c2im + snim*c2re + cnre*s2im + cnim*s2re) >> wp + cnre = t1 + cnim = t2 + snre = t3 + snim = t4 + n = 1 + termre = c1re + termim = c1im + sre = c1re + ((are * cnre - aim * cnim) >> wp) + sim = c1im + ((are * cnim + aim * cnre) >> wp) + n = 3 + termre = ((are * cnre - aim * cnim) >> wp) + termim = ((are * cnim + aim * cnre) >> wp) + sre = c1re + ((are * cnre - aim * cnim) >> wp) + sim = c1im + ((are * cnim + aim * cnre) >> wp) + n = 5 + while are**2 + aim**2 > MIN: + bre, bim = (bre * x2re - bim * x2im) >> wp, \ + (bre * x2im + bim * x2re) >> wp + are, aim = (are * bre - aim * bim) >> wp, \ + (are * bim + aim * bre) >> wp + #cn, sn = (cn*c1 - sn*s1) >> wp, (sn*c1 + cn*s1) >> wp + t1 = (cnre*c2re - cnim*c2im - snre*s2re + snim*s2im) >> wp + t2 = (cnre*c2im + cnim*c2re - snre*s2im - snim*s2re) >> wp + t3 = (snre*c2re - snim*c2im + cnre*s2re - cnim*s2im) >> wp + t4 = (snre*c2im + snim*c2re + cnre*s2im + cnim*s2re) >> wp + cnre = t1 + cnim = t2 + snre = t3 + snim = t4 + termre = ((are * cnre - aim * cnim) >> wp) + termim = ((aim * cnre + are * cnim) >> wp) + sre += ((are * cnre - aim * cnim) >> wp) + sim += ((aim * cnre + are * cnim) >> wp) + n += 2 + sre = (sre << 1) + sim = (sim << 1) + sre = ctx.ldexp(sre, -wp) + sim = ctx.ldexp(sim, -wp) + s = ctx.mpc(sre, sim) + s *= ctx.nthroot(q, 4) + return s + +@defun +def _djacobi_theta2(ctx, z, q, nd): + MIN = 2 + extra1 = 10 + extra2 = 20 + if (not ctx._im(q)) and (not ctx._im(z)): + wp = ctx.prec + extra1 + x = ctx.to_fixed(ctx._re(q), wp) + x2 = (x*x) >> wp + a = b = x2 + c1, s1 = ctx.cos_sin(ctx._re(z), prec=wp) + cn = c1 = ctx.to_fixed(c1, wp) + sn = s1 = ctx.to_fixed(s1, wp) + c2 = (c1*c1 - s1*s1) >> wp + s2 = (c1 * s1) >> (wp - 1) + cn, sn = (cn*c2 - sn*s2) >> wp, (sn*c2 + cn*s2) >> wp + if (nd&1): + s = s1 + ((a * sn * 3**nd) >> wp) + else: + s = c1 + ((a * cn * 3**nd) >> wp) + n = 2 + while abs(a) > MIN: + b = (b*x2) >> wp + a = (a*b) >> wp + cn, sn = (cn*c2 - sn*s2) >> wp, (sn*c2 + cn*s2) >> wp + if nd&1: + s += (a * sn * (2*n+1)**nd) >> wp + else: + s += (a * cn * (2*n+1)**nd) >> wp + n += 1 + s = -(s << 1) + s = ctx.ldexp(s, -wp) + # case z real, q complex + elif not ctx._im(z): + wp = ctx.prec + extra2 + xre = ctx.to_fixed(ctx._re(q), wp) + xim = ctx.to_fixed(ctx._im(q), wp) + x2re = (xre*xre - xim*xim) >> wp + x2im = (xre*xim) >> (wp - 1) + are = bre = x2re + aim = bim = x2im + c1, s1 = ctx.cos_sin(ctx._re(z), prec=wp) + cn = c1 = ctx.to_fixed(c1, wp) + sn = s1 = ctx.to_fixed(s1, wp) + c2 = (c1*c1 - s1*s1) >> wp + s2 = (c1 * s1) >> (wp - 1) + cn, sn = (cn*c2 - sn*s2) >> wp, (sn*c2 + cn*s2) >> wp + if (nd&1): + sre = s1 + ((are * sn * 3**nd) >> wp) + sim = ((aim * sn * 3**nd) >> wp) + else: + sre = c1 + ((are * cn * 3**nd) >> wp) + sim = ((aim * cn * 3**nd) >> wp) + n = 5 + while are**2 + aim**2 > MIN: + bre, bim = (bre * x2re - bim * x2im) >> wp, \ + (bre * x2im + bim * x2re) >> wp + are, aim = (are * bre - aim * bim) >> wp, \ + (are * bim + aim * bre) >> wp + cn, sn = (cn*c2 - sn*s2) >> wp, (sn*c2 + cn*s2) >> wp + + if (nd&1): + sre += ((are * sn * n**nd) >> wp) + sim += ((aim * sn * n**nd) >> wp) + else: + sre += ((are * cn * n**nd) >> wp) + sim += ((aim * cn * n**nd) >> wp) + n += 2 + sre = -(sre << 1) + sim = -(sim << 1) + sre = ctx.ldexp(sre, -wp) + sim = ctx.ldexp(sim, -wp) + s = ctx.mpc(sre, sim) + #case z complex, q real + elif not ctx._im(q): + wp = ctx.prec + extra2 + x = ctx.to_fixed(ctx._re(q), wp) + x2 = (x*x) >> wp + a = b = x2 + prec0 = ctx.prec + ctx.prec = wp + c1, s1 = ctx.cos_sin(z) + ctx.prec = prec0 + cnre = c1re = ctx.to_fixed(ctx._re(c1), wp) + cnim = c1im = ctx.to_fixed(ctx._im(c1), wp) + snre = s1re = ctx.to_fixed(ctx._re(s1), wp) + snim = s1im = ctx.to_fixed(ctx._im(s1), wp) + #c2 = (c1*c1 - s1*s1) >> wp + c2re = (c1re*c1re - c1im*c1im - s1re*s1re + s1im*s1im) >> wp + c2im = (c1re*c1im - s1re*s1im) >> (wp - 1) + #s2 = (c1 * s1) >> (wp - 1) + s2re = (c1re*s1re - c1im*s1im) >> (wp - 1) + s2im = (c1re*s1im + c1im*s1re) >> (wp - 1) + #cn, sn = (cn*c2 - sn*s2) >> wp, (sn*c2 + cn*s2) >> wp + t1 = (cnre*c2re - cnim*c2im - snre*s2re + snim*s2im) >> wp + t2 = (cnre*c2im + cnim*c2re - snre*s2im - snim*s2re) >> wp + t3 = (snre*c2re - snim*c2im + cnre*s2re - cnim*s2im) >> wp + t4 = (snre*c2im + snim*c2re + cnre*s2im + cnim*s2re) >> wp + cnre = t1 + cnim = t2 + snre = t3 + snim = t4 + if (nd&1): + sre = s1re + ((a * snre * 3**nd) >> wp) + sim = s1im + ((a * snim * 3**nd) >> wp) + else: + sre = c1re + ((a * cnre * 3**nd) >> wp) + sim = c1im + ((a * cnim * 3**nd) >> wp) + n = 5 + while abs(a) > MIN: + b = (b*x2) >> wp + a = (a*b) >> wp + t1 = (cnre*c2re - cnim*c2im - snre*s2re + snim*s2im) >> wp + t2 = (cnre*c2im + cnim*c2re - snre*s2im - snim*s2re) >> wp + t3 = (snre*c2re - snim*c2im + cnre*s2re - cnim*s2im) >> wp + t4 = (snre*c2im + snim*c2re + cnre*s2im + cnim*s2re) >> wp + cnre = t1 + cnim = t2 + snre = t3 + snim = t4 + if (nd&1): + sre += ((a * snre * n**nd) >> wp) + sim += ((a * snim * n**nd) >> wp) + else: + sre += ((a * cnre * n**nd) >> wp) + sim += ((a * cnim * n**nd) >> wp) + n += 2 + sre = -(sre << 1) + sim = -(sim << 1) + sre = ctx.ldexp(sre, -wp) + sim = ctx.ldexp(sim, -wp) + s = ctx.mpc(sre, sim) + # case z and q complex + else: + wp = ctx.prec + extra2 + xre = ctx.to_fixed(ctx._re(q), wp) + xim = ctx.to_fixed(ctx._im(q), wp) + x2re = (xre*xre - xim*xim) >> wp + x2im = (xre*xim) >> (wp - 1) + are = bre = x2re + aim = bim = x2im + prec0 = ctx.prec + ctx.prec = wp + # cos(2*z), sin(2*z) with z complex + c1, s1 = ctx.cos_sin(z) + ctx.prec = prec0 + cnre = c1re = ctx.to_fixed(ctx._re(c1), wp) + cnim = c1im = ctx.to_fixed(ctx._im(c1), wp) + snre = s1re = ctx.to_fixed(ctx._re(s1), wp) + snim = s1im = ctx.to_fixed(ctx._im(s1), wp) + c2re = (c1re*c1re - c1im*c1im - s1re*s1re + s1im*s1im) >> wp + c2im = (c1re*c1im - s1re*s1im) >> (wp - 1) + s2re = (c1re*s1re - c1im*s1im) >> (wp - 1) + s2im = (c1re*s1im + c1im*s1re) >> (wp - 1) + t1 = (cnre*c2re - cnim*c2im - snre*s2re + snim*s2im) >> wp + t2 = (cnre*c2im + cnim*c2re - snre*s2im - snim*s2re) >> wp + t3 = (snre*c2re - snim*c2im + cnre*s2re - cnim*s2im) >> wp + t4 = (snre*c2im + snim*c2re + cnre*s2im + cnim*s2re) >> wp + cnre = t1 + cnim = t2 + snre = t3 + snim = t4 + if (nd&1): + sre = s1re + (((are * snre - aim * snim) * 3**nd) >> wp) + sim = s1im + (((are * snim + aim * snre)* 3**nd) >> wp) + else: + sre = c1re + (((are * cnre - aim * cnim) * 3**nd) >> wp) + sim = c1im + (((are * cnim + aim * cnre)* 3**nd) >> wp) + n = 5 + while are**2 + aim**2 > MIN: + bre, bim = (bre * x2re - bim * x2im) >> wp, \ + (bre * x2im + bim * x2re) >> wp + are, aim = (are * bre - aim * bim) >> wp, \ + (are * bim + aim * bre) >> wp + #cn, sn = (cn*c1 - sn*s1) >> wp, (sn*c1 + cn*s1) >> wp + t1 = (cnre*c2re - cnim*c2im - snre*s2re + snim*s2im) >> wp + t2 = (cnre*c2im + cnim*c2re - snre*s2im - snim*s2re) >> wp + t3 = (snre*c2re - snim*c2im + cnre*s2re - cnim*s2im) >> wp + t4 = (snre*c2im + snim*c2re + cnre*s2im + cnim*s2re) >> wp + cnre = t1 + cnim = t2 + snre = t3 + snim = t4 + if (nd&1): + sre += (((are * snre - aim * snim) * n**nd) >> wp) + sim += (((aim * snre + are * snim) * n**nd) >> wp) + else: + sre += (((are * cnre - aim * cnim) * n**nd) >> wp) + sim += (((aim * cnre + are * cnim) * n**nd) >> wp) + n += 2 + sre = -(sre << 1) + sim = -(sim << 1) + sre = ctx.ldexp(sre, -wp) + sim = ctx.ldexp(sim, -wp) + s = ctx.mpc(sre, sim) + s *= ctx.nthroot(q, 4) + if (nd&1): + return (-1)**(nd//2) * s + else: + return (-1)**(1 + nd//2) * s + +@defun +def _jacobi_theta3(ctx, z, q): + extra1 = 10 + extra2 = 20 + MIN = 2 + if z == ctx.zero: + if not ctx._im(q): + wp = ctx.prec + extra1 + x = ctx.to_fixed(ctx._re(q), wp) + s = x + a = b = x + x2 = (x*x) >> wp + while abs(a) > MIN: + b = (b*x2) >> wp + a = (a*b) >> wp + s += a + s = (1 << wp) + (s << 1) + s = ctx.ldexp(s, -wp) + return s + else: + wp = ctx.prec + extra1 + xre = ctx.to_fixed(ctx._re(q), wp) + xim = ctx.to_fixed(ctx._im(q), wp) + x2re = (xre*xre - xim*xim) >> wp + x2im = (xre*xim) >> (wp - 1) + sre = are = bre = xre + sim = aim = bim = xim + while are**2 + aim**2 > MIN: + bre, bim = (bre * x2re - bim * x2im) >> wp, \ + (bre * x2im + bim * x2re) >> wp + are, aim = (are * bre - aim * bim) >> wp, \ + (are * bim + aim * bre) >> wp + sre += are + sim += aim + sre = (1 << wp) + (sre << 1) + sim = (sim << 1) + sre = ctx.ldexp(sre, -wp) + sim = ctx.ldexp(sim, -wp) + s = ctx.mpc(sre, sim) + return s + else: + if (not ctx._im(q)) and (not ctx._im(z)): + s = 0 + wp = ctx.prec + extra1 + x = ctx.to_fixed(ctx._re(q), wp) + a = b = x + x2 = (x*x) >> wp + c1, s1 = ctx.cos_sin(ctx._re(z)*2, prec=wp) + c1 = ctx.to_fixed(c1, wp) + s1 = ctx.to_fixed(s1, wp) + cn = c1 + sn = s1 + s += (a * cn) >> wp + while abs(a) > MIN: + b = (b*x2) >> wp + a = (a*b) >> wp + cn, sn = (cn*c1 - sn*s1) >> wp, (sn*c1 + cn*s1) >> wp + s += (a * cn) >> wp + s = (1 << wp) + (s << 1) + s = ctx.ldexp(s, -wp) + return s + # case z real, q complex + elif not ctx._im(z): + wp = ctx.prec + extra2 + xre = ctx.to_fixed(ctx._re(q), wp) + xim = ctx.to_fixed(ctx._im(q), wp) + x2re = (xre*xre - xim*xim) >> wp + x2im = (xre*xim) >> (wp - 1) + are = bre = xre + aim = bim = xim + c1, s1 = ctx.cos_sin(ctx._re(z)*2, prec=wp) + c1 = ctx.to_fixed(c1, wp) + s1 = ctx.to_fixed(s1, wp) + cn = c1 + sn = s1 + sre = (are * cn) >> wp + sim = (aim * cn) >> wp + while are**2 + aim**2 > MIN: + bre, bim = (bre * x2re - bim * x2im) >> wp, \ + (bre * x2im + bim * x2re) >> wp + are, aim = (are * bre - aim * bim) >> wp, \ + (are * bim + aim * bre) >> wp + cn, sn = (cn*c1 - sn*s1) >> wp, (sn*c1 + cn*s1) >> wp + sre += (are * cn) >> wp + sim += (aim * cn) >> wp + sre = (1 << wp) + (sre << 1) + sim = (sim << 1) + sre = ctx.ldexp(sre, -wp) + sim = ctx.ldexp(sim, -wp) + s = ctx.mpc(sre, sim) + return s + #case z complex, q real + elif not ctx._im(q): + wp = ctx.prec + extra2 + x = ctx.to_fixed(ctx._re(q), wp) + a = b = x + x2 = (x*x) >> wp + prec0 = ctx.prec + ctx.prec = wp + c1, s1 = ctx.cos_sin(2*z) + ctx.prec = prec0 + cnre = c1re = ctx.to_fixed(ctx._re(c1), wp) + cnim = c1im = ctx.to_fixed(ctx._im(c1), wp) + snre = s1re = ctx.to_fixed(ctx._re(s1), wp) + snim = s1im = ctx.to_fixed(ctx._im(s1), wp) + sre = (a * cnre) >> wp + sim = (a * cnim) >> wp + while abs(a) > MIN: + b = (b*x2) >> wp + a = (a*b) >> wp + t1 = (cnre*c1re - cnim*c1im - snre*s1re + snim*s1im) >> wp + t2 = (cnre*c1im + cnim*c1re - snre*s1im - snim*s1re) >> wp + t3 = (snre*c1re - snim*c1im + cnre*s1re - cnim*s1im) >> wp + t4 = (snre*c1im + snim*c1re + cnre*s1im + cnim*s1re) >> wp + cnre = t1 + cnim = t2 + snre = t3 + snim = t4 + sre += (a * cnre) >> wp + sim += (a * cnim) >> wp + sre = (1 << wp) + (sre << 1) + sim = (sim << 1) + sre = ctx.ldexp(sre, -wp) + sim = ctx.ldexp(sim, -wp) + s = ctx.mpc(sre, sim) + return s + # case z and q complex + else: + wp = ctx.prec + extra2 + xre = ctx.to_fixed(ctx._re(q), wp) + xim = ctx.to_fixed(ctx._im(q), wp) + x2re = (xre*xre - xim*xim) >> wp + x2im = (xre*xim) >> (wp - 1) + are = bre = xre + aim = bim = xim + prec0 = ctx.prec + ctx.prec = wp + # cos(2*z), sin(2*z) with z complex + c1, s1 = ctx.cos_sin(2*z) + ctx.prec = prec0 + cnre = c1re = ctx.to_fixed(ctx._re(c1), wp) + cnim = c1im = ctx.to_fixed(ctx._im(c1), wp) + snre = s1re = ctx.to_fixed(ctx._re(s1), wp) + snim = s1im = ctx.to_fixed(ctx._im(s1), wp) + sre = (are * cnre - aim * cnim) >> wp + sim = (aim * cnre + are * cnim) >> wp + while are**2 + aim**2 > MIN: + bre, bim = (bre * x2re - bim * x2im) >> wp, \ + (bre * x2im + bim * x2re) >> wp + are, aim = (are * bre - aim * bim) >> wp, \ + (are * bim + aim * bre) >> wp + t1 = (cnre*c1re - cnim*c1im - snre*s1re + snim*s1im) >> wp + t2 = (cnre*c1im + cnim*c1re - snre*s1im - snim*s1re) >> wp + t3 = (snre*c1re - snim*c1im + cnre*s1re - cnim*s1im) >> wp + t4 = (snre*c1im + snim*c1re + cnre*s1im + cnim*s1re) >> wp + cnre = t1 + cnim = t2 + snre = t3 + snim = t4 + sre += (are * cnre - aim * cnim) >> wp + sim += (aim * cnre + are * cnim) >> wp + sre = (1 << wp) + (sre << 1) + sim = (sim << 1) + sre = ctx.ldexp(sre, -wp) + sim = ctx.ldexp(sim, -wp) + s = ctx.mpc(sre, sim) + return s + +@defun +def _djacobi_theta3(ctx, z, q, nd): + """nd=1,2,3 order of the derivative with respect to z""" + MIN = 2 + extra1 = 10 + extra2 = 20 + if (not ctx._im(q)) and (not ctx._im(z)): + s = 0 + wp = ctx.prec + extra1 + x = ctx.to_fixed(ctx._re(q), wp) + a = b = x + x2 = (x*x) >> wp + c1, s1 = ctx.cos_sin(ctx._re(z)*2, prec=wp) + c1 = ctx.to_fixed(c1, wp) + s1 = ctx.to_fixed(s1, wp) + cn = c1 + sn = s1 + if (nd&1): + s += (a * sn) >> wp + else: + s += (a * cn) >> wp + n = 2 + while abs(a) > MIN: + b = (b*x2) >> wp + a = (a*b) >> wp + cn, sn = (cn*c1 - sn*s1) >> wp, (sn*c1 + cn*s1) >> wp + if nd&1: + s += (a * sn * n**nd) >> wp + else: + s += (a * cn * n**nd) >> wp + n += 1 + s = -(s << (nd+1)) + s = ctx.ldexp(s, -wp) + # case z real, q complex + elif not ctx._im(z): + wp = ctx.prec + extra2 + xre = ctx.to_fixed(ctx._re(q), wp) + xim = ctx.to_fixed(ctx._im(q), wp) + x2re = (xre*xre - xim*xim) >> wp + x2im = (xre*xim) >> (wp - 1) + are = bre = xre + aim = bim = xim + c1, s1 = ctx.cos_sin(ctx._re(z)*2, prec=wp) + c1 = ctx.to_fixed(c1, wp) + s1 = ctx.to_fixed(s1, wp) + cn = c1 + sn = s1 + if (nd&1): + sre = (are * sn) >> wp + sim = (aim * sn) >> wp + else: + sre = (are * cn) >> wp + sim = (aim * cn) >> wp + n = 2 + while are**2 + aim**2 > MIN: + bre, bim = (bre * x2re - bim * x2im) >> wp, \ + (bre * x2im + bim * x2re) >> wp + are, aim = (are * bre - aim * bim) >> wp, \ + (are * bim + aim * bre) >> wp + cn, sn = (cn*c1 - sn*s1) >> wp, (sn*c1 + cn*s1) >> wp + if nd&1: + sre += (are * sn * n**nd) >> wp + sim += (aim * sn * n**nd) >> wp + else: + sre += (are * cn * n**nd) >> wp + sim += (aim * cn * n**nd) >> wp + n += 1 + sre = -(sre << (nd+1)) + sim = -(sim << (nd+1)) + sre = ctx.ldexp(sre, -wp) + sim = ctx.ldexp(sim, -wp) + s = ctx.mpc(sre, sim) + #case z complex, q real + elif not ctx._im(q): + wp = ctx.prec + extra2 + x = ctx.to_fixed(ctx._re(q), wp) + a = b = x + x2 = (x*x) >> wp + prec0 = ctx.prec + ctx.prec = wp + c1, s1 = ctx.cos_sin(2*z) + ctx.prec = prec0 + cnre = c1re = ctx.to_fixed(ctx._re(c1), wp) + cnim = c1im = ctx.to_fixed(ctx._im(c1), wp) + snre = s1re = ctx.to_fixed(ctx._re(s1), wp) + snim = s1im = ctx.to_fixed(ctx._im(s1), wp) + if (nd&1): + sre = (a * snre) >> wp + sim = (a * snim) >> wp + else: + sre = (a * cnre) >> wp + sim = (a * cnim) >> wp + n = 2 + while abs(a) > MIN: + b = (b*x2) >> wp + a = (a*b) >> wp + t1 = (cnre*c1re - cnim*c1im - snre*s1re + snim*s1im) >> wp + t2 = (cnre*c1im + cnim*c1re - snre*s1im - snim*s1re) >> wp + t3 = (snre*c1re - snim*c1im + cnre*s1re - cnim*s1im) >> wp + t4 = (snre*c1im + snim*c1re + cnre*s1im + cnim*s1re) >> wp + cnre = t1 + cnim = t2 + snre = t3 + snim = t4 + if (nd&1): + sre += (a * snre * n**nd) >> wp + sim += (a * snim * n**nd) >> wp + else: + sre += (a * cnre * n**nd) >> wp + sim += (a * cnim * n**nd) >> wp + n += 1 + sre = -(sre << (nd+1)) + sim = -(sim << (nd+1)) + sre = ctx.ldexp(sre, -wp) + sim = ctx.ldexp(sim, -wp) + s = ctx.mpc(sre, sim) + # case z and q complex + else: + wp = ctx.prec + extra2 + xre = ctx.to_fixed(ctx._re(q), wp) + xim = ctx.to_fixed(ctx._im(q), wp) + x2re = (xre*xre - xim*xim) >> wp + x2im = (xre*xim) >> (wp - 1) + are = bre = xre + aim = bim = xim + prec0 = ctx.prec + ctx.prec = wp + # cos(2*z), sin(2*z) with z complex + c1, s1 = ctx.cos_sin(2*z) + ctx.prec = prec0 + cnre = c1re = ctx.to_fixed(ctx._re(c1), wp) + cnim = c1im = ctx.to_fixed(ctx._im(c1), wp) + snre = s1re = ctx.to_fixed(ctx._re(s1), wp) + snim = s1im = ctx.to_fixed(ctx._im(s1), wp) + if (nd&1): + sre = (are * snre - aim * snim) >> wp + sim = (aim * snre + are * snim) >> wp + else: + sre = (are * cnre - aim * cnim) >> wp + sim = (aim * cnre + are * cnim) >> wp + n = 2 + while are**2 + aim**2 > MIN: + bre, bim = (bre * x2re - bim * x2im) >> wp, \ + (bre * x2im + bim * x2re) >> wp + are, aim = (are * bre - aim * bim) >> wp, \ + (are * bim + aim * bre) >> wp + t1 = (cnre*c1re - cnim*c1im - snre*s1re + snim*s1im) >> wp + t2 = (cnre*c1im + cnim*c1re - snre*s1im - snim*s1re) >> wp + t3 = (snre*c1re - snim*c1im + cnre*s1re - cnim*s1im) >> wp + t4 = (snre*c1im + snim*c1re + cnre*s1im + cnim*s1re) >> wp + cnre = t1 + cnim = t2 + snre = t3 + snim = t4 + if(nd&1): + sre += ((are * snre - aim * snim) * n**nd) >> wp + sim += ((aim * snre + are * snim) * n**nd) >> wp + else: + sre += ((are * cnre - aim * cnim) * n**nd) >> wp + sim += ((aim * cnre + are * cnim) * n**nd) >> wp + n += 1 + sre = -(sre << (nd+1)) + sim = -(sim << (nd+1)) + sre = ctx.ldexp(sre, -wp) + sim = ctx.ldexp(sim, -wp) + s = ctx.mpc(sre, sim) + if (nd&1): + return (-1)**(nd//2) * s + else: + return (-1)**(1 + nd//2) * s + +@defun +def _jacobi_theta2a(ctx, z, q): + """ + case ctx._im(z) != 0 + theta(2, z, q) = + q**1/4 * Sum(q**(n*n + n) * exp(j*(2*n + 1)*z), n=-inf, inf) + max term for minimum (2*n+1)*log(q).real - 2* ctx._im(z) + n0 = int(ctx._im(z)/log(q).real - 1/2) + theta(2, z, q) = + q**1/4 * Sum(q**(n*n + n) * exp(j*(2*n + 1)*z), n=n0, inf) + + q**1/4 * Sum(q**(n*n + n) * exp(j*(2*n + 1)*z), n, n0-1, -inf) + """ + n = n0 = int(ctx._im(z)/ctx._re(ctx.log(q)) - 1/2) + e2 = ctx.expj(2*z) + e = e0 = ctx.expj((2*n+1)*z) + a = q**(n*n + n) + # leading term + term = a * e + s = term + eps1 = ctx.eps*abs(term) + while 1: + n += 1 + e = e * e2 + term = q**(n*n + n) * e + if abs(term) < eps1: + break + s += term + e = e0 + e2 = ctx.expj(-2*z) + n = n0 + while 1: + n -= 1 + e = e * e2 + term = q**(n*n + n) * e + if abs(term) < eps1: + break + s += term + s = s * ctx.nthroot(q, 4) + return s + +@defun +def _jacobi_theta3a(ctx, z, q): + """ + case ctx._im(z) != 0 + theta3(z, q) = Sum(q**(n*n) * exp(j*2*n*z), n, -inf, inf) + max term for n*abs(log(q).real) + ctx._im(z) ~= 0 + n0 = int(- ctx._im(z)/abs(log(q).real)) + """ + n = n0 = int(-ctx._im(z)/abs(ctx._re(ctx.log(q)))) + e2 = ctx.expj(2*z) + e = e0 = ctx.expj(2*n*z) + s = term = q**(n*n) * e + eps1 = ctx.eps*abs(term) + while 1: + n += 1 + e = e * e2 + term = q**(n*n) * e + if abs(term) < eps1: + break + s += term + e = e0 + e2 = ctx.expj(-2*z) + n = n0 + while 1: + n -= 1 + e = e * e2 + term = q**(n*n) * e + if abs(term) < eps1: + break + s += term + return s + +@defun +def _djacobi_theta2a(ctx, z, q, nd): + """ + case ctx._im(z) != 0 + dtheta(2, z, q, nd) = + j* q**1/4 * Sum(q**(n*n + n) * (2*n+1)*exp(j*(2*n + 1)*z), n=-inf, inf) + max term for (2*n0+1)*log(q).real - 2* ctx._im(z) ~= 0 + n0 = int(ctx._im(z)/log(q).real - 1/2) + """ + n = n0 = int(ctx._im(z)/ctx._re(ctx.log(q)) - 1/2) + e2 = ctx.expj(2*z) + e = e0 = ctx.expj((2*n + 1)*z) + a = q**(n*n + n) + # leading term + term = (2*n+1)**nd * a * e + s = term + eps1 = ctx.eps*abs(term) + while 1: + n += 1 + e = e * e2 + term = (2*n+1)**nd * q**(n*n + n) * e + if abs(term) < eps1: + break + s += term + e = e0 + e2 = ctx.expj(-2*z) + n = n0 + while 1: + n -= 1 + e = e * e2 + term = (2*n+1)**nd * q**(n*n + n) * e + if abs(term) < eps1: + break + s += term + return ctx.j**nd * s * ctx.nthroot(q, 4) + +@defun +def _djacobi_theta3a(ctx, z, q, nd): + """ + case ctx._im(z) != 0 + djtheta3(z, q, nd) = (2*j)**nd * + Sum(q**(n*n) * n**nd * exp(j*2*n*z), n, -inf, inf) + max term for minimum n*abs(log(q).real) + ctx._im(z) + """ + n = n0 = int(-ctx._im(z)/abs(ctx._re(ctx.log(q)))) + e2 = ctx.expj(2*z) + e = e0 = ctx.expj(2*n*z) + a = q**(n*n) * e + s = term = n**nd * a + if n != 0: + eps1 = ctx.eps*abs(term) + else: + eps1 = ctx.eps*abs(a) + while 1: + n += 1 + e = e * e2 + a = q**(n*n) * e + term = n**nd * a + if n != 0: + aterm = abs(term) + else: + aterm = abs(a) + if aterm < eps1: + break + s += term + e = e0 + e2 = ctx.expj(-2*z) + n = n0 + while 1: + n -= 1 + e = e * e2 + a = q**(n*n) * e + term = n**nd * a + if n != 0: + aterm = abs(term) + else: + aterm = abs(a) + if aterm < eps1: + break + s += term + return (2*ctx.j)**nd * s + +@defun +def jtheta(ctx, n, z, q, derivative=0): + if derivative: + return ctx._djtheta(n, z, q, derivative) + + z = ctx.convert(z) + q = ctx.convert(q) + + # Implementation note + # If ctx._im(z) is close to zero, _jacobi_theta2 and _jacobi_theta3 + # are used, + # which compute the series starting from n=0 using fixed precision + # numbers; + # otherwise _jacobi_theta2a and _jacobi_theta3a are used, which compute + # the series starting from n=n0, which is the largest term. + + # TODO: write _jacobi_theta2a and _jacobi_theta3a using fixed-point + + if abs(q) > ctx.THETA_Q_LIM: + raise ValueError('abs(q) > THETA_Q_LIM = %f' % ctx.THETA_Q_LIM) + + extra = 10 + if z: + M = ctx.mag(z) + if M > 5 or (n == 1 and M < -5): + extra += 2*abs(M) + cz = 0.5 + extra2 = 50 + prec0 = ctx.prec + try: + ctx.prec += extra + if n == 1: + if ctx._im(z): + if abs(ctx._im(z)) < cz * abs(ctx._re(ctx.log(q))): + ctx.dps += extra2 + res = ctx._jacobi_theta2(z - ctx.pi/2, q) + else: + ctx.dps += 10 + res = ctx._jacobi_theta2a(z - ctx.pi/2, q) + else: + res = ctx._jacobi_theta2(z - ctx.pi/2, q) + elif n == 2: + if ctx._im(z): + if abs(ctx._im(z)) < cz * abs(ctx._re(ctx.log(q))): + ctx.dps += extra2 + res = ctx._jacobi_theta2(z, q) + else: + ctx.dps += 10 + res = ctx._jacobi_theta2a(z, q) + else: + res = ctx._jacobi_theta2(z, q) + elif n == 3: + if ctx._im(z): + if abs(ctx._im(z)) < cz * abs(ctx._re(ctx.log(q))): + ctx.dps += extra2 + res = ctx._jacobi_theta3(z, q) + else: + ctx.dps += 10 + res = ctx._jacobi_theta3a(z, q) + else: + res = ctx._jacobi_theta3(z, q) + elif n == 4: + if ctx._im(z): + if abs(ctx._im(z)) < cz * abs(ctx._re(ctx.log(q))): + ctx.dps += extra2 + res = ctx._jacobi_theta3(z, -q) + else: + ctx.dps += 10 + res = ctx._jacobi_theta3a(z, -q) + else: + res = ctx._jacobi_theta3(z, -q) + else: + raise ValueError + finally: + ctx.prec = prec0 + return res + +@defun +def _djtheta(ctx, n, z, q, derivative=1): + z = ctx.convert(z) + q = ctx.convert(q) + nd = int(derivative) + + if abs(q) > ctx.THETA_Q_LIM: + raise ValueError('abs(q) > THETA_Q_LIM = %f' % ctx.THETA_Q_LIM) + extra = 10 + ctx.prec * nd // 10 + if z: + M = ctx.mag(z) + if M > 5 or (n != 1 and M < -5): + extra += 2*abs(M) + cz = 0.5 + extra2 = 50 + prec0 = ctx.prec + try: + ctx.prec += extra + if n == 1: + if ctx._im(z): + if abs(ctx._im(z)) < cz * abs(ctx._re(ctx.log(q))): + ctx.dps += extra2 + res = ctx._djacobi_theta2(z - ctx.pi/2, q, nd) + else: + ctx.dps += 10 + res = ctx._djacobi_theta2a(z - ctx.pi/2, q, nd) + else: + res = ctx._djacobi_theta2(z - ctx.pi/2, q, nd) + elif n == 2: + if ctx._im(z): + if abs(ctx._im(z)) < cz * abs(ctx._re(ctx.log(q))): + ctx.dps += extra2 + res = ctx._djacobi_theta2(z, q, nd) + else: + ctx.dps += 10 + res = ctx._djacobi_theta2a(z, q, nd) + else: + res = ctx._djacobi_theta2(z, q, nd) + elif n == 3: + if ctx._im(z): + if abs(ctx._im(z)) < cz * abs(ctx._re(ctx.log(q))): + ctx.dps += extra2 + res = ctx._djacobi_theta3(z, q, nd) + else: + ctx.dps += 10 + res = ctx._djacobi_theta3a(z, q, nd) + else: + res = ctx._djacobi_theta3(z, q, nd) + elif n == 4: + if ctx._im(z): + if abs(ctx._im(z)) < cz * abs(ctx._re(ctx.log(q))): + ctx.dps += extra2 + res = ctx._djacobi_theta3(z, -q, nd) + else: + ctx.dps += 10 + res = ctx._djacobi_theta3a(z, -q, nd) + else: + res = ctx._djacobi_theta3(z, -q, nd) + else: + raise ValueError + finally: + ctx.prec = prec0 + return +res diff --git a/lib/python3.12/site-packages/mpmath/functions/zeta.py b/lib/python3.12/site-packages/mpmath/functions/zeta.py new file mode 100644 index 0000000000000000000000000000000000000000..d7ede50d95e5b6eff511619620c934529942cbdd --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/functions/zeta.py @@ -0,0 +1,1154 @@ +from __future__ import print_function + +from ..libmp.backend import xrange +from .functions import defun, defun_wrapped, defun_static + +@defun +def stieltjes(ctx, n, a=1): + n = ctx.convert(n) + a = ctx.convert(a) + if n < 0: + return ctx.bad_domain("Stieltjes constants defined for n >= 0") + if hasattr(ctx, "stieltjes_cache"): + stieltjes_cache = ctx.stieltjes_cache + else: + stieltjes_cache = ctx.stieltjes_cache = {} + if a == 1: + if n == 0: + return +ctx.euler + if n in stieltjes_cache: + prec, s = stieltjes_cache[n] + if prec >= ctx.prec: + return +s + mag = 1 + def f(x): + xa = x/a + v = (xa-ctx.j)*ctx.ln(a-ctx.j*x)**n/(1+xa**2)/(ctx.exp(2*ctx.pi*x)-1) + return ctx._re(v) / mag + orig = ctx.prec + try: + # Normalize integrand by approx. magnitude to + # speed up quadrature (which uses absolute error) + if n > 50: + ctx.prec = 20 + mag = ctx.quad(f, [0,ctx.inf], maxdegree=3) + ctx.prec = orig + 10 + int(n**0.5) + s = ctx.quad(f, [0,ctx.inf], maxdegree=20) + v = ctx.ln(a)**n/(2*a) - ctx.ln(a)**(n+1)/(n+1) + 2*s/a*mag + finally: + ctx.prec = orig + if a == 1 and ctx.isint(n): + stieltjes_cache[n] = (ctx.prec, v) + return +v + +@defun_wrapped +def siegeltheta(ctx, t, derivative=0): + d = int(derivative) + if (t == ctx.inf or t == ctx.ninf): + if d < 2: + if t == ctx.ninf and d == 0: + return ctx.ninf + return ctx.inf + else: + return ctx.zero + if d == 0: + if ctx._im(t): + # XXX: cancellation occurs + a = ctx.loggamma(0.25+0.5j*t) + b = ctx.loggamma(0.25-0.5j*t) + return -ctx.ln(ctx.pi)/2*t - 0.5j*(a-b) + else: + if ctx.isinf(t): + return t + return ctx._im(ctx.loggamma(0.25+0.5j*t)) - ctx.ln(ctx.pi)/2*t + if d > 0: + a = (-0.5j)**(d-1)*ctx.polygamma(d-1, 0.25-0.5j*t) + b = (0.5j)**(d-1)*ctx.polygamma(d-1, 0.25+0.5j*t) + if ctx._im(t): + if d == 1: + return -0.5*ctx.log(ctx.pi)+0.25*(a+b) + else: + return 0.25*(a+b) + else: + if d == 1: + return ctx._re(-0.5*ctx.log(ctx.pi)+0.25*(a+b)) + else: + return ctx._re(0.25*(a+b)) + +@defun_wrapped +def grampoint(ctx, n): + # asymptotic expansion, from + # http://mathworld.wolfram.com/GramPoint.html + g = 2*ctx.pi*ctx.exp(1+ctx.lambertw((8*n+1)/(8*ctx.e))) + return ctx.findroot(lambda t: ctx.siegeltheta(t)-ctx.pi*n, g) + + +@defun_wrapped +def siegelz(ctx, t, **kwargs): + d = int(kwargs.get("derivative", 0)) + t = ctx.convert(t) + t1 = ctx._re(t) + t2 = ctx._im(t) + prec = ctx.prec + try: + if abs(t1) > 500*prec and t2**2 < t1: + v = ctx.rs_z(t, d) + if ctx._is_real_type(t): + return ctx._re(v) + return v + except NotImplementedError: + pass + ctx.prec += 21 + e1 = ctx.expj(ctx.siegeltheta(t)) + z = ctx.zeta(0.5+ctx.j*t) + if d == 0: + v = e1*z + ctx.prec=prec + if ctx._is_real_type(t): + return ctx._re(v) + return +v + z1 = ctx.zeta(0.5+ctx.j*t, derivative=1) + theta1 = ctx.siegeltheta(t, derivative=1) + if d == 1: + v = ctx.j*e1*(z1+z*theta1) + ctx.prec=prec + if ctx._is_real_type(t): + return ctx._re(v) + return +v + z2 = ctx.zeta(0.5+ctx.j*t, derivative=2) + theta2 = ctx.siegeltheta(t, derivative=2) + comb1 = theta1**2-ctx.j*theta2 + if d == 2: + def terms(): + return [2*z1*theta1, z2, z*comb1] + v = ctx.sum_accurately(terms, 1) + v = -e1*v + ctx.prec = prec + if ctx._is_real_type(t): + return ctx._re(v) + return +v + ctx.prec += 10 + z3 = ctx.zeta(0.5+ctx.j*t, derivative=3) + theta3 = ctx.siegeltheta(t, derivative=3) + comb2 = theta1**3-3*ctx.j*theta1*theta2-theta3 + if d == 3: + def terms(): + return [3*theta1*z2, 3*z1*comb1, z3+z*comb2] + v = ctx.sum_accurately(terms, 1) + v = -ctx.j*e1*v + ctx.prec = prec + if ctx._is_real_type(t): + return ctx._re(v) + return +v + z4 = ctx.zeta(0.5+ctx.j*t, derivative=4) + theta4 = ctx.siegeltheta(t, derivative=4) + def terms(): + return [theta1**4, -6*ctx.j*theta1**2*theta2, -3*theta2**2, + -4*theta1*theta3, ctx.j*theta4] + comb3 = ctx.sum_accurately(terms, 1) + if d == 4: + def terms(): + return [6*theta1**2*z2, -6*ctx.j*z2*theta2, 4*theta1*z3, + 4*z1*comb2, z4, z*comb3] + v = ctx.sum_accurately(terms, 1) + v = e1*v + ctx.prec = prec + if ctx._is_real_type(t): + return ctx._re(v) + return +v + if d > 4: + h = lambda x: ctx.siegelz(x, derivative=4) + return ctx.diff(h, t, n=d-4) + + +_zeta_zeros = [ +14.134725142,21.022039639,25.010857580,30.424876126,32.935061588, +37.586178159,40.918719012,43.327073281,48.005150881,49.773832478, +52.970321478,56.446247697,59.347044003,60.831778525,65.112544048, +67.079810529,69.546401711,72.067157674,75.704690699,77.144840069, +79.337375020,82.910380854,84.735492981,87.425274613,88.809111208, +92.491899271,94.651344041,95.870634228,98.831194218,101.317851006, +103.725538040,105.446623052,107.168611184,111.029535543,111.874659177, +114.320220915,116.226680321,118.790782866,121.370125002,122.946829294, +124.256818554,127.516683880,129.578704200,131.087688531,133.497737203, +134.756509753,138.116042055,139.736208952,141.123707404,143.111845808, +146.000982487,147.422765343,150.053520421,150.925257612,153.024693811, +156.112909294,157.597591818,158.849988171,161.188964138,163.030709687, +165.537069188,167.184439978,169.094515416,169.911976479,173.411536520, +174.754191523,176.441434298,178.377407776,179.916484020,182.207078484, +184.874467848,185.598783678,187.228922584,189.416158656,192.026656361, +193.079726604,195.265396680,196.876481841,198.015309676,201.264751944, +202.493594514,204.189671803,205.394697202,207.906258888,209.576509717, +211.690862595,213.347919360,214.547044783,216.169538508,219.067596349, +220.714918839,221.430705555,224.007000255,224.983324670,227.421444280, +229.337413306,231.250188700,231.987235253,233.693404179,236.524229666, +] + +def _load_zeta_zeros(url): + import urllib + d = urllib.urlopen(url) + L = [float(x) for x in d.readlines()] + # Sanity check + assert round(L[0]) == 14 + _zeta_zeros[:] = L + +@defun +def oldzetazero(ctx, n, url='http://www.dtc.umn.edu/~odlyzko/zeta_tables/zeros1'): + n = int(n) + if n < 0: + return ctx.zetazero(-n).conjugate() + if n == 0: + raise ValueError("n must be nonzero") + if n > len(_zeta_zeros) and n <= 100000: + _load_zeta_zeros(url) + if n > len(_zeta_zeros): + raise NotImplementedError("n too large for zetazeros") + return ctx.mpc(0.5, ctx.findroot(ctx.siegelz, _zeta_zeros[n-1])) + +@defun_wrapped +def riemannr(ctx, x): + if x == 0: + return ctx.zero + # Check if a simple asymptotic estimate is accurate enough + if abs(x) > 1000: + a = ctx.li(x) + b = 0.5*ctx.li(ctx.sqrt(x)) + if abs(b) < abs(a)*ctx.eps: + return a + if abs(x) < 0.01: + # XXX + ctx.prec += int(-ctx.log(abs(x),2)) + # Sum Gram's series + s = t = ctx.one + u = ctx.ln(x) + k = 1 + while abs(t) > abs(s)*ctx.eps: + t = t * u / k + s += t / (k * ctx._zeta_int(k+1)) + k += 1 + return s + +@defun_static +def primepi(ctx, x): + x = int(x) + if x < 2: + return 0 + return len(ctx.list_primes(x)) + +# TODO: fix the interface wrt contexts +@defun_wrapped +def primepi2(ctx, x): + x = int(x) + if x < 2: + return ctx._iv.zero + if x < 2657: + return ctx._iv.mpf(ctx.primepi(x)) + mid = ctx.li(x) + # Schoenfeld's estimate for x >= 2657, assuming RH + err = ctx.sqrt(x,rounding='u')*ctx.ln(x,rounding='u')/8/ctx.pi(rounding='d') + a = ctx.floor((ctx._iv.mpf(mid)-err).a, rounding='d') + b = ctx.ceil((ctx._iv.mpf(mid)+err).b, rounding='u') + return ctx._iv.mpf([a,b]) + +@defun_wrapped +def primezeta(ctx, s): + if ctx.isnan(s): + return s + if ctx.re(s) <= 0: + raise ValueError("prime zeta function defined only for re(s) > 0") + if s == 1: + return ctx.inf + if s == 0.5: + return ctx.mpc(ctx.ninf, ctx.pi) + r = ctx.re(s) + if r > ctx.prec: + return 0.5**s + else: + wp = ctx.prec + int(r) + def terms(): + orig = ctx.prec + # zeta ~ 1+eps; need to set precision + # to get logarithm accurately + k = 0 + while 1: + k += 1 + u = ctx.moebius(k) + if not u: + continue + ctx.prec = wp + t = u*ctx.ln(ctx.zeta(k*s))/k + if not t: + return + #print ctx.prec, ctx.nstr(t) + ctx.prec = orig + yield t + return ctx.sum_accurately(terms) + +# TODO: for bernpoly and eulerpoly, ensure that all exact zeros are covered + +@defun_wrapped +def bernpoly(ctx, n, z): + # Slow implementation: + #return sum(ctx.binomial(n,k)*ctx.bernoulli(k)*z**(n-k) for k in xrange(0,n+1)) + n = int(n) + if n < 0: + raise ValueError("Bernoulli polynomials only defined for n >= 0") + if z == 0 or (z == 1 and n > 1): + return ctx.bernoulli(n) + if z == 0.5: + return (ctx.ldexp(1,1-n)-1)*ctx.bernoulli(n) + if n <= 3: + if n == 0: return z ** 0 + if n == 1: return z - 0.5 + if n == 2: return (6*z*(z-1)+1)/6 + if n == 3: return z*(z*(z-1.5)+0.5) + if ctx.isinf(z): + return z ** n + if ctx.isnan(z): + return z + if abs(z) > 2: + def terms(): + t = ctx.one + yield t + r = ctx.one/z + k = 1 + while k <= n: + t = t*(n+1-k)/k*r + if not (k > 2 and k & 1): + yield t*ctx.bernoulli(k) + k += 1 + return ctx.sum_accurately(terms) * z**n + else: + def terms(): + yield ctx.bernoulli(n) + t = ctx.one + k = 1 + while k <= n: + t = t*(n+1-k)/k * z + m = n-k + if not (m > 2 and m & 1): + yield t*ctx.bernoulli(m) + k += 1 + return ctx.sum_accurately(terms) + +@defun_wrapped +def eulerpoly(ctx, n, z): + n = int(n) + if n < 0: + raise ValueError("Euler polynomials only defined for n >= 0") + if n <= 2: + if n == 0: return z ** 0 + if n == 1: return z - 0.5 + if n == 2: return z*(z-1) + if ctx.isinf(z): + return z**n + if ctx.isnan(z): + return z + m = n+1 + if z == 0: + return -2*(ctx.ldexp(1,m)-1)*ctx.bernoulli(m)/m * z**0 + if z == 1: + return 2*(ctx.ldexp(1,m)-1)*ctx.bernoulli(m)/m * z**0 + if z == 0.5: + if n % 2: + return ctx.zero + # Use exact code for Euler numbers + if n < 100 or n*ctx.mag(0.46839865*n) < ctx.prec*0.25: + return ctx.ldexp(ctx._eulernum(n), -n) + # http://functions.wolfram.com/Polynomials/EulerE2/06/01/02/01/0002/ + def terms(): + t = ctx.one + k = 0 + w = ctx.ldexp(1,n+2) + while 1: + v = n-k+1 + if not (v > 2 and v & 1): + yield (2-w)*ctx.bernoulli(v)*t + k += 1 + if k > n: + break + t = t*z*(n-k+2)/k + w *= 0.5 + return ctx.sum_accurately(terms) / m + +@defun +def eulernum(ctx, n, exact=False): + n = int(n) + if exact: + return int(ctx._eulernum(n)) + if n < 100: + return ctx.mpf(ctx._eulernum(n)) + if n % 2: + return ctx.zero + return ctx.ldexp(ctx.eulerpoly(n,0.5), n) + +# TODO: this should be implemented low-level +def polylog_series(ctx, s, z): + tol = +ctx.eps + l = ctx.zero + k = 1 + zk = z + while 1: + term = zk / k**s + l += term + if abs(term) < tol: + break + zk *= z + k += 1 + return l + +def polylog_continuation(ctx, n, z): + if n < 0: + return z*0 + twopij = 2j * ctx.pi + a = -twopij**n/ctx.fac(n) * ctx.bernpoly(n, ctx.ln(z)/twopij) + if ctx._is_real_type(z) and z < 0: + a = ctx._re(a) + if ctx._im(z) < 0 or (ctx._im(z) == 0 and ctx._re(z) >= 1): + a -= twopij*ctx.ln(z)**(n-1)/ctx.fac(n-1) + return a + +def polylog_unitcircle(ctx, n, z): + tol = +ctx.eps + if n > 1: + l = ctx.zero + logz = ctx.ln(z) + logmz = ctx.one + m = 0 + while 1: + if (n-m) != 1: + term = ctx.zeta(n-m) * logmz / ctx.fac(m) + if term and abs(term) < tol: + break + l += term + logmz *= logz + m += 1 + l += ctx.ln(z)**(n-1)/ctx.fac(n-1)*(ctx.harmonic(n-1)-ctx.ln(-ctx.ln(z))) + elif n < 1: # else + l = ctx.fac(-n)*(-ctx.ln(z))**(n-1) + logz = ctx.ln(z) + logkz = ctx.one + k = 0 + while 1: + b = ctx.bernoulli(k-n+1) + if b: + term = b*logkz/(ctx.fac(k)*(k-n+1)) + if abs(term) < tol: + break + l -= term + logkz *= logz + k += 1 + else: + raise ValueError + if ctx._is_real_type(z) and z < 0: + l = ctx._re(l) + return l + +def polylog_general(ctx, s, z): + v = ctx.zero + u = ctx.ln(z) + if not abs(u) < 5: # theoretically |u| < 2*pi + j = ctx.j + v = 1-s + y = ctx.ln(-z)/(2*ctx.pi*j) + return ctx.gamma(v)*(j**v*ctx.zeta(v,0.5+y) + j**-v*ctx.zeta(v,0.5-y))/(2*ctx.pi)**v + t = 1 + k = 0 + while 1: + term = ctx.zeta(s-k) * t + if abs(term) < ctx.eps: + break + v += term + k += 1 + t *= u + t /= k + return ctx.gamma(1-s)*(-u)**(s-1) + v + +@defun_wrapped +def polylog(ctx, s, z): + s = ctx.convert(s) + z = ctx.convert(z) + if z == 1: + return ctx.zeta(s) + if z == -1: + return -ctx.altzeta(s) + if s == 0: + return z/(1-z) + if s == 1: + return -ctx.ln(1-z) + if s == -1: + return z/(1-z)**2 + if abs(z) <= 0.75 or (not ctx.isint(s) and abs(z) < 0.9): + return polylog_series(ctx, s, z) + if abs(z) >= 1.4 and ctx.isint(s): + return (-1)**(s+1)*polylog_series(ctx, s, 1/z) + polylog_continuation(ctx, int(ctx.re(s)), z) + if ctx.isint(s): + return polylog_unitcircle(ctx, int(ctx.re(s)), z) + return polylog_general(ctx, s, z) + +@defun_wrapped +def clsin(ctx, s, z, pi=False): + if ctx.isint(s) and s < 0 and int(s) % 2 == 1: + return z*0 + if pi: + a = ctx.expjpi(z) + else: + a = ctx.expj(z) + if ctx._is_real_type(z) and ctx._is_real_type(s): + return ctx.im(ctx.polylog(s,a)) + b = 1/a + return (-0.5j)*(ctx.polylog(s,a) - ctx.polylog(s,b)) + +@defun_wrapped +def clcos(ctx, s, z, pi=False): + if ctx.isint(s) and s < 0 and int(s) % 2 == 0: + return z*0 + if pi: + a = ctx.expjpi(z) + else: + a = ctx.expj(z) + if ctx._is_real_type(z) and ctx._is_real_type(s): + return ctx.re(ctx.polylog(s,a)) + b = 1/a + return 0.5*(ctx.polylog(s,a) + ctx.polylog(s,b)) + +@defun +def altzeta(ctx, s, **kwargs): + try: + return ctx._altzeta(s, **kwargs) + except NotImplementedError: + return ctx._altzeta_generic(s) + +@defun_wrapped +def _altzeta_generic(ctx, s): + if s == 1: + return ctx.ln2 + 0*s + return -ctx.powm1(2, 1-s) * ctx.zeta(s) + +@defun +def zeta(ctx, s, a=1, derivative=0, method=None, **kwargs): + d = int(derivative) + if a == 1 and not (d or method): + try: + return ctx._zeta(s, **kwargs) + except NotImplementedError: + pass + s = ctx.convert(s) + prec = ctx.prec + method = kwargs.get('method') + verbose = kwargs.get('verbose') + if (not s) and (not derivative): + return ctx.mpf(0.5) - ctx._convert_param(a)[0] + if a == 1 and method != 'euler-maclaurin': + im = abs(ctx._im(s)) + re = abs(ctx._re(s)) + #if (im < prec or method == 'borwein') and not derivative: + # try: + # if verbose: + # print "zeta: Attempting to use the Borwein algorithm" + # return ctx._zeta(s, **kwargs) + # except NotImplementedError: + # if verbose: + # print "zeta: Could not use the Borwein algorithm" + # pass + if abs(im) > 500*prec and 10*re < prec and derivative <= 4 or \ + method == 'riemann-siegel': + try: # py2.4 compatible try block + try: + if verbose: + print("zeta: Attempting to use the Riemann-Siegel algorithm") + return ctx.rs_zeta(s, derivative, **kwargs) + except NotImplementedError: + if verbose: + print("zeta: Could not use the Riemann-Siegel algorithm") + pass + finally: + ctx.prec = prec + if s == 1: + return ctx.inf + abss = abs(s) + if abss == ctx.inf: + if ctx.re(s) == ctx.inf: + if d == 0: + return ctx.one + return ctx.zero + return s*0 + elif ctx.isnan(abss): + return 1/s + if ctx.re(s) > 2*ctx.prec and a == 1 and not derivative: + return ctx.one + ctx.power(2, -s) + return +ctx._hurwitz(s, a, d, **kwargs) + +@defun +def _hurwitz(ctx, s, a=1, d=0, **kwargs): + prec = ctx.prec + verbose = kwargs.get('verbose') + try: + extraprec = 10 + ctx.prec += extraprec + # We strongly want to special-case rational a + a, atype = ctx._convert_param(a) + if ctx.re(s) < 0: + if verbose: + print("zeta: Attempting reflection formula") + try: + return _hurwitz_reflection(ctx, s, a, d, atype) + except NotImplementedError: + pass + if verbose: + print("zeta: Reflection formula failed") + if verbose: + print("zeta: Using the Euler-Maclaurin algorithm") + while 1: + ctx.prec = prec + extraprec + T1, T2 = _hurwitz_em(ctx, s, a, d, prec+10, verbose) + cancellation = ctx.mag(T1) - ctx.mag(T1+T2) + if verbose: + print("Term 1:", T1) + print("Term 2:", T2) + print("Cancellation:", cancellation, "bits") + if cancellation < extraprec: + return T1 + T2 + else: + extraprec = max(2*extraprec, min(cancellation + 5, 100*prec)) + if extraprec > kwargs.get('maxprec', 100*prec): + raise ctx.NoConvergence("zeta: too much cancellation") + finally: + ctx.prec = prec + +def _hurwitz_reflection(ctx, s, a, d, atype): + # TODO: implement for derivatives + if d != 0: + raise NotImplementedError + res = ctx.re(s) + negs = -s + # Integer reflection formula + if ctx.isnpint(s): + n = int(res) + if n <= 0: + return ctx.bernpoly(1-n, a) / (n-1) + if not (atype == 'Q' or atype == 'Z'): + raise NotImplementedError + t = 1-s + # We now require a to be standardized + v = 0 + shift = 0 + b = a + while ctx.re(b) > 1: + b -= 1 + v -= b**negs + shift -= 1 + while ctx.re(b) <= 0: + v += b**negs + b += 1 + shift += 1 + # Rational reflection formula + try: + p, q = a._mpq_ + except: + assert a == int(a) + p = int(a) + q = 1 + p += shift*q + assert 1 <= p <= q + g = ctx.fsum(ctx.cospi(t/2-2*k*b)*ctx._hurwitz(t,(k,q)) \ + for k in range(1,q+1)) + g *= 2*ctx.gamma(t)/(2*ctx.pi*q)**t + v += g + return v + +def _hurwitz_em(ctx, s, a, d, prec, verbose): + # May not be converted at this point + a = ctx.convert(a) + tol = -prec + # Estimate number of terms for Euler-Maclaurin summation; could be improved + M1 = 0 + M2 = prec // 3 + N = M2 + lsum = 0 + # This speeds up the recurrence for derivatives + if ctx.isint(s): + s = int(ctx._re(s)) + s1 = s-1 + while 1: + # Truncated L-series + l = ctx._zetasum(s, M1+a, M2-M1-1, [d])[0][0] + #if d: + # l = ctx.fsum((-ctx.ln(n+a))**d * (n+a)**negs for n in range(M1,M2)) + #else: + # l = ctx.fsum((n+a)**negs for n in range(M1,M2)) + lsum += l + M2a = M2+a + logM2a = ctx.ln(M2a) + logM2ad = logM2a**d + logs = [logM2ad] + logr = 1/logM2a + rM2a = 1/M2a + M2as = M2a**(-s) + if d: + tailsum = ctx.gammainc(d+1, s1*logM2a) / s1**(d+1) + else: + tailsum = 1/((s1)*(M2a)**s1) + tailsum += 0.5 * logM2ad * M2as + U = [1] + r = M2as + fact = 2 + for j in range(1, N+1): + # TODO: the following could perhaps be tidied a bit + j2 = 2*j + if j == 1: + upds = [1] + else: + upds = [j2-2, j2-1] + for m in upds: + D = min(m,d+1) + if m <= d: + logs.append(logs[-1] * logr) + Un = [0]*(D+1) + for i in xrange(D): Un[i] = (1-m-s)*U[i] + for i in xrange(1,D+1): Un[i] += (d-(i-1))*U[i-1] + U = Un + r *= rM2a + t = ctx.fdot(U, logs) * r * ctx.bernoulli(j2)/(-fact) + tailsum += t + if ctx.mag(t) < tol: + return lsum, (-1)**d * tailsum + fact *= (j2+1)*(j2+2) + if verbose: + print("Sum range:", M1, M2, "term magnitude", ctx.mag(t), "tolerance", tol) + M1, M2 = M2, M2*2 + if ctx.re(s) < 0: + N += N//2 + + + +@defun +def _zetasum(ctx, s, a, n, derivatives=[0], reflect=False): + """ + Returns [xd0,xd1,...,xdr], [yd0,yd1,...ydr] where + + xdk = D^k ( 1/a^s + 1/(a+1)^s + ... + 1/(a+n)^s ) + ydk = D^k conj( 1/a^(1-s) + 1/(a+1)^(1-s) + ... + 1/(a+n)^(1-s) ) + + D^k = kth derivative with respect to s, k ranges over the given list of + derivatives (which should consist of either a single element + or a range 0,1,...r). If reflect=False, the ydks are not computed. + """ + #print "zetasum", s, a, n + # don't use the fixed-point code if there are large exponentials + if abs(ctx.re(s)) < 0.5 * ctx.prec: + try: + return ctx._zetasum_fast(s, a, n, derivatives, reflect) + except NotImplementedError: + pass + negs = ctx.fneg(s, exact=True) + have_derivatives = derivatives != [0] + have_one_derivative = len(derivatives) == 1 + if not reflect: + if not have_derivatives: + return [ctx.fsum((a+k)**negs for k in xrange(n+1))], [] + if have_one_derivative: + d = derivatives[0] + x = ctx.fsum(ctx.ln(a+k)**d * (a+k)**negs for k in xrange(n+1)) + return [(-1)**d * x], [] + maxd = max(derivatives) + if not have_one_derivative: + derivatives = range(maxd+1) + xs = [ctx.zero for d in derivatives] + if reflect: + ys = [ctx.zero for d in derivatives] + else: + ys = [] + for k in xrange(n+1): + w = a + k + xterm = w ** negs + if reflect: + yterm = ctx.conj(ctx.one / (w * xterm)) + if have_derivatives: + logw = -ctx.ln(w) + if have_one_derivative: + logw = logw ** maxd + xs[0] += xterm * logw + if reflect: + ys[0] += yterm * logw + else: + t = ctx.one + for d in derivatives: + xs[d] += xterm * t + if reflect: + ys[d] += yterm * t + t *= logw + else: + xs[0] += xterm + if reflect: + ys[0] += yterm + return xs, ys + +@defun +def dirichlet(ctx, s, chi=[1], derivative=0): + s = ctx.convert(s) + q = len(chi) + d = int(derivative) + if d > 2: + raise NotImplementedError("arbitrary order derivatives") + prec = ctx.prec + try: + ctx.prec += 10 + if s == 1: + have_pole = True + for x in chi: + if x and x != 1: + have_pole = False + h = +ctx.eps + ctx.prec *= 2*(d+1) + s += h + if have_pole: + return +ctx.inf + z = ctx.zero + for p in range(1,q+1): + if chi[p%q]: + if d == 1: + z += chi[p%q] * (ctx.zeta(s, (p,q), 1) - \ + ctx.zeta(s, (p,q))*ctx.log(q)) + else: + z += chi[p%q] * ctx.zeta(s, (p,q)) + z /= q**s + finally: + ctx.prec = prec + return +z + + +def secondzeta_main_term(ctx, s, a, **kwargs): + tol = ctx.eps + f = lambda n: ctx.gammainc(0.5*s, a*gamm**2, regularized=True)*gamm**(-s) + totsum = term = ctx.zero + mg = ctx.inf + n = 0 + while mg > tol: + totsum += term + n += 1 + gamm = ctx.im(ctx.zetazero_memoized(n)) + term = f(n) + mg = abs(term) + err = 0 + if kwargs.get("error"): + sg = ctx.re(s) + err = 0.5*ctx.pi**(-1)*max(1,sg)*a**(sg-0.5)*ctx.log(gamm/(2*ctx.pi))*\ + ctx.gammainc(-0.5, a*gamm**2)/abs(ctx.gamma(s/2)) + err = abs(err) + return +totsum, err, n + +def secondzeta_prime_term(ctx, s, a, **kwargs): + tol = ctx.eps + f = lambda n: ctx.gammainc(0.5*(1-s),0.25*ctx.log(n)**2 * a**(-1))*\ + ((0.5*ctx.log(n))**(s-1))*ctx.mangoldt(n)/ctx.sqrt(n)/\ + (2*ctx.gamma(0.5*s)*ctx.sqrt(ctx.pi)) + totsum = term = ctx.zero + mg = ctx.inf + n = 1 + while mg > tol or n < 9: + totsum += term + n += 1 + term = f(n) + if term == 0: + mg = ctx.inf + else: + mg = abs(term) + if kwargs.get("error"): + err = mg + return +totsum, err, n + +def secondzeta_exp_term(ctx, s, a): + if ctx.isint(s) and ctx.re(s) <= 0: + m = int(round(ctx.re(s))) + if not m & 1: + return ctx.mpf('-0.25')**(-m//2) + tol = ctx.eps + f = lambda n: (0.25*a)**n/((n+0.5*s)*ctx.fac(n)) + totsum = ctx.zero + term = f(0) + mg = ctx.inf + n = 0 + while mg > tol: + totsum += term + n += 1 + term = f(n) + mg = abs(term) + v = a**(0.5*s)*totsum/ctx.gamma(0.5*s) + return v + +def secondzeta_singular_term(ctx, s, a, **kwargs): + factor = a**(0.5*(s-1))/(4*ctx.sqrt(ctx.pi)*ctx.gamma(0.5*s)) + extraprec = ctx.mag(factor) + ctx.prec += extraprec + factor = a**(0.5*(s-1))/(4*ctx.sqrt(ctx.pi)*ctx.gamma(0.5*s)) + tol = ctx.eps + f = lambda n: ctx.bernpoly(n,0.75)*(4*ctx.sqrt(a))**n*\ + ctx.gamma(0.5*n)/((s+n-1)*ctx.fac(n)) + totsum = ctx.zero + mg1 = ctx.inf + n = 1 + term = f(n) + mg2 = abs(term) + while mg2 > tol and mg2 <= mg1: + totsum += term + n += 1 + term = f(n) + totsum += term + n +=1 + term = f(n) + mg1 = mg2 + mg2 = abs(term) + totsum += term + pole = -2*(s-1)**(-2)+(ctx.euler+ctx.log(16*ctx.pi**2*a))*(s-1)**(-1) + st = factor*(pole+totsum) + err = 0 + if kwargs.get("error"): + if not ((mg2 > tol) and (mg2 <= mg1)): + if mg2 <= tol: + err = ctx.mpf(10)**int(ctx.log(abs(factor*tol),10)) + if mg2 > mg1: + err = ctx.mpf(10)**int(ctx.log(abs(factor*mg1),10)) + err = max(err, ctx.eps*1.) + ctx.prec -= extraprec + return +st, err + +@defun +def secondzeta(ctx, s, a = 0.015, **kwargs): + r""" + Evaluates the secondary zeta function `Z(s)`, defined for + `\mathrm{Re}(s)>1` by + + .. math :: + + Z(s) = \sum_{n=1}^{\infty} \frac{1}{\tau_n^s} + + where `\frac12+i\tau_n` runs through the zeros of `\zeta(s)` with + imaginary part positive. + + `Z(s)` extends to a meromorphic function on `\mathbb{C}` with a + double pole at `s=1` and simple poles at the points `-2n` for + `n=0`, 1, 2, ... + + **Examples** + + >>> from mpmath import * + >>> mp.pretty = True; mp.dps = 15 + >>> secondzeta(2) + 0.023104993115419 + >>> xi = lambda s: 0.5*s*(s-1)*pi**(-0.5*s)*gamma(0.5*s)*zeta(s) + >>> Xi = lambda t: xi(0.5+t*j) + >>> chop(-0.5*diff(Xi,0,n=2)/Xi(0)) + 0.023104993115419 + + We may ask for an approximate error value:: + + >>> secondzeta(0.5+100j, error=True) + ((-0.216272011276718 - 0.844952708937228j), 2.22044604925031e-16) + + The function has poles at the negative odd integers, + and dyadic rational values at the negative even integers:: + + >>> mp.dps = 30 + >>> secondzeta(-8) + -0.67236328125 + >>> secondzeta(-7) + +inf + + **Implementation notes** + + The function is computed as sum of four terms `Z(s)=A(s)-P(s)+E(s)-S(s)` + respectively main, prime, exponential and singular terms. + The main term `A(s)` is computed from the zeros of zeta. + The prime term depends on the von Mangoldt function. + The singular term is responsible for the poles of the function. + + The four terms depends on a small parameter `a`. We may change the + value of `a`. Theoretically this has no effect on the sum of the four + terms, but in practice may be important. + + A smaller value of the parameter `a` makes `A(s)` depend on + a smaller number of zeros of zeta, but `P(s)` uses more values of + von Mangoldt function. + + We may also add a verbose option to obtain data about the + values of the four terms. + + >>> mp.dps = 10 + >>> secondzeta(0.5 + 40j, error=True, verbose=True) + main term = (-30190318549.138656312556 - 13964804384.624622876523j) + computed using 19 zeros of zeta + prime term = (132717176.89212754625045 + 188980555.17563978290601j) + computed using 9 values of the von Mangoldt function + exponential term = (542447428666.07179812536 + 362434922978.80192435203j) + singular term = (512124392939.98154322355 + 348281138038.65531023921j) + ((0.059471043 + 0.3463514534j), 1.455191523e-11) + + >>> secondzeta(0.5 + 40j, a=0.04, error=True, verbose=True) + main term = (-151962888.19606243907725 - 217930683.90210294051982j) + computed using 9 zeros of zeta + prime term = (2476659342.3038722372461 + 28711581821.921627163136j) + computed using 37 values of the von Mangoldt function + exponential term = (178506047114.7838188264 + 819674143244.45677330576j) + singular term = (175877424884.22441310708 + 790744630738.28669174871j) + ((0.059471043 + 0.3463514534j), 1.455191523e-11) + + Notice the great cancellation between the four terms. Changing `a`, the + four terms are very different numbers but the cancellation gives + the good value of Z(s). + + **References** + + A. Voros, Zeta functions for the Riemann zeros, Ann. Institute Fourier, + 53, (2003) 665--699. + + A. Voros, Zeta functions over Zeros of Zeta Functions, Lecture Notes + of the Unione Matematica Italiana, Springer, 2009. + """ + s = ctx.convert(s) + a = ctx.convert(a) + tol = ctx.eps + if ctx.isint(s) and ctx.re(s) <= 1: + if abs(s-1) < tol*1000: + return ctx.inf + m = int(round(ctx.re(s))) + if m & 1: + return ctx.inf + else: + return ((-1)**(-m//2)*\ + ctx.fraction(8-ctx.eulernum(-m,exact=True),2**(-m+3))) + prec = ctx.prec + try: + t3 = secondzeta_exp_term(ctx, s, a) + extraprec = max(ctx.mag(t3),0) + ctx.prec += extraprec + 3 + t1, r1, gt = secondzeta_main_term(ctx,s,a,error='True', verbose='True') + t2, r2, pt = secondzeta_prime_term(ctx,s,a,error='True', verbose='True') + t4, r4 = secondzeta_singular_term(ctx,s,a,error='True') + t3 = secondzeta_exp_term(ctx, s, a) + err = r1+r2+r4 + t = t1-t2+t3-t4 + if kwargs.get("verbose"): + print('main term =', t1) + print(' computed using', gt, 'zeros of zeta') + print('prime term =', t2) + print(' computed using', pt, 'values of the von Mangoldt function') + print('exponential term =', t3) + print('singular term =', t4) + finally: + ctx.prec = prec + if kwargs.get("error"): + w = max(ctx.mag(abs(t)),0) + err = max(err*2**w, ctx.eps*1.*2**w) + return +t, err + return +t + + +@defun_wrapped +def lerchphi(ctx, z, s, a): + r""" + Gives the Lerch transcendent, defined for `|z| < 1` and + `\Re{a} > 0` by + + .. math :: + + \Phi(z,s,a) = \sum_{k=0}^{\infty} \frac{z^k}{(a+k)^s} + + and generally by the recurrence `\Phi(z,s,a) = z \Phi(z,s,a+1) + a^{-s}` + along with the integral representation valid for `\Re{a} > 0` + + .. math :: + + \Phi(z,s,a) = \frac{1}{2 a^s} + + \int_0^{\infty} \frac{z^t}{(a+t)^s} dt - + 2 \int_0^{\infty} \frac{\sin(t \log z - s + \operatorname{arctan}(t/a)}{(a^2 + t^2)^{s/2} + (e^{2 \pi t}-1)} dt. + + The Lerch transcendent generalizes the Hurwitz zeta function :func:`zeta` + (`z = 1`) and the polylogarithm :func:`polylog` (`a = 1`). + + **Examples** + + Several evaluations in terms of simpler functions:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> lerchphi(-1,2,0.5); 4*catalan + 3.663862376708876060218414 + 3.663862376708876060218414 + >>> diff(lerchphi, (-1,-2,1), (0,1,0)); 7*zeta(3)/(4*pi**2) + 0.2131391994087528954617607 + 0.2131391994087528954617607 + >>> lerchphi(-4,1,1); log(5)/4 + 0.4023594781085250936501898 + 0.4023594781085250936501898 + >>> lerchphi(-3+2j,1,0.5); 2*atanh(sqrt(-3+2j))/sqrt(-3+2j) + (1.142423447120257137774002 + 0.2118232380980201350495795j) + (1.142423447120257137774002 + 0.2118232380980201350495795j) + + Evaluation works for complex arguments and `|z| \ge 1`:: + + >>> lerchphi(1+2j, 3-j, 4+2j) + (0.002025009957009908600539469 + 0.003327897536813558807438089j) + >>> lerchphi(-2,2,-2.5) + -12.28676272353094275265944 + >>> lerchphi(10,10,10) + (-4.462130727102185701817349e-11 - 1.575172198981096218823481e-12j) + >>> lerchphi(10,10,-10.5) + (112658784011940.5605789002 - 498113185.5756221777743631j) + + Some degenerate cases:: + + >>> lerchphi(0,1,2) + 0.5 + >>> lerchphi(0,1,-2) + -0.5 + + Reduction to simpler functions:: + + >>> lerchphi(1, 4.25+1j, 1) + (1.044674457556746668033975 - 0.04674508654012658932271226j) + >>> zeta(4.25+1j) + (1.044674457556746668033975 - 0.04674508654012658932271226j) + >>> lerchphi(1 - 0.5**10, 4.25+1j, 1) + (1.044629338021507546737197 - 0.04667768813963388181708101j) + >>> lerchphi(3, 4, 1) + (1.249503297023366545192592 - 0.2314252413375664776474462j) + >>> polylog(4, 3) / 3 + (1.249503297023366545192592 - 0.2314252413375664776474462j) + >>> lerchphi(3, 4, 1 - 0.5**10) + (1.253978063946663945672674 - 0.2316736622836535468765376j) + + **References** + + 1. [DLMF]_ section 25.14 + + """ + if z == 0: + return a ** (-s) + # Faster, but these cases are useful for testing right now + if z == 1: + return ctx.zeta(s, a) + if a == 1: + return ctx.polylog(s, z) / z + if ctx.re(a) < 1: + if ctx.isnpint(a): + raise ValueError("Lerch transcendent complex infinity") + m = int(ctx.ceil(1-ctx.re(a))) + v = ctx.zero + zpow = ctx.one + for n in xrange(m): + v += zpow / (a+n)**s + zpow *= z + return zpow * ctx.lerchphi(z,s, a+m) + v + g = ctx.ln(z) + v = 1/(2*a**s) + ctx.gammainc(1-s, -a*g) * (-g)**(s-1) / z**a + h = s / 2 + r = 2*ctx.pi + f = lambda t: ctx.sin(s*ctx.atan(t/a)-t*g) / \ + ((a**2+t**2)**h * ctx.expm1(r*t)) + v += 2*ctx.quad(f, [0, ctx.inf]) + if not ctx.im(z) and not ctx.im(s) and not ctx.im(a) and ctx.re(z) < 1: + v = ctx.chop(v) + return v diff --git a/lib/python3.12/site-packages/mpmath/functions/zetazeros.py b/lib/python3.12/site-packages/mpmath/functions/zetazeros.py new file mode 100644 index 0000000000000000000000000000000000000000..37c11a29426b0114053ae61664541f7ae7de95d8 --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/functions/zetazeros.py @@ -0,0 +1,1018 @@ +""" +The function zetazero(n) computes the n-th nontrivial zero of zeta(s). + +The general strategy is to locate a block of Gram intervals B where we +know exactly the number of zeros contained and which of those zeros +is that which we search. + +If n <= 400 000 000 we know exactly the Rosser exceptions, contained +in a list in this file. Hence for n<=400 000 000 we simply +look at these list of exceptions. If our zero is implicated in one of +these exceptions we have our block B. In other case we simply locate +the good Rosser block containing our zero. + +For n > 400 000 000 we apply the method of Turing, as complemented by +Lehman, Brent and Trudgian to find a suitable B. +""" + +from .functions import defun, defun_wrapped + +def find_rosser_block_zero(ctx, n): + """for n<400 000 000 determines a block were one find our zero""" + for k in range(len(_ROSSER_EXCEPTIONS)//2): + a=_ROSSER_EXCEPTIONS[2*k][0] + b=_ROSSER_EXCEPTIONS[2*k][1] + if ((a<= n-2) and (n-1 <= b)): + t0 = ctx.grampoint(a) + t1 = ctx.grampoint(b) + v0 = ctx._fp.siegelz(t0) + v1 = ctx._fp.siegelz(t1) + my_zero_number = n-a-1 + zero_number_block = b-a + pattern = _ROSSER_EXCEPTIONS[2*k+1] + return (my_zero_number, [a,b], [t0,t1], [v0,v1]) + k = n-2 + t,v,b = compute_triple_tvb(ctx, k) + T = [t] + V = [v] + while b < 0: + k -= 1 + t,v,b = compute_triple_tvb(ctx, k) + T.insert(0,t) + V.insert(0,v) + my_zero_number = n-k-1 + m = n-1 + t,v,b = compute_triple_tvb(ctx, m) + T.append(t) + V.append(v) + while b < 0: + m += 1 + t,v,b = compute_triple_tvb(ctx, m) + T.append(t) + V.append(v) + return (my_zero_number, [k,m], T, V) + +def wpzeros(t): + """Precision needed to compute higher zeros""" + wp = 53 + if t > 3*10**8: + wp = 63 + if t > 10**11: + wp = 70 + if t > 10**14: + wp = 83 + return wp + +def separate_zeros_in_block(ctx, zero_number_block, T, V, limitloop=None, + fp_tolerance=None): + """Separate the zeros contained in the block T, limitloop + determines how long one must search""" + if limitloop is None: + limitloop = ctx.inf + loopnumber = 0 + variations = count_variations(V) + while ((variations < zero_number_block) and (loopnumber 0): + alpha = ctx.sqrt(u/v) + b= (alpha*a+b2)/(alpha+1) + else: + b = (a+b2)/2 + if fp_tolerance < 10: + w = ctx._fp.siegelz(b) + if abs(w)ITERATION_LIMIT)and(loopnumber>2)and(variations+2==zero_number_block): + dtMax=0 + dtSec=0 + kMax = 0 + for k1 in range(1,len(T)): + dt = T[k1]-T[k1-1] + if dt > dtMax: + kMax=k1 + dtSec = dtMax + dtMax = dt + elif (dtdtSec): + dtSec = dt + if dtMax>3*dtSec: + f = lambda x: ctx.rs_z(x,derivative=1) + t0=T[kMax-1] + t1 = T[kMax] + t=ctx.findroot(f, (t0,t1), solver ='illinois',verify=False, verbose=False) + v = ctx.siegelz(t) + if (t0 2*wpz: + index +=1 + precs = [precs[0] // 2 +3+2*index] + precs + ctx.prec = precs[0] + guard + r = ctx.findroot(lambda x:ctx.siegelz(x), (t0,t1), solver ='illinois', verbose=False) + #print "first step at", ctx.dps, "digits" + z=ctx.mpc(0.5,r) + for prec in precs[1:]: + ctx.prec = prec + guard + #print "refining to", ctx.dps, "digits" + znew = z - ctx.zeta(z) / ctx.zeta(z, derivative=1) + #print "difference", ctx.nstr(abs(z-znew)) + z=ctx.mpc(0.5,ctx.im(znew)) + return ctx.im(z) + +def sure_number_block(ctx, n): + """The number of good Rosser blocks needed to apply + Turing method + References: + R. P. Brent, On the Zeros of the Riemann Zeta Function + in the Critical Strip, Math. Comp. 33 (1979) 1361--1372 + T. Trudgian, Improvements to Turing Method, Math. Comp.""" + if n < 9*10**5: + return(2) + g = ctx.grampoint(n-100) + lg = ctx._fp.ln(g) + brent = 0.0061 * lg**2 +0.08*lg + trudgian = 0.0031 * lg**2 +0.11*lg + N = ctx.ceil(min(brent,trudgian)) + N = int(N) + return N + +def compute_triple_tvb(ctx, n): + t = ctx.grampoint(n) + v = ctx._fp.siegelz(t) + if ctx.mag(abs(v))400 000 000""" + sb = sure_number_block(ctx, n) + number_goodblocks = 0 + m2 = n-1 + t, v, b = compute_triple_tvb(ctx, m2) + Tf = [t] + Vf = [v] + while b < 0: + m2 += 1 + t,v,b = compute_triple_tvb(ctx, m2) + Tf.append(t) + Vf.append(v) + goodpoints = [m2] + T = [t] + V = [v] + while number_goodblocks < 2*sb: + m2 += 1 + t, v, b = compute_triple_tvb(ctx, m2) + T.append(t) + V.append(v) + while b < 0: + m2 += 1 + t,v,b = compute_triple_tvb(ctx, m2) + T.append(t) + V.append(v) + goodpoints.append(m2) + zn = len(T)-1 + A, B, separated =\ + separate_zeros_in_block(ctx, zn, T, V, limitloop=ITERATION_LIMIT, + fp_tolerance=fp_tolerance) + Tf.pop() + Tf.extend(A) + Vf.pop() + Vf.extend(B) + if separated: + number_goodblocks += 1 + else: + number_goodblocks = 0 + T = [t] + V = [v] + # Now the same procedure to the left + number_goodblocks = 0 + m2 = n-2 + t, v, b = compute_triple_tvb(ctx, m2) + Tf.insert(0,t) + Vf.insert(0,v) + while b < 0: + m2 -= 1 + t,v,b = compute_triple_tvb(ctx, m2) + Tf.insert(0,t) + Vf.insert(0,v) + goodpoints.insert(0,m2) + T = [t] + V = [v] + while number_goodblocks < 2*sb: + m2 -= 1 + t, v, b = compute_triple_tvb(ctx, m2) + T.insert(0,t) + V.insert(0,v) + while b < 0: + m2 -= 1 + t,v,b = compute_triple_tvb(ctx, m2) + T.insert(0,t) + V.insert(0,v) + goodpoints.insert(0,m2) + zn = len(T)-1 + A, B, separated =\ + separate_zeros_in_block(ctx, zn, T, V, limitloop=ITERATION_LIMIT, fp_tolerance=fp_tolerance) + A.pop() + Tf = A+Tf + B.pop() + Vf = B+Vf + if separated: + number_goodblocks += 1 + else: + number_goodblocks = 0 + T = [t] + V = [v] + r = goodpoints[2*sb] + lg = len(goodpoints) + s = goodpoints[lg-2*sb-1] + tr, vr, br = compute_triple_tvb(ctx, r) + ar = Tf.index(tr) + ts, vs, bs = compute_triple_tvb(ctx, s) + as1 = Tf.index(ts) + T = Tf[ar:as1+1] + V = Vf[ar:as1+1] + zn = s-r + A, B, separated =\ + separate_zeros_in_block(ctx, zn,T,V,limitloop=ITERATION_LIMIT, fp_tolerance=fp_tolerance) + if separated: + return (n-r-1,[r,s],A,B) + q = goodpoints[sb] + lg = len(goodpoints) + t = goodpoints[lg-sb-1] + tq, vq, bq = compute_triple_tvb(ctx, q) + aq = Tf.index(tq) + tt, vt, bt = compute_triple_tvb(ctx, t) + at = Tf.index(tt) + T = Tf[aq:at+1] + V = Vf[aq:at+1] + return (n-q-1,[q,t],T,V) + +def count_variations(V): + count = 0 + vold = V[0] + for n in range(1, len(V)): + vnew = V[n] + if vold*vnew < 0: + count +=1 + vold = vnew + return count + +def pattern_construct(ctx, block, T, V): + pattern = '(' + a = block[0] + b = block[1] + t0,v0,b0 = compute_triple_tvb(ctx, a) + k = 0 + k0 = 0 + for n in range(a+1,b+1): + t1,v1,b1 = compute_triple_tvb(ctx, n) + lgT =len(T) + while (k < lgT) and (T[k] <= t1): + k += 1 + L = V[k0:k] + L.append(v1) + L.insert(0,v0) + count = count_variations(L) + pattern = pattern + ("%s" % count) + if b1 > 0: + pattern = pattern + ')(' + k0 = k + t0,v0,b0 = t1,v1,b1 + pattern = pattern[:-1] + return pattern + +@defun +def zetazero(ctx, n, info=False, round=True): + r""" + Computes the `n`-th nontrivial zero of `\zeta(s)` on the critical line, + i.e. returns an approximation of the `n`-th largest complex number + `s = \frac{1}{2} + ti` for which `\zeta(s) = 0`. Equivalently, the + imaginary part `t` is a zero of the Z-function (:func:`~mpmath.siegelz`). + + **Examples** + + The first few zeros:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> zetazero(1) + (0.5 + 14.13472514173469379045725j) + >>> zetazero(2) + (0.5 + 21.02203963877155499262848j) + >>> zetazero(20) + (0.5 + 77.14484006887480537268266j) + + Verifying that the values are zeros:: + + >>> for n in range(1,5): + ... s = zetazero(n) + ... chop(zeta(s)), chop(siegelz(s.imag)) + ... + (0.0, 0.0) + (0.0, 0.0) + (0.0, 0.0) + (0.0, 0.0) + + Negative indices give the conjugate zeros (`n = 0` is undefined):: + + >>> zetazero(-1) + (0.5 - 14.13472514173469379045725j) + + :func:`~mpmath.zetazero` supports arbitrarily large `n` and arbitrary precision:: + + >>> mp.dps = 15 + >>> zetazero(1234567) + (0.5 + 727690.906948208j) + >>> mp.dps = 50 + >>> zetazero(1234567) + (0.5 + 727690.9069482075392389420041147142092708393819935j) + >>> chop(zeta(_)/_) + 0.0 + + with *info=True*, :func:`~mpmath.zetazero` gives additional information:: + + >>> mp.dps = 15 + >>> zetazero(542964976,info=True) + ((0.5 + 209039046.578535j), [542964969, 542964978], 6, '(013111110)') + + This means that the zero is between Gram points 542964969 and 542964978; + it is the 6-th zero between them. Finally (01311110) is the pattern + of zeros in this interval. The numbers indicate the number of zeros + in each Gram interval (Rosser blocks between parenthesis). In this case + there is only one Rosser block of length nine. + """ + n = int(n) + if n < 0: + return ctx.zetazero(-n).conjugate() + if n == 0: + raise ValueError("n must be nonzero") + wpinitial = ctx.prec + try: + wpz, fp_tolerance = comp_fp_tolerance(ctx, n) + ctx.prec = wpz + if n < 400000000: + my_zero_number, block, T, V =\ + find_rosser_block_zero(ctx, n) + else: + my_zero_number, block, T, V =\ + search_supergood_block(ctx, n, fp_tolerance) + zero_number_block = block[1]-block[0] + T, V, separated = separate_zeros_in_block(ctx, zero_number_block, T, V, + limitloop=ctx.inf, fp_tolerance=fp_tolerance) + if info: + pattern = pattern_construct(ctx,block,T,V) + prec = max(wpinitial, wpz) + t = separate_my_zero(ctx, my_zero_number, zero_number_block,T,V,prec) + v = ctx.mpc(0.5,t) + finally: + ctx.prec = wpinitial + if round: + v =+v + if info: + return (v,block,my_zero_number,pattern) + else: + return v + +def gram_index(ctx, t): + if t > 10**13: + wp = 3*ctx.log(t, 10) + else: + wp = 0 + prec = ctx.prec + try: + ctx.prec += wp + h = int(ctx.siegeltheta(t)/ctx.pi) + finally: + ctx.prec = prec + return(h) + +def count_to(ctx, t, T, V): + count = 0 + vold = V[0] + told = T[0] + tnew = T[1] + k = 1 + while tnew < t: + vnew = V[k] + if vold*vnew < 0: + count += 1 + vold = vnew + k += 1 + tnew = T[k] + a = ctx.siegelz(t) + if a*vold < 0: + count += 1 + return count + +def comp_fp_tolerance(ctx, n): + wpz = wpzeros(n*ctx.log(n)) + if n < 15*10**8: + fp_tolerance = 0.0005 + elif n <= 10**14: + fp_tolerance = 0.1 + else: + fp_tolerance = 100 + return wpz, fp_tolerance + +@defun +def nzeros(ctx, t): + r""" + Computes the number of zeros of the Riemann zeta function in + `(0,1) \times (0,t]`, usually denoted by `N(t)`. + + **Examples** + + The first zero has imaginary part between 14 and 15:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> nzeros(14) + 0 + >>> nzeros(15) + 1 + >>> zetazero(1) + (0.5 + 14.1347251417347j) + + Some closely spaced zeros:: + + >>> nzeros(10**7) + 21136125 + >>> zetazero(21136125) + (0.5 + 9999999.32718175j) + >>> zetazero(21136126) + (0.5 + 10000000.2400236j) + >>> nzeros(545439823.215) + 1500000001 + >>> zetazero(1500000001) + (0.5 + 545439823.201985j) + >>> zetazero(1500000002) + (0.5 + 545439823.325697j) + + This confirms the data given by J. van de Lune, + H. J. J. te Riele and D. T. Winter in 1986. + """ + if t < 14.1347251417347: + return 0 + x = gram_index(ctx, t) + k = int(ctx.floor(x)) + wpinitial = ctx.prec + wpz, fp_tolerance = comp_fp_tolerance(ctx, k) + ctx.prec = wpz + a = ctx.siegelz(t) + if k == -1 and a < 0: + return 0 + elif k == -1 and a > 0: + return 1 + if k+2 < 400000000: + Rblock = find_rosser_block_zero(ctx, k+2) + else: + Rblock = search_supergood_block(ctx, k+2, fp_tolerance) + n1, n2 = Rblock[1] + if n2-n1 == 1: + b = Rblock[3][0] + if a*b > 0: + ctx.prec = wpinitial + return k+1 + else: + ctx.prec = wpinitial + return k+2 + my_zero_number,block, T, V = Rblock + zero_number_block = n2-n1 + T, V, separated = separate_zeros_in_block(ctx,\ + zero_number_block, T, V,\ + limitloop=ctx.inf,\ + fp_tolerance=fp_tolerance) + n = count_to(ctx, t, T, V) + ctx.prec = wpinitial + return n+n1+1 + +@defun_wrapped +def backlunds(ctx, t): + r""" + Computes the function + `S(t) = \operatorname{arg} \zeta(\frac{1}{2} + it) / \pi`. + + See Titchmarsh Section 9.3 for details of the definition. + + **Examples** + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> backlunds(217.3) + 0.16302205431184 + + Generally, the value is a small number. At Gram points it is an integer, + frequently equal to 0:: + + >>> chop(backlunds(grampoint(200))) + 0.0 + >>> backlunds(extraprec(10)(grampoint)(211)) + 1.0 + >>> backlunds(extraprec(10)(grampoint)(232)) + -1.0 + + The number of zeros of the Riemann zeta function up to height `t` + satisfies `N(t) = \theta(t)/\pi + 1 + S(t)` (see :func:nzeros` and + :func:`siegeltheta`):: + + >>> t = 1234.55 + >>> nzeros(t) + 842 + >>> siegeltheta(t)/pi+1+backlunds(t) + 842.0 + + """ + return ctx.nzeros(t)-1-ctx.siegeltheta(t)/ctx.pi + + +""" +_ROSSER_EXCEPTIONS is a list of all exceptions to +Rosser's rule for n <= 400 000 000. + +Alternately the entry is of type [n,m], or a string. +The string is the zero pattern of the Block and the relevant +adjacent. For example (010)3 corresponds to a block +composed of three Gram intervals, the first ant third without +a zero and the intermediate with a zero. The next Gram interval +contain three zeros. So that in total we have 4 zeros in 4 Gram +blocks. n and m are the indices of the Gram points of this +interval of four Gram intervals. The Rosser exception is therefore +formed by the three Gram intervals that are signaled between +parenthesis. + +We have included also some Rosser's exceptions beyond n=400 000 000 +that are noted in the literature by some reason. + +The list is composed from the data published in the references: + +R. P. Brent, J. van de Lune, H. J. J. te Riele, D. T. Winter, +'On the Zeros of the Riemann Zeta Function in the Critical Strip. II', +Math. Comp. 39 (1982) 681--688. +See also Corrigenda in Math. Comp. 46 (1986) 771. + +J. van de Lune, H. J. J. te Riele, +'On the Zeros of the Riemann Zeta Function in the Critical Strip. III', +Math. Comp. 41 (1983) 759--767. +See also Corrigenda in Math. Comp. 46 (1986) 771. + +J. van de Lune, +'Sums of Equal Powers of Positive Integers', +Dissertation, +Vrije Universiteit te Amsterdam, Centrum voor Wiskunde en Informatica, +Amsterdam, 1984. + +Thanks to the authors all this papers and those others that have +contributed to make this possible. +""" + + + + + + + +_ROSSER_EXCEPTIONS = \ +[[13999525, 13999528], '(00)3', +[30783329, 30783332], '(00)3', +[30930926, 30930929], '3(00)', +[37592215, 37592218], '(00)3', +[40870156, 40870159], '(00)3', +[43628107, 43628110], '(00)3', +[46082042, 46082045], '(00)3', +[46875667, 46875670], '(00)3', +[49624540, 49624543], '3(00)', +[50799238, 50799241], '(00)3', +[55221453, 55221456], '3(00)', +[56948779, 56948782], '3(00)', +[60515663, 60515666], '(00)3', +[61331766, 61331770], '(00)40', +[69784843, 69784846], '3(00)', +[75052114, 75052117], '(00)3', +[79545240, 79545243], '3(00)', +[79652247, 79652250], '3(00)', +[83088043, 83088046], '(00)3', +[83689522, 83689525], '3(00)', +[85348958, 85348961], '(00)3', +[86513820, 86513823], '(00)3', +[87947596, 87947599], '3(00)', +[88600095, 88600098], '(00)3', +[93681183, 93681186], '(00)3', +[100316551, 100316554], '3(00)', +[100788444, 100788447], '(00)3', +[106236172, 106236175], '(00)3', +[106941327, 106941330], '3(00)', +[107287955, 107287958], '(00)3', +[107532016, 107532019], '3(00)', +[110571044, 110571047], '(00)3', +[111885253, 111885256], '3(00)', +[113239783, 113239786], '(00)3', +[120159903, 120159906], '(00)3', +[121424391, 121424394], '3(00)', +[121692931, 121692934], '3(00)', +[121934170, 121934173], '3(00)', +[122612848, 122612851], '3(00)', +[126116567, 126116570], '(00)3', +[127936513, 127936516], '(00)3', +[128710277, 128710280], '3(00)', +[129398902, 129398905], '3(00)', +[130461096, 130461099], '3(00)', +[131331947, 131331950], '3(00)', +[137334071, 137334074], '3(00)', +[137832603, 137832606], '(00)3', +[138799471, 138799474], '3(00)', +[139027791, 139027794], '(00)3', +[141617806, 141617809], '(00)3', +[144454931, 144454934], '(00)3', +[145402379, 145402382], '3(00)', +[146130245, 146130248], '3(00)', +[147059770, 147059773], '(00)3', +[147896099, 147896102], '3(00)', +[151097113, 151097116], '(00)3', +[152539438, 152539441], '(00)3', +[152863168, 152863171], '3(00)', +[153522726, 153522729], '3(00)', +[155171524, 155171527], '3(00)', +[155366607, 155366610], '(00)3', +[157260686, 157260689], '3(00)', +[157269224, 157269227], '(00)3', +[157755123, 157755126], '(00)3', +[158298484, 158298487], '3(00)', +[160369050, 160369053], '3(00)', +[162962787, 162962790], '(00)3', +[163724709, 163724712], '(00)3', +[164198113, 164198116], '3(00)', +[164689301, 164689305], '(00)40', +[164880228, 164880231], '3(00)', +[166201932, 166201935], '(00)3', +[168573836, 168573839], '(00)3', +[169750763, 169750766], '(00)3', +[170375507, 170375510], '(00)3', +[170704879, 170704882], '3(00)', +[172000992, 172000995], '3(00)', +[173289941, 173289944], '(00)3', +[173737613, 173737616], '3(00)', +[174102513, 174102516], '(00)3', +[174284990, 174284993], '(00)3', +[174500513, 174500516], '(00)3', +[175710609, 175710612], '(00)3', +[176870843, 176870846], '3(00)', +[177332732, 177332735], '3(00)', +[177902861, 177902864], '3(00)', +[179979095, 179979098], '(00)3', +[181233726, 181233729], '3(00)', +[181625435, 181625438], '(00)3', +[182105255, 182105259], '22(00)', +[182223559, 182223562], '3(00)', +[191116404, 191116407], '3(00)', +[191165599, 191165602], '3(00)', +[191297535, 191297539], '(00)22', +[192485616, 192485619], '(00)3', +[193264634, 193264638], '22(00)', +[194696968, 194696971], '(00)3', +[195876805, 195876808], '(00)3', +[195916548, 195916551], '3(00)', +[196395160, 196395163], '3(00)', +[196676303, 196676306], '(00)3', +[197889882, 197889885], '3(00)', +[198014122, 198014125], '(00)3', +[199235289, 199235292], '(00)3', +[201007375, 201007378], '(00)3', +[201030605, 201030608], '3(00)', +[201184290, 201184293], '3(00)', +[201685414, 201685418], '(00)22', +[202762875, 202762878], '3(00)', +[202860957, 202860960], '3(00)', +[203832577, 203832580], '3(00)', +[205880544, 205880547], '(00)3', +[206357111, 206357114], '(00)3', +[207159767, 207159770], '3(00)', +[207167343, 207167346], '3(00)', +[207482539, 207482543], '3(010)', +[207669540, 207669543], '3(00)', +[208053426, 208053429], '(00)3', +[208110027, 208110030], '3(00)', +[209513826, 209513829], '3(00)', +[212623522, 212623525], '(00)3', +[213841715, 213841718], '(00)3', +[214012333, 214012336], '(00)3', +[214073567, 214073570], '(00)3', +[215170600, 215170603], '3(00)', +[215881039, 215881042], '3(00)', +[216274604, 216274607], '3(00)', +[216957120, 216957123], '3(00)', +[217323208, 217323211], '(00)3', +[218799264, 218799267], '(00)3', +[218803557, 218803560], '3(00)', +[219735146, 219735149], '(00)3', +[219830062, 219830065], '3(00)', +[219897904, 219897907], '(00)3', +[221205545, 221205548], '(00)3', +[223601929, 223601932], '(00)3', +[223907076, 223907079], '3(00)', +[223970397, 223970400], '(00)3', +[224874044, 224874048], '22(00)', +[225291157, 225291160], '(00)3', +[227481734, 227481737], '(00)3', +[228006442, 228006445], '3(00)', +[228357900, 228357903], '(00)3', +[228386399, 228386402], '(00)3', +[228907446, 228907449], '(00)3', +[228984552, 228984555], '3(00)', +[229140285, 229140288], '3(00)', +[231810024, 231810027], '(00)3', +[232838062, 232838065], '3(00)', +[234389088, 234389091], '3(00)', +[235588194, 235588197], '(00)3', +[236645695, 236645698], '(00)3', +[236962876, 236962879], '3(00)', +[237516723, 237516727], '04(00)', +[240004911, 240004914], '(00)3', +[240221306, 240221309], '3(00)', +[241389213, 241389217], '(010)3', +[241549003, 241549006], '(00)3', +[241729717, 241729720], '(00)3', +[241743684, 241743687], '3(00)', +[243780200, 243780203], '3(00)', +[243801317, 243801320], '(00)3', +[244122072, 244122075], '(00)3', +[244691224, 244691227], '3(00)', +[244841577, 244841580], '(00)3', +[245813461, 245813464], '(00)3', +[246299475, 246299478], '(00)3', +[246450176, 246450179], '3(00)', +[249069349, 249069352], '(00)3', +[250076378, 250076381], '(00)3', +[252442157, 252442160], '3(00)', +[252904231, 252904234], '3(00)', +[255145220, 255145223], '(00)3', +[255285971, 255285974], '3(00)', +[256713230, 256713233], '(00)3', +[257992082, 257992085], '(00)3', +[258447955, 258447959], '22(00)', +[259298045, 259298048], '3(00)', +[262141503, 262141506], '(00)3', +[263681743, 263681746], '3(00)', +[266527881, 266527885], '(010)3', +[266617122, 266617125], '(00)3', +[266628044, 266628047], '3(00)', +[267305763, 267305766], '(00)3', +[267388404, 267388407], '3(00)', +[267441672, 267441675], '3(00)', +[267464886, 267464889], '(00)3', +[267554907, 267554910], '3(00)', +[269787480, 269787483], '(00)3', +[270881434, 270881437], '(00)3', +[270997583, 270997586], '3(00)', +[272096378, 272096381], '3(00)', +[272583009, 272583012], '(00)3', +[274190881, 274190884], '3(00)', +[274268747, 274268750], '(00)3', +[275297429, 275297432], '3(00)', +[275545476, 275545479], '3(00)', +[275898479, 275898482], '3(00)', +[275953000, 275953003], '(00)3', +[277117197, 277117201], '(00)22', +[277447310, 277447313], '3(00)', +[279059657, 279059660], '3(00)', +[279259144, 279259147], '3(00)', +[279513636, 279513639], '3(00)', +[279849069, 279849072], '3(00)', +[280291419, 280291422], '(00)3', +[281449425, 281449428], '3(00)', +[281507953, 281507956], '3(00)', +[281825600, 281825603], '(00)3', +[282547093, 282547096], '3(00)', +[283120963, 283120966], '3(00)', +[283323493, 283323496], '(00)3', +[284764535, 284764538], '3(00)', +[286172639, 286172642], '3(00)', +[286688824, 286688827], '(00)3', +[287222172, 287222175], '3(00)', +[287235534, 287235537], '3(00)', +[287304861, 287304864], '3(00)', +[287433571, 287433574], '(00)3', +[287823551, 287823554], '(00)3', +[287872422, 287872425], '3(00)', +[288766615, 288766618], '3(00)', +[290122963, 290122966], '3(00)', +[290450849, 290450853], '(00)22', +[291426141, 291426144], '3(00)', +[292810353, 292810356], '3(00)', +[293109861, 293109864], '3(00)', +[293398054, 293398057], '3(00)', +[294134426, 294134429], '3(00)', +[294216438, 294216441], '(00)3', +[295367141, 295367144], '3(00)', +[297834111, 297834114], '3(00)', +[299099969, 299099972], '3(00)', +[300746958, 300746961], '3(00)', +[301097423, 301097426], '(00)3', +[301834209, 301834212], '(00)3', +[302554791, 302554794], '(00)3', +[303497445, 303497448], '3(00)', +[304165344, 304165347], '3(00)', +[304790218, 304790222], '3(010)', +[305302352, 305302355], '(00)3', +[306785996, 306785999], '3(00)', +[307051443, 307051446], '3(00)', +[307481539, 307481542], '3(00)', +[308605569, 308605572], '3(00)', +[309237610, 309237613], '3(00)', +[310509287, 310509290], '(00)3', +[310554057, 310554060], '3(00)', +[310646345, 310646348], '3(00)', +[311274896, 311274899], '(00)3', +[311894272, 311894275], '3(00)', +[312269470, 312269473], '(00)3', +[312306601, 312306605], '(00)40', +[312683193, 312683196], '3(00)', +[314499804, 314499807], '3(00)', +[314636802, 314636805], '(00)3', +[314689897, 314689900], '3(00)', +[314721319, 314721322], '3(00)', +[316132890, 316132893], '3(00)', +[316217470, 316217474], '(010)3', +[316465705, 316465708], '3(00)', +[316542790, 316542793], '(00)3', +[320822347, 320822350], '3(00)', +[321733242, 321733245], '3(00)', +[324413970, 324413973], '(00)3', +[325950140, 325950143], '(00)3', +[326675884, 326675887], '(00)3', +[326704208, 326704211], '3(00)', +[327596247, 327596250], '3(00)', +[328123172, 328123175], '3(00)', +[328182212, 328182215], '(00)3', +[328257498, 328257501], '3(00)', +[328315836, 328315839], '(00)3', +[328800974, 328800977], '(00)3', +[328998509, 328998512], '3(00)', +[329725370, 329725373], '(00)3', +[332080601, 332080604], '(00)3', +[332221246, 332221249], '(00)3', +[332299899, 332299902], '(00)3', +[332532822, 332532825], '(00)3', +[333334544, 333334548], '(00)22', +[333881266, 333881269], '3(00)', +[334703267, 334703270], '3(00)', +[334875138, 334875141], '3(00)', +[336531451, 336531454], '3(00)', +[336825907, 336825910], '(00)3', +[336993167, 336993170], '(00)3', +[337493998, 337494001], '3(00)', +[337861034, 337861037], '3(00)', +[337899191, 337899194], '(00)3', +[337958123, 337958126], '(00)3', +[342331982, 342331985], '3(00)', +[342676068, 342676071], '3(00)', +[347063781, 347063784], '3(00)', +[347697348, 347697351], '3(00)', +[347954319, 347954322], '3(00)', +[348162775, 348162778], '3(00)', +[349210702, 349210705], '(00)3', +[349212913, 349212916], '3(00)', +[349248650, 349248653], '(00)3', +[349913500, 349913503], '3(00)', +[350891529, 350891532], '3(00)', +[351089323, 351089326], '3(00)', +[351826158, 351826161], '3(00)', +[352228580, 352228583], '(00)3', +[352376244, 352376247], '3(00)', +[352853758, 352853761], '(00)3', +[355110439, 355110442], '(00)3', +[355808090, 355808094], '(00)40', +[355941556, 355941559], '3(00)', +[356360231, 356360234], '(00)3', +[356586657, 356586660], '3(00)', +[356892926, 356892929], '(00)3', +[356908232, 356908235], '3(00)', +[357912730, 357912733], '3(00)', +[358120344, 358120347], '3(00)', +[359044096, 359044099], '(00)3', +[360819357, 360819360], '3(00)', +[361399662, 361399666], '(010)3', +[362361315, 362361318], '(00)3', +[363610112, 363610115], '(00)3', +[363964804, 363964807], '3(00)', +[364527375, 364527378], '(00)3', +[365090327, 365090330], '(00)3', +[365414539, 365414542], '3(00)', +[366738474, 366738477], '3(00)', +[368714778, 368714783], '04(010)', +[368831545, 368831548], '(00)3', +[368902387, 368902390], '(00)3', +[370109769, 370109772], '3(00)', +[370963333, 370963336], '3(00)', +[372541136, 372541140], '3(010)', +[372681562, 372681565], '(00)3', +[373009410, 373009413], '(00)3', +[373458970, 373458973], '3(00)', +[375648658, 375648661], '3(00)', +[376834728, 376834731], '3(00)', +[377119945, 377119948], '(00)3', +[377335703, 377335706], '(00)3', +[378091745, 378091748], '3(00)', +[379139522, 379139525], '3(00)', +[380279160, 380279163], '(00)3', +[380619442, 380619445], '3(00)', +[381244231, 381244234], '3(00)', +[382327446, 382327450], '(010)3', +[382357073, 382357076], '3(00)', +[383545479, 383545482], '3(00)', +[384363766, 384363769], '(00)3', +[384401786, 384401790], '22(00)', +[385198212, 385198215], '3(00)', +[385824476, 385824479], '(00)3', +[385908194, 385908197], '3(00)', +[386946806, 386946809], '3(00)', +[387592175, 387592179], '22(00)', +[388329293, 388329296], '(00)3', +[388679566, 388679569], '3(00)', +[388832142, 388832145], '3(00)', +[390087103, 390087106], '(00)3', +[390190926, 390190930], '(00)22', +[390331207, 390331210], '3(00)', +[391674495, 391674498], '3(00)', +[391937831, 391937834], '3(00)', +[391951632, 391951636], '(00)22', +[392963986, 392963989], '(00)3', +[393007921, 393007924], '3(00)', +[393373210, 393373213], '3(00)', +[393759572, 393759575], '(00)3', +[394036662, 394036665], '(00)3', +[395813866, 395813869], '(00)3', +[395956690, 395956693], '3(00)', +[396031670, 396031673], '3(00)', +[397076433, 397076436], '3(00)', +[397470601, 397470604], '3(00)', +[398289458, 398289461], '3(00)', +# +[368714778, 368714783], '04(010)', +[437953499, 437953504], '04(010)', +[526196233, 526196238], '032(00)', +[744719566, 744719571], '(010)40', +[750375857, 750375862], '032(00)', +[958241932, 958241937], '04(010)', +[983377342, 983377347], '(00)410', +[1003780080, 1003780085], '04(010)', +[1070232754, 1070232759], '(00)230', +[1209834865, 1209834870], '032(00)', +[1257209100, 1257209105], '(00)410', +[1368002233, 1368002238], '(00)230' +] diff --git a/lib/python3.12/site-packages/mpmath/identification.py b/lib/python3.12/site-packages/mpmath/identification.py new file mode 100644 index 0000000000000000000000000000000000000000..226f62d3fe9cacedbd9ba2b1e66ff0ad017fa604 --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/identification.py @@ -0,0 +1,844 @@ +""" +Implements the PSLQ algorithm for integer relation detection, +and derivative algorithms for constant recognition. +""" + +from .libmp.backend import xrange +from .libmp import int_types, sqrt_fixed + +# round to nearest integer (can be done more elegantly...) +def round_fixed(x, prec): + return ((x + (1<<(prec-1))) >> prec) << prec + +class IdentificationMethods(object): + pass + + +def pslq(ctx, x, tol=None, maxcoeff=1000, maxsteps=100, verbose=False): + r""" + Given a vector of real numbers `x = [x_0, x_1, ..., x_n]`, ``pslq(x)`` + uses the PSLQ algorithm to find a list of integers + `[c_0, c_1, ..., c_n]` such that + + .. math :: + + |c_1 x_1 + c_2 x_2 + ... + c_n x_n| < \mathrm{tol} + + and such that `\max |c_k| < \mathrm{maxcoeff}`. If no such vector + exists, :func:`~mpmath.pslq` returns ``None``. The tolerance defaults to + 3/4 of the working precision. + + **Examples** + + Find rational approximations for `\pi`:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> pslq([-1, pi], tol=0.01) + [22, 7] + >>> pslq([-1, pi], tol=0.001) + [355, 113] + >>> mpf(22)/7; mpf(355)/113; +pi + 3.14285714285714 + 3.14159292035398 + 3.14159265358979 + + Pi is not a rational number with denominator less than 1000:: + + >>> pslq([-1, pi]) + >>> + + To within the standard precision, it can however be approximated + by at least one rational number with denominator less than `10^{12}`:: + + >>> p, q = pslq([-1, pi], maxcoeff=10**12) + >>> print(p); print(q) + 238410049439 + 75888275702 + >>> mpf(p)/q + 3.14159265358979 + + The PSLQ algorithm can be applied to long vectors. For example, + we can investigate the rational (in)dependence of integer square + roots:: + + >>> mp.dps = 30 + >>> pslq([sqrt(n) for n in range(2, 5+1)]) + >>> + >>> pslq([sqrt(n) for n in range(2, 6+1)]) + >>> + >>> pslq([sqrt(n) for n in range(2, 8+1)]) + [2, 0, 0, 0, 0, 0, -1] + + **Machin formulas** + + A famous formula for `\pi` is Machin's, + + .. math :: + + \frac{\pi}{4} = 4 \operatorname{acot} 5 - \operatorname{acot} 239 + + There are actually infinitely many formulas of this type. Two + others are + + .. math :: + + \frac{\pi}{4} = \operatorname{acot} 1 + + \frac{\pi}{4} = 12 \operatorname{acot} 49 + 32 \operatorname{acot} 57 + + 5 \operatorname{acot} 239 + 12 \operatorname{acot} 110443 + + We can easily verify the formulas using the PSLQ algorithm:: + + >>> mp.dps = 30 + >>> pslq([pi/4, acot(1)]) + [1, -1] + >>> pslq([pi/4, acot(5), acot(239)]) + [1, -4, 1] + >>> pslq([pi/4, acot(49), acot(57), acot(239), acot(110443)]) + [1, -12, -32, 5, -12] + + We could try to generate a custom Machin-like formula by running + the PSLQ algorithm with a few inverse cotangent values, for example + acot(2), acot(3) ... acot(10). Unfortunately, there is a linear + dependence among these values, resulting in only that dependence + being detected, with a zero coefficient for `\pi`:: + + >>> pslq([pi] + [acot(n) for n in range(2,11)]) + [0, 1, -1, 0, 0, 0, -1, 0, 0, 0] + + We get better luck by removing linearly dependent terms:: + + >>> pslq([pi] + [acot(n) for n in range(2,11) if n not in (3, 5)]) + [1, -8, 0, 0, 4, 0, 0, 0] + + In other words, we found the following formula:: + + >>> 8*acot(2) - 4*acot(7) + 3.14159265358979323846264338328 + >>> +pi + 3.14159265358979323846264338328 + + **Algorithm** + + This is a fairly direct translation to Python of the pseudocode given by + David Bailey, "The PSLQ Integer Relation Algorithm": + http://www.cecm.sfu.ca/organics/papers/bailey/paper/html/node3.html + + The present implementation uses fixed-point instead of floating-point + arithmetic, since this is significantly (about 7x) faster. + """ + + n = len(x) + if n < 2: + raise ValueError("n cannot be less than 2") + + # At too low precision, the algorithm becomes meaningless + prec = ctx.prec + if prec < 53: + raise ValueError("prec cannot be less than 53") + + if verbose and prec // max(2,n) < 5: + print("Warning: precision for PSLQ may be too low") + + target = int(prec * 0.75) + + if tol is None: + tol = ctx.mpf(2)**(-target) + else: + tol = ctx.convert(tol) + + extra = 60 + prec += extra + + if verbose: + print("PSLQ using prec %i and tol %s" % (prec, ctx.nstr(tol))) + + tol = ctx.to_fixed(tol, prec) + assert tol + + # Convert to fixed-point numbers. The dummy None is added so we can + # use 1-based indexing. (This just allows us to be consistent with + # Bailey's indexing. The algorithm is 100 lines long, so debugging + # a single wrong index can be painful.) + x = [None] + [ctx.to_fixed(ctx.mpf(xk), prec) for xk in x] + + # Sanity check on magnitudes + minx = min(abs(xx) for xx in x[1:]) + if not minx: + raise ValueError("PSLQ requires a vector of nonzero numbers") + if minx < tol//100: + if verbose: + print("STOPPING: (one number is too small)") + return None + + g = sqrt_fixed((4<> prec) + s[k] = sqrt_fixed(t, prec) + t = s[1] + y = x[:] + for k in xrange(1, n+1): + y[k] = (x[k] << prec) // t + s[k] = (s[k] << prec) // t + # step 3 + for i in xrange(1, n+1): + for j in xrange(i+1, n): + H[i,j] = 0 + if i <= n-1: + if s[i]: + H[i,i] = (s[i+1] << prec) // s[i] + else: + H[i,i] = 0 + for j in range(1, i): + sjj1 = s[j]*s[j+1] + if sjj1: + H[i,j] = ((-y[i]*y[j])<> prec) + for k in xrange(1, j+1): + H[i,k] = H[i,k] - (t*H[j,k] >> prec) + for k in xrange(1, n+1): + A[i,k] = A[i,k] - (t*A[j,k] >> prec) + B[k,j] = B[k,j] + (t*B[k,i] >> prec) + # Main algorithm + for REP in range(maxsteps): + # Step 1 + m = -1 + szmax = -1 + for i in range(1, n): + h = H[i,i] + sz = (g**i * abs(h)) >> (prec*(i-1)) + if sz > szmax: + m = i + szmax = sz + # Step 2 + y[m], y[m+1] = y[m+1], y[m] + for i in xrange(1,n+1): H[m,i], H[m+1,i] = H[m+1,i], H[m,i] + for i in xrange(1,n+1): A[m,i], A[m+1,i] = A[m+1,i], A[m,i] + for i in xrange(1,n+1): B[i,m], B[i,m+1] = B[i,m+1], B[i,m] + # Step 3 + if m <= n - 2: + t0 = sqrt_fixed((H[m,m]**2 + H[m,m+1]**2)>>prec, prec) + # A zero element probably indicates that the precision has + # been exhausted. XXX: this could be spurious, due to + # using fixed-point arithmetic + if not t0: + break + t1 = (H[m,m] << prec) // t0 + t2 = (H[m,m+1] << prec) // t0 + for i in xrange(m, n+1): + t3 = H[i,m] + t4 = H[i,m+1] + H[i,m] = (t1*t3+t2*t4) >> prec + H[i,m+1] = (-t2*t3+t1*t4) >> prec + # Step 4 + for i in xrange(m+1, n+1): + for j in xrange(min(i-1, m+1), 0, -1): + try: + t = round_fixed((H[i,j] << prec)//H[j,j], prec) + # Precision probably exhausted + except ZeroDivisionError: + break + y[j] = y[j] + ((t*y[i]) >> prec) + for k in xrange(1, j+1): + H[i,k] = H[i,k] - (t*H[j,k] >> prec) + for k in xrange(1, n+1): + A[i,k] = A[i,k] - (t*A[j,k] >> prec) + B[k,j] = B[k,j] + (t*B[k,i] >> prec) + # Until a relation is found, the error typically decreases + # slowly (e.g. a factor 1-10) with each step TODO: we could + # compare err from two successive iterations. If there is a + # large drop (several orders of magnitude), that indicates a + # "high quality" relation was detected. Reporting this to + # the user somehow might be useful. + best_err = maxcoeff<> prec) for j in \ + range(1,n+1)] + if max(abs(v) for v in vec) < maxcoeff: + if verbose: + print("FOUND relation at iter %i/%i, error: %s" % \ + (REP, maxsteps, ctx.nstr(err / ctx.mpf(2)**prec, 1))) + return vec + best_err = min(err, best_err) + # Calculate a lower bound for the norm. We could do this + # more exactly (using the Euclidean norm) but there is probably + # no practical benefit. + recnorm = max(abs(h) for h in H.values()) + if recnorm: + norm = ((1 << (2*prec)) // recnorm) >> prec + norm //= 100 + else: + norm = ctx.inf + if verbose: + print("%i/%i: Error: %8s Norm: %s" % \ + (REP, maxsteps, ctx.nstr(best_err / ctx.mpf(2)**prec, 1), norm)) + if norm >= maxcoeff: + break + if verbose: + print("CANCELLING after step %i/%i." % (REP, maxsteps)) + print("Could not find an integer relation. Norm bound: %s" % norm) + return None + +def findpoly(ctx, x, n=1, **kwargs): + r""" + ``findpoly(x, n)`` returns the coefficients of an integer + polynomial `P` of degree at most `n` such that `P(x) \approx 0`. + If no polynomial having `x` as a root can be found, + :func:`~mpmath.findpoly` returns ``None``. + + :func:`~mpmath.findpoly` works by successively calling :func:`~mpmath.pslq` with + the vectors `[1, x]`, `[1, x, x^2]`, `[1, x, x^2, x^3]`, ..., + `[1, x, x^2, .., x^n]` as input. Keyword arguments given to + :func:`~mpmath.findpoly` are forwarded verbatim to :func:`~mpmath.pslq`. In + particular, you can specify a tolerance for `P(x)` with ``tol`` + and a maximum permitted coefficient size with ``maxcoeff``. + + For large values of `n`, it is recommended to run :func:`~mpmath.findpoly` + at high precision; preferably 50 digits or more. + + **Examples** + + By default (degree `n = 1`), :func:`~mpmath.findpoly` simply finds a linear + polynomial with a rational root:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> findpoly(0.7) + [-10, 7] + + The generated coefficient list is valid input to ``polyval`` and + ``polyroots``:: + + >>> nprint(polyval(findpoly(phi, 2), phi), 1) + -2.0e-16 + >>> for r in polyroots(findpoly(phi, 2)): + ... print(r) + ... + -0.618033988749895 + 1.61803398874989 + + Numbers of the form `m + n \sqrt p` for integers `(m, n, p)` are + solutions to quadratic equations. As we find here, `1+\sqrt 2` + is a root of the polynomial `x^2 - 2x - 1`:: + + >>> findpoly(1+sqrt(2), 2) + [1, -2, -1] + >>> findroot(lambda x: x**2 - 2*x - 1, 1) + 2.4142135623731 + + Despite only containing square roots, the following number results + in a polynomial of degree 4:: + + >>> findpoly(sqrt(2)+sqrt(3), 4) + [1, 0, -10, 0, 1] + + In fact, `x^4 - 10x^2 + 1` is the *minimal polynomial* of + `r = \sqrt 2 + \sqrt 3`, meaning that a rational polynomial of + lower degree having `r` as a root does not exist. Given sufficient + precision, :func:`~mpmath.findpoly` will usually find the correct + minimal polynomial of a given algebraic number. + + **Non-algebraic numbers** + + If :func:`~mpmath.findpoly` fails to find a polynomial with given + coefficient size and tolerance constraints, that means no such + polynomial exists. + + We can verify that `\pi` is not an algebraic number of degree 3 with + coefficients less than 1000:: + + >>> mp.dps = 15 + >>> findpoly(pi, 3) + >>> + + It is always possible to find an algebraic approximation of a number + using one (or several) of the following methods: + + 1. Increasing the permitted degree + 2. Allowing larger coefficients + 3. Reducing the tolerance + + One example of each method is shown below:: + + >>> mp.dps = 15 + >>> findpoly(pi, 4) + [95, -545, 863, -183, -298] + >>> findpoly(pi, 3, maxcoeff=10000) + [836, -1734, -2658, -457] + >>> findpoly(pi, 3, tol=1e-7) + [-4, 22, -29, -2] + + It is unknown whether Euler's constant is transcendental (or even + irrational). We can use :func:`~mpmath.findpoly` to check that if is + an algebraic number, its minimal polynomial must have degree + at least 7 and a coefficient of magnitude at least 1000000:: + + >>> mp.dps = 200 + >>> findpoly(euler, 6, maxcoeff=10**6, tol=1e-100, maxsteps=1000) + >>> + + Note that the high precision and strict tolerance is necessary + for such high-degree runs, since otherwise unwanted low-accuracy + approximations will be detected. It may also be necessary to set + maxsteps high to prevent a premature exit (before the coefficient + bound has been reached). Running with ``verbose=True`` to get an + idea what is happening can be useful. + """ + x = ctx.mpf(x) + if n < 1: + raise ValueError("n cannot be less than 1") + if x == 0: + return [1, 0] + xs = [ctx.mpf(1)] + for i in range(1,n+1): + xs.append(x**i) + a = ctx.pslq(xs, **kwargs) + if a is not None: + return a[::-1] + +def fracgcd(p, q): + x, y = p, q + while y: + x, y = y, x % y + if x != 1: + p //= x + q //= x + if q == 1: + return p + return p, q + +def pslqstring(r, constants): + q = r[0] + r = r[1:] + s = [] + for i in range(len(r)): + p = r[i] + if p: + z = fracgcd(-p,q) + cs = constants[i][1] + if cs == '1': + cs = '' + else: + cs = '*' + cs + if isinstance(z, int_types): + if z > 0: term = str(z) + cs + else: term = ("(%s)" % z) + cs + else: + term = ("(%s/%s)" % z) + cs + s.append(term) + s = ' + '.join(s) + if '+' in s or '*' in s: + s = '(' + s + ')' + return s or '0' + +def prodstring(r, constants): + q = r[0] + r = r[1:] + num = [] + den = [] + for i in range(len(r)): + p = r[i] + if p: + z = fracgcd(-p,q) + cs = constants[i][1] + if isinstance(z, int_types): + if abs(z) == 1: t = cs + else: t = '%s**%s' % (cs, abs(z)) + ([num,den][z<0]).append(t) + else: + t = '%s**(%s/%s)' % (cs, abs(z[0]), z[1]) + ([num,den][z[0]<0]).append(t) + num = '*'.join(num) + den = '*'.join(den) + if num and den: return "(%s)/(%s)" % (num, den) + if num: return num + if den: return "1/(%s)" % den + +def quadraticstring(ctx,t,a,b,c): + if c < 0: + a,b,c = -a,-b,-c + u1 = (-b+ctx.sqrt(b**2-4*a*c))/(2*c) + u2 = (-b-ctx.sqrt(b**2-4*a*c))/(2*c) + if abs(u1-t) < abs(u2-t): + if b: s = '((%s+sqrt(%s))/%s)' % (-b,b**2-4*a*c,2*c) + else: s = '(sqrt(%s)/%s)' % (-4*a*c,2*c) + else: + if b: s = '((%s-sqrt(%s))/%s)' % (-b,b**2-4*a*c,2*c) + else: s = '(-sqrt(%s)/%s)' % (-4*a*c,2*c) + return s + +# Transformation y = f(x,c), with inverse function x = f(y,c) +# The third entry indicates whether the transformation is +# redundant when c = 1 +transforms = [ + (lambda ctx,x,c: x*c, '$y/$c', 0), + (lambda ctx,x,c: x/c, '$c*$y', 1), + (lambda ctx,x,c: c/x, '$c/$y', 0), + (lambda ctx,x,c: (x*c)**2, 'sqrt($y)/$c', 0), + (lambda ctx,x,c: (x/c)**2, '$c*sqrt($y)', 1), + (lambda ctx,x,c: (c/x)**2, '$c/sqrt($y)', 0), + (lambda ctx,x,c: c*x**2, 'sqrt($y)/sqrt($c)', 1), + (lambda ctx,x,c: x**2/c, 'sqrt($c)*sqrt($y)', 1), + (lambda ctx,x,c: c/x**2, 'sqrt($c)/sqrt($y)', 1), + (lambda ctx,x,c: ctx.sqrt(x*c), '$y**2/$c', 0), + (lambda ctx,x,c: ctx.sqrt(x/c), '$c*$y**2', 1), + (lambda ctx,x,c: ctx.sqrt(c/x), '$c/$y**2', 0), + (lambda ctx,x,c: c*ctx.sqrt(x), '$y**2/$c**2', 1), + (lambda ctx,x,c: ctx.sqrt(x)/c, '$c**2*$y**2', 1), + (lambda ctx,x,c: c/ctx.sqrt(x), '$c**2/$y**2', 1), + (lambda ctx,x,c: ctx.exp(x*c), 'log($y)/$c', 0), + (lambda ctx,x,c: ctx.exp(x/c), '$c*log($y)', 1), + (lambda ctx,x,c: ctx.exp(c/x), '$c/log($y)', 0), + (lambda ctx,x,c: c*ctx.exp(x), 'log($y/$c)', 1), + (lambda ctx,x,c: ctx.exp(x)/c, 'log($c*$y)', 1), + (lambda ctx,x,c: c/ctx.exp(x), 'log($c/$y)', 0), + (lambda ctx,x,c: ctx.ln(x*c), 'exp($y)/$c', 0), + (lambda ctx,x,c: ctx.ln(x/c), '$c*exp($y)', 1), + (lambda ctx,x,c: ctx.ln(c/x), '$c/exp($y)', 0), + (lambda ctx,x,c: c*ctx.ln(x), 'exp($y/$c)', 1), + (lambda ctx,x,c: ctx.ln(x)/c, 'exp($c*$y)', 1), + (lambda ctx,x,c: c/ctx.ln(x), 'exp($c/$y)', 0), +] + +def identify(ctx, x, constants=[], tol=None, maxcoeff=1000, full=False, + verbose=False): + r""" + Given a real number `x`, ``identify(x)`` attempts to find an exact + formula for `x`. This formula is returned as a string. If no match + is found, ``None`` is returned. With ``full=True``, a list of + matching formulas is returned. + + As a simple example, :func:`~mpmath.identify` will find an algebraic + formula for the golden ratio:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> identify(phi) + '((1+sqrt(5))/2)' + + :func:`~mpmath.identify` can identify simple algebraic numbers and simple + combinations of given base constants, as well as certain basic + transformations thereof. More specifically, :func:`~mpmath.identify` + looks for the following: + + 1. Fractions + 2. Quadratic algebraic numbers + 3. Rational linear combinations of the base constants + 4. Any of the above after first transforming `x` into `f(x)` where + `f(x)` is `1/x`, `\sqrt x`, `x^2`, `\log x` or `\exp x`, either + directly or with `x` or `f(x)` multiplied or divided by one of + the base constants + 5. Products of fractional powers of the base constants and + small integers + + Base constants can be given as a list of strings representing mpmath + expressions (:func:`~mpmath.identify` will ``eval`` the strings to numerical + values and use the original strings for the output), or as a dict of + formula:value pairs. + + In order not to produce spurious results, :func:`~mpmath.identify` should + be used with high precision; preferably 50 digits or more. + + **Examples** + + Simple identifications can be performed safely at standard + precision. Here the default recognition of rational, algebraic, + and exp/log of algebraic numbers is demonstrated:: + + >>> mp.dps = 15 + >>> identify(0.22222222222222222) + '(2/9)' + >>> identify(1.9662210973805663) + 'sqrt(((24+sqrt(48))/8))' + >>> identify(4.1132503787829275) + 'exp((sqrt(8)/2))' + >>> identify(0.881373587019543) + 'log(((2+sqrt(8))/2))' + + By default, :func:`~mpmath.identify` does not recognize `\pi`. At standard + precision it finds a not too useful approximation. At slightly + increased precision, this approximation is no longer accurate + enough and :func:`~mpmath.identify` more correctly returns ``None``:: + + >>> identify(pi) + '(2**(176/117)*3**(20/117)*5**(35/39))/(7**(92/117))' + >>> mp.dps = 30 + >>> identify(pi) + >>> + + Numbers such as `\pi`, and simple combinations of user-defined + constants, can be identified if they are provided explicitly:: + + >>> identify(3*pi-2*e, ['pi', 'e']) + '(3*pi + (-2)*e)' + + Here is an example using a dict of constants. Note that the + constants need not be "atomic"; :func:`~mpmath.identify` can just + as well express the given number in terms of expressions + given by formulas:: + + >>> identify(pi+e, {'a':pi+2, 'b':2*e}) + '((-2) + 1*a + (1/2)*b)' + + Next, we attempt some identifications with a set of base constants. + It is necessary to increase the precision a bit. + + >>> mp.dps = 50 + >>> base = ['sqrt(2)','pi','log(2)'] + >>> identify(0.25, base) + '(1/4)' + >>> identify(3*pi + 2*sqrt(2) + 5*log(2)/7, base) + '(2*sqrt(2) + 3*pi + (5/7)*log(2))' + >>> identify(exp(pi+2), base) + 'exp((2 + 1*pi))' + >>> identify(1/(3+sqrt(2)), base) + '((3/7) + (-1/7)*sqrt(2))' + >>> identify(sqrt(2)/(3*pi+4), base) + 'sqrt(2)/(4 + 3*pi)' + >>> identify(5**(mpf(1)/3)*pi*log(2)**2, base) + '5**(1/3)*pi*log(2)**2' + + An example of an erroneous solution being found when too low + precision is used:: + + >>> mp.dps = 15 + >>> identify(1/(3*pi-4*e+sqrt(8)), ['pi', 'e', 'sqrt(2)']) + '((11/25) + (-158/75)*pi + (76/75)*e + (44/15)*sqrt(2))' + >>> mp.dps = 50 + >>> identify(1/(3*pi-4*e+sqrt(8)), ['pi', 'e', 'sqrt(2)']) + '1/(3*pi + (-4)*e + 2*sqrt(2))' + + **Finding approximate solutions** + + The tolerance ``tol`` defaults to 3/4 of the working precision. + Lowering the tolerance is useful for finding approximate matches. + We can for example try to generate approximations for pi:: + + >>> mp.dps = 15 + >>> identify(pi, tol=1e-2) + '(22/7)' + >>> identify(pi, tol=1e-3) + '(355/113)' + >>> identify(pi, tol=1e-10) + '(5**(339/269))/(2**(64/269)*3**(13/269)*7**(92/269))' + + With ``full=True``, and by supplying a few base constants, + ``identify`` can generate almost endless lists of approximations + for any number (the output below has been truncated to show only + the first few):: + + >>> for p in identify(pi, ['e', 'catalan'], tol=1e-5, full=True): + ... print(p) + ... # doctest: +ELLIPSIS + e/log((6 + (-4/3)*e)) + (3**3*5*e*catalan**2)/(2*7**2) + sqrt(((-13) + 1*e + 22*catalan)) + log(((-6) + 24*e + 4*catalan)/e) + exp(catalan*((-1/5) + (8/15)*e)) + catalan*(6 + (-6)*e + 15*catalan) + sqrt((5 + 26*e + (-3)*catalan))/e + e*sqrt(((-27) + 2*e + 25*catalan)) + log(((-1) + (-11)*e + 59*catalan)) + ((3/20) + (21/20)*e + (3/20)*catalan) + ... + + The numerical values are roughly as close to `\pi` as permitted by the + specified tolerance: + + >>> e/log(6-4*e/3) + 3.14157719846001 + >>> 135*e*catalan**2/98 + 3.14166950419369 + >>> sqrt(e-13+22*catalan) + 3.14158000062992 + >>> log(24*e-6+4*catalan)-1 + 3.14158791577159 + + **Symbolic processing** + + The output formula can be evaluated as a Python expression. + Note however that if fractions (like '2/3') are present in + the formula, Python's :func:`~mpmath.eval()` may erroneously perform + integer division. Note also that the output is not necessarily + in the algebraically simplest form:: + + >>> identify(sqrt(2)) + '(sqrt(8)/2)' + + As a solution to both problems, consider using SymPy's + :func:`~mpmath.sympify` to convert the formula into a symbolic expression. + SymPy can be used to pretty-print or further simplify the formula + symbolically:: + + >>> from sympy import sympify # doctest: +SKIP + >>> sympify(identify(sqrt(2))) # doctest: +SKIP + 2**(1/2) + + Sometimes :func:`~mpmath.identify` can simplify an expression further than + a symbolic algorithm:: + + >>> from sympy import simplify # doctest: +SKIP + >>> x = sympify('-1/(-3/2+(1/2)*5**(1/2))*(3/2-1/2*5**(1/2))**(1/2)') # doctest: +SKIP + >>> x # doctest: +SKIP + (3/2 - 5**(1/2)/2)**(-1/2) + >>> x = simplify(x) # doctest: +SKIP + >>> x # doctest: +SKIP + 2/(6 - 2*5**(1/2))**(1/2) + >>> mp.dps = 30 # doctest: +SKIP + >>> x = sympify(identify(x.evalf(30))) # doctest: +SKIP + >>> x # doctest: +SKIP + 1/2 + 5**(1/2)/2 + + (In fact, this functionality is available directly in SymPy as the + function :func:`~mpmath.nsimplify`, which is essentially a wrapper for + :func:`~mpmath.identify`.) + + **Miscellaneous issues and limitations** + + The input `x` must be a real number. All base constants must be + positive real numbers and must not be rationals or rational linear + combinations of each other. + + The worst-case computation time grows quickly with the number of + base constants. Already with 3 or 4 base constants, + :func:`~mpmath.identify` may require several seconds to finish. To search + for relations among a large number of constants, you should + consider using :func:`~mpmath.pslq` directly. + + The extended transformations are applied to x, not the constants + separately. As a result, ``identify`` will for example be able to + recognize ``exp(2*pi+3)`` with ``pi`` given as a base constant, but + not ``2*exp(pi)+3``. It will be able to recognize the latter if + ``exp(pi)`` is given explicitly as a base constant. + + """ + + solutions = [] + + def addsolution(s): + if verbose: print("Found: ", s) + solutions.append(s) + + x = ctx.mpf(x) + + # Further along, x will be assumed positive + if x == 0: + if full: return ['0'] + else: return '0' + if x < 0: + sol = ctx.identify(-x, constants, tol, maxcoeff, full, verbose) + if sol is None: + return sol + if full: + return ["-(%s)"%s for s in sol] + else: + return "-(%s)" % sol + + if tol: + tol = ctx.mpf(tol) + else: + tol = ctx.eps**0.7 + M = maxcoeff + + if constants: + if isinstance(constants, dict): + constants = [(ctx.mpf(v), name) for (name, v) in sorted(constants.items())] + else: + namespace = dict((name, getattr(ctx,name)) for name in dir(ctx)) + constants = [(eval(p, namespace), p) for p in constants] + else: + constants = [] + + # We always want to find at least rational terms + if 1 not in [value for (name, value) in constants]: + constants = [(ctx.mpf(1), '1')] + constants + + # PSLQ with simple algebraic and functional transformations + for ft, ftn, red in transforms: + for c, cn in constants: + if red and cn == '1': + continue + t = ft(ctx,x,c) + # Prevent exponential transforms from wreaking havoc + if abs(t) > M**2 or abs(t) < tol: + continue + # Linear combination of base constants + r = ctx.pslq([t] + [a[0] for a in constants], tol, M) + s = None + if r is not None and max(abs(uw) for uw in r) <= M and r[0]: + s = pslqstring(r, constants) + # Quadratic algebraic numbers + else: + q = ctx.pslq([ctx.one, t, t**2], tol, M) + if q is not None and len(q) == 3 and q[2]: + aa, bb, cc = q + if max(abs(aa),abs(bb),abs(cc)) <= M: + s = quadraticstring(ctx,t,aa,bb,cc) + if s: + if cn == '1' and ('/$c' in ftn): + s = ftn.replace('$y', s).replace('/$c', '') + else: + s = ftn.replace('$y', s).replace('$c', cn) + addsolution(s) + if not full: return solutions[0] + + if verbose: + print(".") + + # Check for a direct multiplicative formula + if x != 1: + # Allow fractional powers of fractions + ilogs = [2,3,5,7] + # Watch out for existing fractional powers of fractions + logs = [] + for a, s in constants: + if not sum(bool(ctx.findpoly(ctx.ln(a)/ctx.ln(i),1)) for i in ilogs): + logs.append((ctx.ln(a), s)) + logs = [(ctx.ln(i),str(i)) for i in ilogs] + logs + r = ctx.pslq([ctx.ln(x)] + [a[0] for a in logs], tol, M) + if r is not None and max(abs(uw) for uw in r) <= M and r[0]: + addsolution(prodstring(r, logs)) + if not full: return solutions[0] + + if full: + return sorted(solutions, key=len) + else: + return None + +IdentificationMethods.pslq = pslq +IdentificationMethods.findpoly = findpoly +IdentificationMethods.identify = identify + + +if __name__ == '__main__': + import doctest + doctest.testmod() diff --git a/lib/python3.12/site-packages/mpmath/libmp/__init__.py b/lib/python3.12/site-packages/mpmath/libmp/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..1573114afc4fbce73f2ba9d2ddc99882c00027c0 --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/libmp/__init__.py @@ -0,0 +1,77 @@ +from .libmpf import (prec_to_dps, dps_to_prec, repr_dps, + round_down, round_up, round_floor, round_ceiling, round_nearest, + to_pickable, from_pickable, ComplexResult, + fzero, fnzero, fone, fnone, ftwo, ften, fhalf, fnan, finf, fninf, + math_float_inf, round_int, normalize, normalize1, + from_man_exp, from_int, to_man_exp, to_int, mpf_ceil, mpf_floor, + mpf_nint, mpf_frac, + from_float, from_npfloat, from_Decimal, to_float, from_rational, to_rational, to_fixed, + mpf_rand, mpf_eq, mpf_hash, mpf_cmp, mpf_lt, mpf_le, mpf_gt, mpf_ge, + mpf_pos, mpf_neg, mpf_abs, mpf_sign, mpf_add, mpf_sub, mpf_sum, + mpf_mul, mpf_mul_int, mpf_shift, mpf_frexp, + mpf_div, mpf_rdiv_int, mpf_mod, mpf_pow_int, + mpf_perturb, + to_digits_exp, to_str, str_to_man_exp, from_str, from_bstr, to_bstr, + mpf_sqrt, mpf_hypot) + +from .libmpc import (mpc_one, mpc_zero, mpc_two, mpc_half, + mpc_is_inf, mpc_is_infnan, mpc_to_str, mpc_to_complex, mpc_hash, + mpc_conjugate, mpc_is_nonzero, mpc_add, mpc_add_mpf, + mpc_sub, mpc_sub_mpf, mpc_pos, mpc_neg, mpc_shift, mpc_abs, + mpc_arg, mpc_floor, mpc_ceil, mpc_nint, mpc_frac, mpc_mul, mpc_square, + mpc_mul_mpf, mpc_mul_imag_mpf, mpc_mul_int, + mpc_div, mpc_div_mpf, mpc_reciprocal, mpc_mpf_div, + complex_int_pow, mpc_pow, mpc_pow_mpf, mpc_pow_int, + mpc_sqrt, mpc_nthroot, mpc_cbrt, mpc_exp, mpc_log, mpc_cos, mpc_sin, + mpc_tan, mpc_cos_pi, mpc_sin_pi, mpc_cosh, mpc_sinh, mpc_tanh, + mpc_atan, mpc_acos, mpc_asin, mpc_asinh, mpc_acosh, mpc_atanh, + mpc_fibonacci, mpf_expj, mpf_expjpi, mpc_expj, mpc_expjpi, + mpc_cos_sin, mpc_cos_sin_pi) + +from .libelefun import (ln2_fixed, mpf_ln2, ln10_fixed, mpf_ln10, + pi_fixed, mpf_pi, e_fixed, mpf_e, phi_fixed, mpf_phi, + degree_fixed, mpf_degree, + mpf_pow, mpf_nthroot, mpf_cbrt, log_int_fixed, agm_fixed, + mpf_log, mpf_log_hypot, mpf_exp, mpf_cos_sin, mpf_cos, mpf_sin, mpf_tan, + mpf_cos_sin_pi, mpf_cos_pi, mpf_sin_pi, mpf_cosh_sinh, + mpf_cosh, mpf_sinh, mpf_tanh, mpf_atan, mpf_atan2, mpf_asin, + mpf_acos, mpf_asinh, mpf_acosh, mpf_atanh, mpf_fibonacci) + +from .libhyper import (NoConvergence, make_hyp_summator, + mpf_erf, mpf_erfc, mpf_ei, mpc_ei, mpf_e1, mpc_e1, mpf_expint, + mpf_ci_si, mpf_ci, mpf_si, mpc_ci, mpc_si, mpf_besseljn, + mpc_besseljn, mpf_agm, mpf_agm1, mpc_agm, mpc_agm1, + mpf_ellipk, mpc_ellipk, mpf_ellipe, mpc_ellipe) + +from .gammazeta import (catalan_fixed, mpf_catalan, + khinchin_fixed, mpf_khinchin, glaisher_fixed, mpf_glaisher, + apery_fixed, mpf_apery, euler_fixed, mpf_euler, mertens_fixed, + mpf_mertens, twinprime_fixed, mpf_twinprime, + mpf_bernoulli, bernfrac, mpf_gamma_int, + mpf_factorial, mpc_factorial, mpf_gamma, mpc_gamma, + mpf_loggamma, mpc_loggamma, mpf_rgamma, mpc_rgamma, + mpf_harmonic, mpc_harmonic, mpf_psi0, mpc_psi0, + mpf_psi, mpc_psi, mpf_zeta_int, mpf_zeta, mpc_zeta, + mpf_altzeta, mpc_altzeta, mpf_zetasum, mpc_zetasum) + +from .libmpi import (mpi_str, + mpi_from_str, mpi_to_str, + mpi_eq, mpi_ne, + mpi_lt, mpi_le, mpi_gt, mpi_ge, + mpi_add, mpi_sub, mpi_delta, mpi_mid, + mpi_pos, mpi_neg, mpi_abs, mpi_mul, mpi_div, mpi_exp, + mpi_log, mpi_sqrt, mpi_pow_int, mpi_pow, mpi_cos_sin, + mpi_cos, mpi_sin, mpi_tan, mpi_cot, + mpi_atan, mpi_atan2, + mpci_pos, mpci_neg, mpci_add, mpci_sub, mpci_mul, mpci_div, mpci_pow, + mpci_abs, mpci_pow, mpci_exp, mpci_log, mpci_cos, mpci_sin, + mpi_gamma, mpci_gamma, mpi_loggamma, mpci_loggamma, + mpi_rgamma, mpci_rgamma, mpi_factorial, mpci_factorial) + +from .libintmath import (trailing, bitcount, numeral, bin_to_radix, + isqrt, isqrt_small, isqrt_fast, sqrt_fixed, sqrtrem, ifib, ifac, + list_primes, isprime, moebius, gcd, eulernum, stirling1, stirling2) + +from .backend import (gmpy, sage, BACKEND, STRICT, MPZ, MPZ_TYPE, + MPZ_ZERO, MPZ_ONE, MPZ_TWO, MPZ_THREE, MPZ_FIVE, int_types, + HASH_MODULUS, HASH_BITS) diff --git a/lib/python3.12/site-packages/mpmath/libmp/__pycache__/__init__.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/libmp/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4a87a67d7df6929b4e673330a3be2b7e3f903a2b Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/libmp/__pycache__/__init__.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/libmp/__pycache__/backend.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/libmp/__pycache__/backend.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..345c15efcdd6f554ea41367bd132275fe6e916a4 Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/libmp/__pycache__/backend.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/libmp/__pycache__/gammazeta.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/libmp/__pycache__/gammazeta.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..669d074fcfe637ab55b62630c2644013e9e3925f Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/libmp/__pycache__/gammazeta.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/libmp/__pycache__/libelefun.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/libmp/__pycache__/libelefun.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7fb1ec1fc077eef769493520e9361bdb35dc37b0 Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/libmp/__pycache__/libelefun.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/libmp/__pycache__/libhyper.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/libmp/__pycache__/libhyper.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1b3d497ea9d304ebd2032c1f4ecfc914b1a6a04b Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/libmp/__pycache__/libhyper.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/libmp/__pycache__/libintmath.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/libmp/__pycache__/libintmath.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8ab2316d11c6a2377df7deecb6c8bfcbd991d3de Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/libmp/__pycache__/libintmath.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/libmp/__pycache__/libmpc.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/libmp/__pycache__/libmpc.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6f582a9a32efa0aac8fd07a38ffe4a4eab129511 Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/libmp/__pycache__/libmpc.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/libmp/__pycache__/libmpf.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/libmp/__pycache__/libmpf.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..18aa4f982bf730f0e61130f66a00807fbc7f1549 Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/libmp/__pycache__/libmpf.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/libmp/__pycache__/libmpi.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/libmp/__pycache__/libmpi.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..edb1c8ea62a0416367a64746ca12734cb343fec5 Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/libmp/__pycache__/libmpi.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/libmp/backend.py b/lib/python3.12/site-packages/mpmath/libmp/backend.py new file mode 100644 index 0000000000000000000000000000000000000000..5610221290a05078f21f09df3c1a76b0e4ccdc02 --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/libmp/backend.py @@ -0,0 +1,115 @@ +import os +import sys + +#----------------------------------------------------------------------------# +# Support GMPY for high-speed large integer arithmetic. # +# # +# To allow an external module to handle arithmetic, we need to make sure # +# that all high-precision variables are declared of the correct type. MPZ # +# is the constructor for the high-precision type. It defaults to Python's # +# long type but can be assinged another type, typically gmpy.mpz. # +# # +# MPZ must be used for the mantissa component of an mpf and must be used # +# for internal fixed-point operations. # +# # +# Side-effects # +# 1) "is" cannot be used to test for special values. Must use "==". # +# 2) There are bugs in GMPY prior to v1.02 so we must use v1.03 or later. # +#----------------------------------------------------------------------------# + +# So we can import it from this module +gmpy = None +sage = None +sage_utils = None + +if sys.version_info[0] < 3: + python3 = False +else: + python3 = True + +BACKEND = 'python' + +if not python3: + MPZ = long + xrange = xrange + basestring = basestring + + def exec_(_code_, _globs_=None, _locs_=None): + """Execute code in a namespace.""" + if _globs_ is None: + frame = sys._getframe(1) + _globs_ = frame.f_globals + if _locs_ is None: + _locs_ = frame.f_locals + del frame + elif _locs_ is None: + _locs_ = _globs_ + exec("""exec _code_ in _globs_, _locs_""") +else: + MPZ = int + xrange = range + basestring = str + + import builtins + exec_ = getattr(builtins, "exec") + +# Define constants for calculating hash on Python 3.2. +if sys.version_info >= (3, 2): + HASH_MODULUS = sys.hash_info.modulus + if sys.hash_info.width == 32: + HASH_BITS = 31 + else: + HASH_BITS = 61 +else: + HASH_MODULUS = None + HASH_BITS = None + +if 'MPMATH_NOGMPY' not in os.environ: + try: + try: + import gmpy2 as gmpy + except ImportError: + try: + import gmpy + except ImportError: + raise ImportError + if gmpy.version() >= '1.03': + BACKEND = 'gmpy' + MPZ = gmpy.mpz + except: + pass + +if ('MPMATH_NOSAGE' not in os.environ and 'SAGE_ROOT' in os.environ or + 'MPMATH_SAGE' in os.environ): + try: + import sage.all + import sage.libs.mpmath.utils as _sage_utils + sage = sage.all + sage_utils = _sage_utils + BACKEND = 'sage' + MPZ = sage.Integer + except: + pass + +if 'MPMATH_STRICT' in os.environ: + STRICT = True +else: + STRICT = False + +MPZ_TYPE = type(MPZ(0)) +MPZ_ZERO = MPZ(0) +MPZ_ONE = MPZ(1) +MPZ_TWO = MPZ(2) +MPZ_THREE = MPZ(3) +MPZ_FIVE = MPZ(5) + +try: + if BACKEND == 'python': + int_types = (int, long) + else: + int_types = (int, long, MPZ_TYPE) +except NameError: + if BACKEND == 'python': + int_types = (int,) + else: + int_types = (int, MPZ_TYPE) diff --git a/lib/python3.12/site-packages/mpmath/libmp/gammazeta.py b/lib/python3.12/site-packages/mpmath/libmp/gammazeta.py new file mode 100644 index 0000000000000000000000000000000000000000..3b05cc63c5f00e6c76d8383853dba06f15e46030 --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/libmp/gammazeta.py @@ -0,0 +1,2167 @@ +""" +----------------------------------------------------------------------- +This module implements gamma- and zeta-related functions: + +* Bernoulli numbers +* Factorials +* The gamma function +* Polygamma functions +* Harmonic numbers +* The Riemann zeta function +* Constants related to these functions + +----------------------------------------------------------------------- +""" + +import math +import sys + +from .backend import xrange +from .backend import MPZ, MPZ_ZERO, MPZ_ONE, MPZ_THREE, gmpy + +from .libintmath import list_primes, ifac, ifac2, moebius + +from .libmpf import (\ + round_floor, round_ceiling, round_down, round_up, + round_nearest, round_fast, + lshift, sqrt_fixed, isqrt_fast, + fzero, fone, fnone, fhalf, ftwo, finf, fninf, fnan, + from_int, to_int, to_fixed, from_man_exp, from_rational, + mpf_pos, mpf_neg, mpf_abs, mpf_add, mpf_sub, + mpf_mul, mpf_mul_int, mpf_div, mpf_sqrt, mpf_pow_int, + mpf_rdiv_int, + mpf_perturb, mpf_le, mpf_lt, mpf_gt, mpf_shift, + negative_rnd, reciprocal_rnd, + bitcount, to_float, mpf_floor, mpf_sign, ComplexResult +) + +from .libelefun import (\ + constant_memo, + def_mpf_constant, + mpf_pi, pi_fixed, ln2_fixed, log_int_fixed, mpf_ln2, + mpf_exp, mpf_log, mpf_pow, mpf_cosh, + mpf_cos_sin, mpf_cosh_sinh, mpf_cos_sin_pi, mpf_cos_pi, mpf_sin_pi, + ln_sqrt2pi_fixed, mpf_ln_sqrt2pi, sqrtpi_fixed, mpf_sqrtpi, + cos_sin_fixed, exp_fixed +) + +from .libmpc import (\ + mpc_zero, mpc_one, mpc_half, mpc_two, + mpc_abs, mpc_shift, mpc_pos, mpc_neg, + mpc_add, mpc_sub, mpc_mul, mpc_div, + mpc_add_mpf, mpc_mul_mpf, mpc_div_mpf, mpc_mpf_div, + mpc_mul_int, mpc_pow_int, + mpc_log, mpc_exp, mpc_pow, + mpc_cos_pi, mpc_sin_pi, + mpc_reciprocal, mpc_square, + mpc_sub_mpf +) + + + +# Catalan's constant is computed using Lupas's rapidly convergent series +# (listed on http://mathworld.wolfram.com/CatalansConstant.html) +# oo +# ___ n-1 8n 2 3 2 +# 1 \ (-1) 2 (40n - 24n + 3) [(2n)!] (n!) +# K = --- ) ----------------------------------------- +# 64 /___ 3 2 +# n (2n-1) [(4n)!] +# n = 1 + +@constant_memo +def catalan_fixed(prec): + prec = prec + 20 + a = one = MPZ_ONE << prec + s, t, n = 0, 1, 1 + while t: + a *= 32 * n**3 * (2*n-1) + a //= (3-16*n+16*n**2)**2 + t = a * (-1)**(n-1) * (40*n**2-24*n+3) // (n**3 * (2*n-1)) + s += t + n += 1 + return s >> (20 + 6) + +# Khinchin's constant is relatively difficult to compute. Here +# we use the rational zeta series + +# oo 2*n-1 +# ___ ___ +# \ ` zeta(2*n)-1 \ ` (-1)^(k+1) +# log(K)*log(2) = ) ------------ ) ---------- +# /___. n /___. k +# n = 1 k = 1 + +# which adds half a digit per term. The essential trick for achieving +# reasonable efficiency is to recycle both the values of the zeta +# function (essentially Bernoulli numbers) and the partial terms of +# the inner sum. + +# An alternative might be to use K = 2*exp[1/log(2) X] where + +# / 1 1 [ pi*x*(1-x^2) ] +# X = | ------ log [ ------------ ]. +# / 0 x(1+x) [ sin(pi*x) ] + +# and integrate numerically. In practice, this seems to be slightly +# slower than the zeta series at high precision. + +@constant_memo +def khinchin_fixed(prec): + wp = int(prec + prec**0.5 + 15) + s = MPZ_ZERO + fac = from_int(4) + t = ONE = MPZ_ONE << wp + pi = mpf_pi(wp) + pipow = twopi2 = mpf_shift(mpf_mul(pi, pi, wp), 2) + n = 1 + while 1: + zeta2n = mpf_abs(mpf_bernoulli(2*n, wp)) + zeta2n = mpf_mul(zeta2n, pipow, wp) + zeta2n = mpf_div(zeta2n, fac, wp) + zeta2n = to_fixed(zeta2n, wp) + term = (((zeta2n - ONE) * t) // n) >> wp + if term < 100: + break + #if not n % 10: + # print n, math.log(int(abs(term))) + s += term + t += ONE//(2*n+1) - ONE//(2*n) + n += 1 + fac = mpf_mul_int(fac, (2*n)*(2*n-1), wp) + pipow = mpf_mul(pipow, twopi2, wp) + s = (s << wp) // ln2_fixed(wp) + K = mpf_exp(from_man_exp(s, -wp), wp) + K = to_fixed(K, prec) + return K + + +# Glaisher's constant is defined as A = exp(1/2 - zeta'(-1)). +# One way to compute it would be to perform direct numerical +# differentiation, but computing arbitrary Riemann zeta function +# values at high precision is expensive. We instead use the formula + +# A = exp((6 (-zeta'(2))/pi^2 + log 2 pi + gamma)/12) + +# and compute zeta'(2) from the series representation + +# oo +# ___ +# \ log k +# -zeta'(2) = ) ----- +# /___ 2 +# k +# k = 2 + +# This series converges exceptionally slowly, but can be accelerated +# using Euler-Maclaurin formula. The important insight is that the +# E-M integral can be done in closed form and that the high order +# are given by + +# n / \ +# d | log x | a + b log x +# --- | ----- | = ----------- +# n | 2 | 2 + n +# dx \ x / x + +# where a and b are integers given by a simple recurrence. Note +# that just one logarithm is needed. However, lots of integer +# logarithms are required for the initial summation. + +# This algorithm could possibly be turned into a faster algorithm +# for general evaluation of zeta(s) or zeta'(s); this should be +# looked into. + +@constant_memo +def glaisher_fixed(prec): + wp = prec + 30 + # Number of direct terms to sum before applying the Euler-Maclaurin + # formula to the tail. TODO: choose more intelligently + N = int(0.33*prec + 5) + ONE = MPZ_ONE << wp + # Euler-Maclaurin, step 1: sum log(k)/k**2 for k from 2 to N-1 + s = MPZ_ZERO + for k in range(2, N): + #print k, N + s += log_int_fixed(k, wp) // k**2 + logN = log_int_fixed(N, wp) + #logN = to_fixed(mpf_log(from_int(N), wp+20), wp) + # E-M step 2: integral of log(x)/x**2 from N to inf + s += (ONE + logN) // N + # E-M step 3: endpoint correction term f(N)/2 + s += logN // (N**2 * 2) + # E-M step 4: the series of derivatives + pN = N**3 + a = 1 + b = -2 + j = 3 + fac = from_int(2) + k = 1 + while 1: + # D(2*k-1) * B(2*k) / fac(2*k) [D(n) = nth derivative] + D = ((a << wp) + b*logN) // pN + D = from_man_exp(D, -wp) + B = mpf_bernoulli(2*k, wp) + term = mpf_mul(B, D, wp) + term = mpf_div(term, fac, wp) + term = to_fixed(term, wp) + if abs(term) < 100: + break + #if not k % 10: + # print k, math.log(int(abs(term)), 10) + s -= term + # Advance derivative twice + a, b, pN, j = b-a*j, -j*b, pN*N, j+1 + a, b, pN, j = b-a*j, -j*b, pN*N, j+1 + k += 1 + fac = mpf_mul_int(fac, (2*k)*(2*k-1), wp) + # A = exp((6*s/pi**2 + log(2*pi) + euler)/12) + pi = pi_fixed(wp) + s *= 6 + s = (s << wp) // (pi**2 >> wp) + s += euler_fixed(wp) + s += to_fixed(mpf_log(from_man_exp(2*pi, -wp), wp), wp) + s //= 12 + A = mpf_exp(from_man_exp(s, -wp), wp) + return to_fixed(A, prec) + +# Apery's constant can be computed using the very rapidly convergent +# series +# oo +# ___ 2 10 +# \ n 205 n + 250 n + 77 (n!) +# zeta(3) = ) (-1) ------------------- ---------- +# /___ 64 5 +# n = 0 ((2n+1)!) + +@constant_memo +def apery_fixed(prec): + prec += 20 + d = MPZ_ONE << prec + term = MPZ(77) << prec + n = 1 + s = MPZ_ZERO + while term: + s += term + d *= (n**10) + d //= (((2*n+1)**5) * (2*n)**5) + term = (-1)**n * (205*(n**2) + 250*n + 77) * d + n += 1 + return s >> (20 + 6) + +""" +Euler's constant (gamma) is computed using the Brent-McMillan formula, +gamma ~= I(n)/J(n) - log(n), where + + I(n) = sum_{k=0,1,2,...} (n**k / k!)**2 * H(k) + J(n) = sum_{k=0,1,2,...} (n**k / k!)**2 + H(k) = 1 + 1/2 + 1/3 + ... + 1/k + +The error is bounded by O(exp(-4n)). Choosing n to be a power +of two, 2**p, the logarithm becomes particularly easy to calculate.[1] + +We use the formulation of Algorithm 3.9 in [2] to make the summation +more efficient. + +Reference: +[1] Xavier Gourdon & Pascal Sebah, The Euler constant: gamma +http://numbers.computation.free.fr/Constants/Gamma/gamma.pdf + +[2] [BorweinBailey]_ +""" + +@constant_memo +def euler_fixed(prec): + extra = 30 + prec += extra + # choose p such that exp(-4*(2**p)) < 2**-n + p = int(math.log((prec/4) * math.log(2), 2)) + 1 + n = 2**p + A = U = -p*ln2_fixed(prec) + B = V = MPZ_ONE << prec + k = 1 + while 1: + B = B*n**2//k**2 + A = (A*n**2//k + B)//k + U += A + V += B + if max(abs(A), abs(B)) < 100: + break + k += 1 + return (U<<(prec-extra))//V + +# Use zeta accelerated formulas for the Mertens and twin +# prime constants; see +# http://mathworld.wolfram.com/MertensConstant.html +# http://mathworld.wolfram.com/TwinPrimesConstant.html + +@constant_memo +def mertens_fixed(prec): + wp = prec + 20 + m = 2 + s = mpf_euler(wp) + while 1: + t = mpf_zeta_int(m, wp) + if t == fone: + break + t = mpf_log(t, wp) + t = mpf_mul_int(t, moebius(m), wp) + t = mpf_div(t, from_int(m), wp) + s = mpf_add(s, t) + m += 1 + return to_fixed(s, prec) + +@constant_memo +def twinprime_fixed(prec): + def I(n): + return sum(moebius(d)<<(n//d) for d in xrange(1,n+1) if not n%d)//n + wp = 2*prec + 30 + res = fone + primes = [from_rational(1,p,wp) for p in [2,3,5,7]] + ppowers = [mpf_mul(p,p,wp) for p in primes] + n = 2 + while 1: + a = mpf_zeta_int(n, wp) + for i in range(4): + a = mpf_mul(a, mpf_sub(fone, ppowers[i]), wp) + ppowers[i] = mpf_mul(ppowers[i], primes[i], wp) + a = mpf_pow_int(a, -I(n), wp) + if mpf_pos(a, prec+10, 'n') == fone: + break + #from libmpf import to_str + #print n, to_str(mpf_sub(fone, a), 6) + res = mpf_mul(res, a, wp) + n += 1 + res = mpf_mul(res, from_int(3*15*35), wp) + res = mpf_div(res, from_int(4*16*36), wp) + return to_fixed(res, prec) + + +mpf_euler = def_mpf_constant(euler_fixed) +mpf_apery = def_mpf_constant(apery_fixed) +mpf_khinchin = def_mpf_constant(khinchin_fixed) +mpf_glaisher = def_mpf_constant(glaisher_fixed) +mpf_catalan = def_mpf_constant(catalan_fixed) +mpf_mertens = def_mpf_constant(mertens_fixed) +mpf_twinprime = def_mpf_constant(twinprime_fixed) + + +#-----------------------------------------------------------------------# +# # +# Bernoulli numbers # +# # +#-----------------------------------------------------------------------# + +MAX_BERNOULLI_CACHE = 3000 + + +r""" +Small Bernoulli numbers and factorials are used in numerous summations, +so it is critical for speed that sequential computation is fast and that +values are cached up to a fairly high threshold. + +On the other hand, we also want to support fast computation of isolated +large numbers. Currently, no such acceleration is provided for integer +factorials (though it is for large floating-point factorials, which are +computed via gamma if the precision is low enough). + +For sequential computation of Bernoulli numbers, we use Ramanujan's formula + + / n + 3 \ + B = (A(n) - S(n)) / | | + n \ n / + +where A(n) = (n+3)/3 when n = 0 or 2 (mod 6), A(n) = -(n+3)/6 +when n = 4 (mod 6), and + + [n/6] + ___ + \ / n + 3 \ + S(n) = ) | | * B + /___ \ n - 6*k / n-6*k + k = 1 + +For isolated large Bernoulli numbers, we use the Riemann zeta function +to calculate a numerical value for B_n. The von Staudt-Clausen theorem +can then be used to optionally find the exact value of the +numerator and denominator. +""" + +bernoulli_cache = {} +f3 = from_int(3) +f6 = from_int(6) + +def bernoulli_size(n): + """Accurately estimate the size of B_n (even n > 2 only)""" + lgn = math.log(n,2) + return int(2.326 + 0.5*lgn + n*(lgn - 4.094)) + +BERNOULLI_PREC_CUTOFF = bernoulli_size(MAX_BERNOULLI_CACHE) + +def mpf_bernoulli(n, prec, rnd=None): + """Computation of Bernoulli numbers (numerically)""" + if n < 2: + if n < 0: + raise ValueError("Bernoulli numbers only defined for n >= 0") + if n == 0: + return fone + if n == 1: + return mpf_neg(fhalf) + # For odd n > 1, the Bernoulli numbers are zero + if n & 1: + return fzero + # If precision is extremely high, we can save time by computing + # the Bernoulli number at a lower precision that is sufficient to + # obtain the exact fraction, round to the exact fraction, and + # convert the fraction back to an mpf value at the original precision + if prec > BERNOULLI_PREC_CUTOFF and prec > bernoulli_size(n)*1.1 + 1000: + p, q = bernfrac(n) + return from_rational(p, q, prec, rnd or round_floor) + if n > MAX_BERNOULLI_CACHE: + return mpf_bernoulli_huge(n, prec, rnd) + wp = prec + 30 + # Reuse nearby precisions + wp += 32 - (prec & 31) + cached = bernoulli_cache.get(wp) + if cached: + numbers, state = cached + if n in numbers: + if not rnd: + return numbers[n] + return mpf_pos(numbers[n], prec, rnd) + m, bin, bin1 = state + if n - m > 10: + return mpf_bernoulli_huge(n, prec, rnd) + else: + if n > 10: + return mpf_bernoulli_huge(n, prec, rnd) + numbers = {0:fone} + m, bin, bin1 = state = [2, MPZ(10), MPZ_ONE] + bernoulli_cache[wp] = (numbers, state) + while m <= n: + #print m + case = m % 6 + # Accurately estimate size of B_m so we can use + # fixed point math without using too much precision + szbm = bernoulli_size(m) + s = 0 + sexp = max(0, szbm) - wp + if m < 6: + a = MPZ_ZERO + else: + a = bin1 + for j in xrange(1, m//6+1): + usign, uman, uexp, ubc = u = numbers[m-6*j] + if usign: + uman = -uman + s += lshift(a*uman, uexp-sexp) + # Update inner binomial coefficient + j6 = 6*j + a *= ((m-5-j6)*(m-4-j6)*(m-3-j6)*(m-2-j6)*(m-1-j6)*(m-j6)) + a //= ((4+j6)*(5+j6)*(6+j6)*(7+j6)*(8+j6)*(9+j6)) + if case == 0: b = mpf_rdiv_int(m+3, f3, wp) + if case == 2: b = mpf_rdiv_int(m+3, f3, wp) + if case == 4: b = mpf_rdiv_int(-m-3, f6, wp) + s = from_man_exp(s, sexp, wp) + b = mpf_div(mpf_sub(b, s, wp), from_int(bin), wp) + numbers[m] = b + m += 2 + # Update outer binomial coefficient + bin = bin * ((m+2)*(m+3)) // (m*(m-1)) + if m > 6: + bin1 = bin1 * ((2+m)*(3+m)) // ((m-7)*(m-6)) + state[:] = [m, bin, bin1] + return numbers[n] + +def mpf_bernoulli_huge(n, prec, rnd=None): + wp = prec + 10 + piprec = wp + int(math.log(n,2)) + v = mpf_gamma_int(n+1, wp) + v = mpf_mul(v, mpf_zeta_int(n, wp), wp) + v = mpf_mul(v, mpf_pow_int(mpf_pi(piprec), -n, wp)) + v = mpf_shift(v, 1-n) + if not n & 3: + v = mpf_neg(v) + return mpf_pos(v, prec, rnd or round_fast) + +def bernfrac(n): + r""" + Returns a tuple of integers `(p, q)` such that `p/q = B_n` exactly, + where `B_n` denotes the `n`-th Bernoulli number. The fraction is + always reduced to lowest terms. Note that for `n > 1` and `n` odd, + `B_n = 0`, and `(0, 1)` is returned. + + **Examples** + + The first few Bernoulli numbers are exactly:: + + >>> from mpmath import * + >>> for n in range(15): + ... p, q = bernfrac(n) + ... print("%s %s/%s" % (n, p, q)) + ... + 0 1/1 + 1 -1/2 + 2 1/6 + 3 0/1 + 4 -1/30 + 5 0/1 + 6 1/42 + 7 0/1 + 8 -1/30 + 9 0/1 + 10 5/66 + 11 0/1 + 12 -691/2730 + 13 0/1 + 14 7/6 + + This function works for arbitrarily large `n`:: + + >>> p, q = bernfrac(10**4) + >>> print(q) + 2338224387510 + >>> print(len(str(p))) + 27692 + >>> mp.dps = 15 + >>> print(mpf(p) / q) + -9.04942396360948e+27677 + >>> print(bernoulli(10**4)) + -9.04942396360948e+27677 + + .. note :: + + :func:`~mpmath.bernoulli` computes a floating-point approximation + directly, without computing the exact fraction first. + This is much faster for large `n`. + + **Algorithm** + + :func:`~mpmath.bernfrac` works by computing the value of `B_n` numerically + and then using the von Staudt-Clausen theorem [1] to reconstruct + the exact fraction. For large `n`, this is significantly faster than + computing `B_1, B_2, \ldots, B_2` recursively with exact arithmetic. + The implementation has been tested for `n = 10^m` up to `m = 6`. + + In practice, :func:`~mpmath.bernfrac` appears to be about three times + slower than the specialized program calcbn.exe [2] + + **References** + + 1. MathWorld, von Staudt-Clausen Theorem: + http://mathworld.wolfram.com/vonStaudt-ClausenTheorem.html + + 2. The Bernoulli Number Page: + http://www.bernoulli.org/ + + """ + n = int(n) + if n < 3: + return [(1, 1), (-1, 2), (1, 6)][n] + if n & 1: + return (0, 1) + q = 1 + for k in list_primes(n+1): + if not (n % (k-1)): + q *= k + prec = bernoulli_size(n) + int(math.log(q,2)) + 20 + b = mpf_bernoulli(n, prec) + p = mpf_mul(b, from_int(q)) + pint = to_int(p, round_nearest) + return (pint, q) + + +#-----------------------------------------------------------------------# +# # +# Polygamma functions # +# # +#-----------------------------------------------------------------------# + +r""" +For all polygamma (psi) functions, we use the Euler-Maclaurin summation +formula. It looks slightly different in the m = 0 and m > 0 cases. + +For m = 0, we have + oo + ___ B + (0) 1 \ 2 k -2 k + psi (z) ~ log z + --- - ) ------ z + 2 z /___ (2 k)! + k = 1 + +Experiment shows that the minimum term of the asymptotic series +reaches 2^(-p) when Re(z) > 0.11*p. So we simply use the recurrence +for psi (equivalent, in fact, to summing to the first few terms +directly before applying E-M) to obtain z large enough. + +Since, very crudely, log z ~= 1 for Re(z) > 1, we can use +fixed-point arithmetic (if z is extremely large, log(z) itself +is a sufficient approximation, so we can stop there already). + +For Re(z) << 0, we could use recurrence, but this is of course +inefficient for large negative z, so there we use the +reflection formula instead. + +For m > 0, we have + + N - 1 + ___ + ~~~(m) [ \ 1 ] 1 1 + psi (z) ~ [ ) -------- ] + ---------- + -------- + + [ /___ m+1 ] m+1 m + k = 1 (z+k) ] 2 (z+N) m (z+N) + + oo + ___ B + \ 2 k (m+1) (m+2) ... (m+2k-1) + + ) ------ ------------------------ + /___ (2 k)! m + 2 k + k = 1 (z+N) + +where ~~~ denotes the function rescaled by 1/((-1)^(m+1) m!). + +Here again N is chosen to make z+N large enough for the minimum +term in the last series to become smaller than eps. + +TODO: the current estimation of N for m > 0 is *very suboptimal*. + +TODO: implement the reflection formula for m > 0, Re(z) << 0. +It is generally a combination of multiple cotangents. Need to +figure out a reasonably simple way to generate these formulas +on the fly. + +TODO: maybe use exact algorithms to compute psi for integral +and certain rational arguments, as this can be much more +efficient. (On the other hand, the availability of these +special values provides a convenient way to test the general +algorithm.) +""" + +# Harmonic numbers are just shifted digamma functions +# We should calculate these exactly when x is an integer +# and when doing so is faster. + +def mpf_harmonic(x, prec, rnd): + if x in (fzero, fnan, finf): + return x + a = mpf_psi0(mpf_add(fone, x, prec+5), prec) + return mpf_add(a, mpf_euler(prec+5, rnd), prec, rnd) + +def mpc_harmonic(z, prec, rnd): + if z[1] == fzero: + return (mpf_harmonic(z[0], prec, rnd), fzero) + a = mpc_psi0(mpc_add_mpf(z, fone, prec+5), prec) + return mpc_add_mpf(a, mpf_euler(prec+5, rnd), prec, rnd) + +def mpf_psi0(x, prec, rnd=round_fast): + """ + Computation of the digamma function (psi function of order 0) + of a real argument. + """ + sign, man, exp, bc = x + wp = prec + 10 + if not man: + if x == finf: return x + if x == fninf or x == fnan: return fnan + if x == fzero or (exp >= 0 and sign): + raise ValueError("polygamma pole") + # Near 0 -- fixed-point arithmetic becomes bad + if exp+bc < -5: + v = mpf_psi0(mpf_add(x, fone, prec, rnd), prec, rnd) + return mpf_sub(v, mpf_div(fone, x, wp, rnd), prec, rnd) + # Reflection formula + if sign and exp+bc > 3: + c, s = mpf_cos_sin_pi(x, wp) + q = mpf_mul(mpf_div(c, s, wp), mpf_pi(wp), wp) + p = mpf_psi0(mpf_sub(fone, x, wp), wp) + return mpf_sub(p, q, prec, rnd) + # The logarithmic term is accurate enough + if (not sign) and bc + exp > wp: + return mpf_log(mpf_sub(x, fone, wp), prec, rnd) + # Initial recurrence to obtain a large enough x + m = to_int(x) + n = int(0.11*wp) + 2 + s = MPZ_ZERO + x = to_fixed(x, wp) + one = MPZ_ONE << wp + if m < n: + for k in xrange(m, n): + s -= (one << wp) // x + x += one + x -= one + # Logarithmic term + s += to_fixed(mpf_log(from_man_exp(x, -wp, wp), wp), wp) + # Endpoint term in Euler-Maclaurin expansion + s += (one << wp) // (2*x) + # Euler-Maclaurin remainder sum + x2 = (x*x) >> wp + t = one + prev = 0 + k = 1 + while 1: + t = (t*x2) >> wp + bsign, bman, bexp, bbc = mpf_bernoulli(2*k, wp) + offset = (bexp + 2*wp) + if offset >= 0: term = (bman << offset) // (t*(2*k)) + else: term = (bman >> (-offset)) // (t*(2*k)) + if k & 1: s -= term + else: s += term + if k > 2 and term >= prev: + break + prev = term + k += 1 + return from_man_exp(s, -wp, wp, rnd) + +def mpc_psi0(z, prec, rnd=round_fast): + """ + Computation of the digamma function (psi function of order 0) + of a complex argument. + """ + re, im = z + # Fall back to the real case + if im == fzero: + return (mpf_psi0(re, prec, rnd), fzero) + wp = prec + 20 + sign, man, exp, bc = re + # Reflection formula + if sign and exp+bc > 3: + c = mpc_cos_pi(z, wp) + s = mpc_sin_pi(z, wp) + q = mpc_mul_mpf(mpc_div(c, s, wp), mpf_pi(wp), wp) + p = mpc_psi0(mpc_sub(mpc_one, z, wp), wp) + return mpc_sub(p, q, prec, rnd) + # Just the logarithmic term + if (not sign) and bc + exp > wp: + return mpc_log(mpc_sub(z, mpc_one, wp), prec, rnd) + # Initial recurrence to obtain a large enough z + w = to_int(re) + n = int(0.11*wp) + 2 + s = mpc_zero + if w < n: + for k in xrange(w, n): + s = mpc_sub(s, mpc_reciprocal(z, wp), wp) + z = mpc_add_mpf(z, fone, wp) + z = mpc_sub(z, mpc_one, wp) + # Logarithmic and endpoint term + s = mpc_add(s, mpc_log(z, wp), wp) + s = mpc_add(s, mpc_div(mpc_half, z, wp), wp) + # Euler-Maclaurin remainder sum + z2 = mpc_square(z, wp) + t = mpc_one + prev = mpc_zero + szprev = fzero + k = 1 + eps = mpf_shift(fone, -wp+2) + while 1: + t = mpc_mul(t, z2, wp) + bern = mpf_bernoulli(2*k, wp) + term = mpc_mpf_div(bern, mpc_mul_int(t, 2*k, wp), wp) + s = mpc_sub(s, term, wp) + szterm = mpc_abs(term, 10) + if k > 2 and (mpf_le(szterm, eps) or mpf_le(szprev, szterm)): + break + prev = term + szprev = szterm + k += 1 + return s + +# Currently unoptimized +def mpf_psi(m, x, prec, rnd=round_fast): + """ + Computation of the polygamma function of arbitrary integer order + m >= 0, for a real argument x. + """ + if m == 0: + return mpf_psi0(x, prec, rnd=round_fast) + return mpc_psi(m, (x, fzero), prec, rnd)[0] + +def mpc_psi(m, z, prec, rnd=round_fast): + """ + Computation of the polygamma function of arbitrary integer order + m >= 0, for a complex argument z. + """ + if m == 0: + return mpc_psi0(z, prec, rnd) + re, im = z + wp = prec + 20 + sign, man, exp, bc = re + if not im[1]: + if im in (finf, fninf, fnan): + return (fnan, fnan) + if not man: + if re == finf and im == fzero: + return (fzero, fzero) + if re == fnan: + return (fnan, fnan) + # Recurrence + w = to_int(re) + n = int(0.4*wp + 4*m) + s = mpc_zero + if w < n: + for k in xrange(w, n): + t = mpc_pow_int(z, -m-1, wp) + s = mpc_add(s, t, wp) + z = mpc_add_mpf(z, fone, wp) + zm = mpc_pow_int(z, -m, wp) + z2 = mpc_pow_int(z, -2, wp) + # 1/m*(z+N)^m + integral_term = mpc_div_mpf(zm, from_int(m), wp) + s = mpc_add(s, integral_term, wp) + # 1/2*(z+N)^(-(m+1)) + s = mpc_add(s, mpc_mul_mpf(mpc_div(zm, z, wp), fhalf, wp), wp) + a = m + 1 + b = 2 + k = 1 + # Important: we want to sum up to the *relative* error, + # not the absolute error, because psi^(m)(z) might be tiny + magn = mpc_abs(s, 10) + magn = magn[2]+magn[3] + eps = mpf_shift(fone, magn-wp+2) + while 1: + zm = mpc_mul(zm, z2, wp) + bern = mpf_bernoulli(2*k, wp) + scal = mpf_mul_int(bern, a, wp) + scal = mpf_div(scal, from_int(b), wp) + term = mpc_mul_mpf(zm, scal, wp) + s = mpc_add(s, term, wp) + szterm = mpc_abs(term, 10) + if k > 2 and mpf_le(szterm, eps): + break + #print k, to_str(szterm, 10), to_str(eps, 10) + a *= (m+2*k)*(m+2*k+1) + b *= (2*k+1)*(2*k+2) + k += 1 + # Scale and sign factor + v = mpc_mul_mpf(s, mpf_gamma(from_int(m+1), wp), prec, rnd) + if not (m & 1): + v = mpf_neg(v[0]), mpf_neg(v[1]) + return v + + +#-----------------------------------------------------------------------# +# # +# Riemann zeta function # +# # +#-----------------------------------------------------------------------# + +r""" +We use zeta(s) = eta(s) / (1 - 2**(1-s)) and Borwein's approximation + + n-1 + ___ k + -1 \ (-1) (d_k - d_n) + eta(s) ~= ---- ) ------------------ + d_n /___ s + k = 0 (k + 1) +where + k + ___ i + \ (n + i - 1)! 4 + d_k = n ) ---------------. + /___ (n - i)! (2i)! + i = 0 + +If s = a + b*I, the absolute error for eta(s) is bounded by + + 3 (1 + 2|b|) + ------------ * exp(|b| pi/2) + n + (3+sqrt(8)) + +Disregarding the linear term, we have approximately, + + log(err) ~= log(exp(1.58*|b|)) - log(5.8**n) + log(err) ~= 1.58*|b| - log(5.8)*n + log(err) ~= 1.58*|b| - 1.76*n + log2(err) ~= 2.28*|b| - 2.54*n + +So for p bits, we should choose n > (p + 2.28*|b|) / 2.54. + +References: +----------- + +Peter Borwein, "An Efficient Algorithm for the Riemann Zeta Function" +http://www.cecm.sfu.ca/personal/pborwein/PAPERS/P117.ps + +http://en.wikipedia.org/wiki/Dirichlet_eta_function +""" + +borwein_cache = {} + +def borwein_coefficients(n): + if n in borwein_cache: + return borwein_cache[n] + ds = [MPZ_ZERO] * (n+1) + d = MPZ_ONE + s = ds[0] = MPZ_ONE + for i in range(1, n+1): + d = d * 4 * (n+i-1) * (n-i+1) + d //= ((2*i) * ((2*i)-1)) + s += d + ds[i] = s + borwein_cache[n] = ds + return ds + +ZETA_INT_CACHE_MAX_PREC = 1000 +zeta_int_cache = {} + +def mpf_zeta_int(s, prec, rnd=round_fast): + """ + Optimized computation of zeta(s) for an integer s. + """ + wp = prec + 20 + s = int(s) + if s in zeta_int_cache and zeta_int_cache[s][0] >= wp: + return mpf_pos(zeta_int_cache[s][1], prec, rnd) + if s < 2: + if s == 1: + raise ValueError("zeta(1) pole") + if not s: + return mpf_neg(fhalf) + return mpf_div(mpf_bernoulli(-s+1, wp), from_int(s-1), prec, rnd) + # 2^-s term vanishes? + if s >= wp: + return mpf_perturb(fone, 0, prec, rnd) + # 5^-s term vanishes? + elif s >= wp*0.431: + t = one = 1 << wp + t += 1 << (wp - s) + t += one // (MPZ_THREE ** s) + t += 1 << max(0, wp - s*2) + return from_man_exp(t, -wp, prec, rnd) + else: + # Fast enough to sum directly? + # Even better, we use the Euler product (idea stolen from pari) + m = (float(wp)/(s-1) + 1) + if m < 30: + needed_terms = int(2.0**m + 1) + if needed_terms < int(wp/2.54 + 5) / 10: + t = fone + for k in list_primes(needed_terms): + #print k, needed_terms + powprec = int(wp - s*math.log(k,2)) + if powprec < 2: + break + a = mpf_sub(fone, mpf_pow_int(from_int(k), -s, powprec), wp) + t = mpf_mul(t, a, wp) + return mpf_div(fone, t, wp) + # Use Borwein's algorithm + n = int(wp/2.54 + 5) + d = borwein_coefficients(n) + t = MPZ_ZERO + s = MPZ(s) + for k in xrange(n): + t += (((-1)**k * (d[k] - d[n])) << wp) // (k+1)**s + t = (t << wp) // (-d[n]) + t = (t << wp) // ((1 << wp) - (1 << (wp+1-s))) + if (s in zeta_int_cache and zeta_int_cache[s][0] < wp) or (s not in zeta_int_cache): + zeta_int_cache[s] = (wp, from_man_exp(t, -wp-wp)) + return from_man_exp(t, -wp-wp, prec, rnd) + +def mpf_zeta(s, prec, rnd=round_fast, alt=0): + sign, man, exp, bc = s + if not man: + if s == fzero: + if alt: + return fhalf + else: + return mpf_neg(fhalf) + if s == finf: + return fone + return fnan + wp = prec + 20 + # First term vanishes? + if (not sign) and (exp + bc > (math.log(wp,2) + 2)): + return mpf_perturb(fone, alt, prec, rnd) + # Optimize for integer arguments + elif exp >= 0: + if alt: + if s == fone: + return mpf_ln2(prec, rnd) + z = mpf_zeta_int(to_int(s), wp, negative_rnd[rnd]) + q = mpf_sub(fone, mpf_pow(ftwo, mpf_sub(fone, s, wp), wp), wp) + return mpf_mul(z, q, prec, rnd) + else: + return mpf_zeta_int(to_int(s), prec, rnd) + # Negative: use the reflection formula + # Borwein only proves the accuracy bound for x >= 1/2. However, based on + # tests, the accuracy without reflection is quite good even some distance + # to the left of 1/2. XXX: verify this. + if sign: + # XXX: could use the separate refl. formula for Dirichlet eta + if alt: + q = mpf_sub(fone, mpf_pow(ftwo, mpf_sub(fone, s, wp), wp), wp) + return mpf_mul(mpf_zeta(s, wp), q, prec, rnd) + # XXX: -1 should be done exactly + y = mpf_sub(fone, s, 10*wp) + a = mpf_gamma(y, wp) + b = mpf_zeta(y, wp) + c = mpf_sin_pi(mpf_shift(s, -1), wp) + wp2 = wp + max(0,exp+bc) + pi = mpf_pi(wp+wp2) + d = mpf_div(mpf_pow(mpf_shift(pi, 1), s, wp2), pi, wp2) + return mpf_mul(a,mpf_mul(b,mpf_mul(c,d,wp),wp),prec,rnd) + + # Near pole + r = mpf_sub(fone, s, wp) + asign, aman, aexp, abc = mpf_abs(r) + pole_dist = -2*(aexp+abc) + if pole_dist > wp: + if alt: + return mpf_ln2(prec, rnd) + else: + q = mpf_neg(mpf_div(fone, r, wp)) + return mpf_add(q, mpf_euler(wp), prec, rnd) + else: + wp += max(0, pole_dist) + + t = MPZ_ZERO + #wp += 16 - (prec & 15) + # Use Borwein's algorithm + n = int(wp/2.54 + 5) + d = borwein_coefficients(n) + t = MPZ_ZERO + sf = to_fixed(s, wp) + ln2 = ln2_fixed(wp) + for k in xrange(n): + u = (-sf*log_int_fixed(k+1, wp, ln2)) >> wp + #esign, eman, eexp, ebc = mpf_exp(u, wp) + #offset = eexp + wp + #if offset >= 0: + # w = ((d[k] - d[n]) * eman) << offset + #else: + # w = ((d[k] - d[n]) * eman) >> (-offset) + eman = exp_fixed(u, wp, ln2) + w = (d[k] - d[n]) * eman + if k & 1: + t -= w + else: + t += w + t = t // (-d[n]) + t = from_man_exp(t, -wp, wp) + if alt: + return mpf_pos(t, prec, rnd) + else: + q = mpf_sub(fone, mpf_pow(ftwo, mpf_sub(fone, s, wp), wp), wp) + return mpf_div(t, q, prec, rnd) + +def mpc_zeta(s, prec, rnd=round_fast, alt=0, force=False): + re, im = s + if im == fzero: + return mpf_zeta(re, prec, rnd, alt), fzero + + # slow for large s + if (not force) and mpf_gt(mpc_abs(s, 10), from_int(prec)): + raise NotImplementedError + + wp = prec + 20 + + # Near pole + r = mpc_sub(mpc_one, s, wp) + asign, aman, aexp, abc = mpc_abs(r, 10) + pole_dist = -2*(aexp+abc) + if pole_dist > wp: + if alt: + q = mpf_ln2(wp) + y = mpf_mul(q, mpf_euler(wp), wp) + g = mpf_shift(mpf_mul(q, q, wp), -1) + g = mpf_sub(y, g) + z = mpc_mul_mpf(r, mpf_neg(g), wp) + z = mpc_add_mpf(z, q, wp) + return mpc_pos(z, prec, rnd) + else: + q = mpc_neg(mpc_div(mpc_one, r, wp)) + q = mpc_add_mpf(q, mpf_euler(wp), wp) + return mpc_pos(q, prec, rnd) + else: + wp += max(0, pole_dist) + + # Reflection formula. To be rigorous, we should reflect to the left of + # re = 1/2 (see comments for mpf_zeta), but this leads to unnecessary + # slowdown for interesting values of s + if mpf_lt(re, fzero): + # XXX: could use the separate refl. formula for Dirichlet eta + if alt: + q = mpc_sub(mpc_one, mpc_pow(mpc_two, mpc_sub(mpc_one, s, wp), + wp), wp) + return mpc_mul(mpc_zeta(s, wp), q, prec, rnd) + # XXX: -1 should be done exactly + y = mpc_sub(mpc_one, s, 10*wp) + a = mpc_gamma(y, wp) + b = mpc_zeta(y, wp) + c = mpc_sin_pi(mpc_shift(s, -1), wp) + rsign, rman, rexp, rbc = re + isign, iman, iexp, ibc = im + mag = max(rexp+rbc, iexp+ibc) + wp2 = wp + max(0, mag) + pi = mpf_pi(wp+wp2) + pi2 = (mpf_shift(pi, 1), fzero) + d = mpc_div_mpf(mpc_pow(pi2, s, wp2), pi, wp2) + return mpc_mul(a,mpc_mul(b,mpc_mul(c,d,wp),wp),prec,rnd) + n = int(wp/2.54 + 5) + n += int(0.9*abs(to_int(im))) + d = borwein_coefficients(n) + ref = to_fixed(re, wp) + imf = to_fixed(im, wp) + tre = MPZ_ZERO + tim = MPZ_ZERO + one = MPZ_ONE << wp + one_2wp = MPZ_ONE << (2*wp) + critical_line = re == fhalf + ln2 = ln2_fixed(wp) + pi2 = pi_fixed(wp-1) + wp2 = wp+wp + for k in xrange(n): + log = log_int_fixed(k+1, wp, ln2) + # A square root is much cheaper than an exp + if critical_line: + w = one_2wp // isqrt_fast((k+1) << wp2) + else: + w = exp_fixed((-ref*log) >> wp, wp) + if k & 1: + w *= (d[n] - d[k]) + else: + w *= (d[k] - d[n]) + wre, wim = cos_sin_fixed((-imf*log)>>wp, wp, pi2) + tre += (w * wre) >> wp + tim += (w * wim) >> wp + tre //= (-d[n]) + tim //= (-d[n]) + tre = from_man_exp(tre, -wp, wp) + tim = from_man_exp(tim, -wp, wp) + if alt: + return mpc_pos((tre, tim), prec, rnd) + else: + q = mpc_sub(mpc_one, mpc_pow(mpc_two, r, wp), wp) + return mpc_div((tre, tim), q, prec, rnd) + +def mpf_altzeta(s, prec, rnd=round_fast): + return mpf_zeta(s, prec, rnd, 1) + +def mpc_altzeta(s, prec, rnd=round_fast): + return mpc_zeta(s, prec, rnd, 1) + +# Not optimized currently +mpf_zetasum = None + + +def pow_fixed(x, n, wp): + if n == 1: + return x + y = MPZ_ONE << wp + while n: + if n & 1: + y = (y*x) >> wp + n -= 1 + x = (x*x) >> wp + n //= 2 + return y + +# TODO: optimize / cleanup interface / unify with list_primes +sieve_cache = [] +primes_cache = [] +mult_cache = [] + +def primesieve(n): + global sieve_cache, primes_cache, mult_cache + if n < len(sieve_cache): + sieve = sieve_cache#[:n+1] + primes = primes_cache[:primes_cache.index(max(sieve))+1] + mult = mult_cache#[:n+1] + return sieve, primes, mult + sieve = [0] * (n+1) + mult = [0] * (n+1) + primes = list_primes(n) + for p in primes: + #sieve[p::p] = p + for k in xrange(p,n+1,p): + sieve[k] = p + for i, p in enumerate(sieve): + if i >= 2: + m = 1 + n = i // p + while not n % p: + n //= p + m += 1 + mult[i] = m + sieve_cache = sieve + primes_cache = primes + mult_cache = mult + return sieve, primes, mult + +def zetasum_sieved(critical_line, sre, sim, a, n, wp): + if a < 1: + raise ValueError("a cannot be less than 1") + sieve, primes, mult = primesieve(a+n) + basic_powers = {} + one = MPZ_ONE << wp + one_2wp = MPZ_ONE << (2*wp) + wp2 = wp+wp + ln2 = ln2_fixed(wp) + pi2 = pi_fixed(wp-1) + for p in primes: + if p*2 > a+n: + break + log = log_int_fixed(p, wp, ln2) + cos, sin = cos_sin_fixed((-sim*log)>>wp, wp, pi2) + if critical_line: + u = one_2wp // isqrt_fast(p<>wp, wp) + pre = (u*cos) >> wp + pim = (u*sin) >> wp + basic_powers[p] = [(pre, pim)] + tre, tim = pre, pim + for m in range(1,int(math.log(a+n,p)+0.01)+1): + tre, tim = ((pre*tre-pim*tim)>>wp), ((pim*tre+pre*tim)>>wp) + basic_powers[p].append((tre,tim)) + xre = MPZ_ZERO + xim = MPZ_ZERO + if a == 1: + xre += one + aa = max(a,2) + for k in xrange(aa, a+n+1): + p = sieve[k] + if p in basic_powers: + m = mult[k] + tre, tim = basic_powers[p][m-1] + while 1: + k //= p**m + if k == 1: + break + p = sieve[k] + m = mult[k] + pre, pim = basic_powers[p][m-1] + tre, tim = ((pre*tre-pim*tim)>>wp), ((pim*tre+pre*tim)>>wp) + else: + log = log_int_fixed(k, wp, ln2) + cos, sin = cos_sin_fixed((-sim*log)>>wp, wp, pi2) + if critical_line: + u = one_2wp // isqrt_fast(k<>wp, wp) + tre = (u*cos) >> wp + tim = (u*sin) >> wp + xre += tre + xim += tim + return xre, xim + +# Set to something large to disable +ZETASUM_SIEVE_CUTOFF = 10 + +def mpc_zetasum(s, a, n, derivatives, reflect, prec): + """ + Fast version of mp._zetasum, assuming s = complex, a = integer. + """ + + wp = prec + 10 + derivatives = list(derivatives) + have_derivatives = derivatives != [0] + have_one_derivative = len(derivatives) == 1 + + # parse s + sre, sim = s + critical_line = (sre == fhalf) + sre = to_fixed(sre, wp) + sim = to_fixed(sim, wp) + + if a > 0 and n > ZETASUM_SIEVE_CUTOFF and not have_derivatives \ + and not reflect and (n < 4e7 or sys.maxsize > 2**32): + re, im = zetasum_sieved(critical_line, sre, sim, a, n, wp) + xs = [(from_man_exp(re, -wp, prec, 'n'), from_man_exp(im, -wp, prec, 'n'))] + return xs, [] + + maxd = max(derivatives) + if not have_one_derivative: + derivatives = range(maxd+1) + + # x_d = 0, y_d = 0 + xre = [MPZ_ZERO for d in derivatives] + xim = [MPZ_ZERO for d in derivatives] + if reflect: + yre = [MPZ_ZERO for d in derivatives] + yim = [MPZ_ZERO for d in derivatives] + else: + yre = yim = [] + + one = MPZ_ONE << wp + one_2wp = MPZ_ONE << (2*wp) + + ln2 = ln2_fixed(wp) + pi2 = pi_fixed(wp-1) + wp2 = wp+wp + + for w in xrange(a, a+n+1): + log = log_int_fixed(w, wp, ln2) + cos, sin = cos_sin_fixed((-sim*log)>>wp, wp, pi2) + if critical_line: + u = one_2wp // isqrt_fast(w<>wp, wp) + xterm_re = (u * cos) >> wp + xterm_im = (u * sin) >> wp + if reflect: + reciprocal = (one_2wp // (u*w)) + yterm_re = (reciprocal * cos) >> wp + yterm_im = (reciprocal * sin) >> wp + + if have_derivatives: + if have_one_derivative: + log = pow_fixed(log, maxd, wp) + xre[0] += (xterm_re * log) >> wp + xim[0] += (xterm_im * log) >> wp + if reflect: + yre[0] += (yterm_re * log) >> wp + yim[0] += (yterm_im * log) >> wp + else: + t = MPZ_ONE << wp + for d in derivatives: + xre[d] += (xterm_re * t) >> wp + xim[d] += (xterm_im * t) >> wp + if reflect: + yre[d] += (yterm_re * t) >> wp + yim[d] += (yterm_im * t) >> wp + t = (t * log) >> wp + else: + xre[0] += xterm_re + xim[0] += xterm_im + if reflect: + yre[0] += yterm_re + yim[0] += yterm_im + if have_derivatives: + if have_one_derivative: + if maxd % 2: + xre[0] = -xre[0] + xim[0] = -xim[0] + if reflect: + yre[0] = -yre[0] + yim[0] = -yim[0] + else: + xre = [(-1)**d * xre[d] for d in derivatives] + xim = [(-1)**d * xim[d] for d in derivatives] + if reflect: + yre = [(-1)**d * yre[d] for d in derivatives] + yim = [(-1)**d * yim[d] for d in derivatives] + xs = [(from_man_exp(xa, -wp, prec, 'n'), from_man_exp(xb, -wp, prec, 'n')) + for (xa, xb) in zip(xre, xim)] + ys = [(from_man_exp(ya, -wp, prec, 'n'), from_man_exp(yb, -wp, prec, 'n')) + for (ya, yb) in zip(yre, yim)] + return xs, ys + + +#-----------------------------------------------------------------------# +# # +# The gamma function (NEW IMPLEMENTATION) # +# # +#-----------------------------------------------------------------------# + +# Higher means faster, but more precomputation time +MAX_GAMMA_TAYLOR_PREC = 5000 +# Need to derive higher bounds for Taylor series to go higher +assert MAX_GAMMA_TAYLOR_PREC < 15000 + +# Use Stirling's series if abs(x) > beta*prec +# Important: must be large enough for convergence! +GAMMA_STIRLING_BETA = 0.2 + +SMALL_FACTORIAL_CACHE_SIZE = 150 + +gamma_taylor_cache = {} +gamma_stirling_cache = {} + +small_factorial_cache = [from_int(ifac(n)) for \ + n in range(SMALL_FACTORIAL_CACHE_SIZE+1)] + +def zeta_array(N, prec): + """ + zeta(n) = A * pi**n / n! + B + + where A is a rational number (A = Bernoulli number + for n even) and B is an infinite sum over powers of exp(2*pi). + (B = 0 for n even). + + TODO: this is currently only used for gamma, but could + be very useful elsewhere. + """ + extra = 30 + wp = prec+extra + zeta_values = [MPZ_ZERO] * (N+2) + pi = pi_fixed(wp) + # STEP 1: + one = MPZ_ONE << wp + zeta_values[0] = -one//2 + f_2pi = mpf_shift(mpf_pi(wp),1) + exp_2pi_k = exp_2pi = mpf_exp(f_2pi, wp) + # Compute exponential series + # Store values of 1/(exp(2*pi*k)-1), + # exp(2*pi*k)/(exp(2*pi*k)-1)**2, 1/(exp(2*pi*k)-1)**2 + # pi*k*exp(2*pi*k)/(exp(2*pi*k)-1)**2 + exps3 = [] + k = 1 + while 1: + tp = wp - 9*k + if tp < 1: + break + # 1/(exp(2*pi*k-1) + q1 = mpf_div(fone, mpf_sub(exp_2pi_k, fone, tp), tp) + # pi*k*exp(2*pi*k)/(exp(2*pi*k)-1)**2 + q2 = mpf_mul(exp_2pi_k, mpf_mul(q1,q1,tp), tp) + q1 = to_fixed(q1, wp) + q2 = to_fixed(q2, wp) + q2 = (k * q2 * pi) >> wp + exps3.append((q1, q2)) + # Multiply for next round + exp_2pi_k = mpf_mul(exp_2pi_k, exp_2pi, wp) + k += 1 + # Exponential sum + for n in xrange(3, N+1, 2): + s = MPZ_ZERO + k = 1 + for e1, e2 in exps3: + if n%4 == 3: + t = e1 // k**n + else: + U = (n-1)//4 + t = (e1 + e2//U) // k**n + if not t: + break + s += t + k += 1 + zeta_values[n] = -2*s + # Even zeta values + B = [mpf_abs(mpf_bernoulli(k,wp)) for k in xrange(N+2)] + pi_pow = fpi = mpf_pow_int(mpf_shift(mpf_pi(wp), 1), 2, wp) + pi_pow = mpf_div(pi_pow, from_int(4), wp) + for n in xrange(2,N+2,2): + z = mpf_mul(B[n], pi_pow, wp) + zeta_values[n] = to_fixed(z, wp) + pi_pow = mpf_mul(pi_pow, fpi, wp) + pi_pow = mpf_div(pi_pow, from_int((n+1)*(n+2)), wp) + # Zeta sum + reciprocal_pi = (one << wp) // pi + for n in xrange(3, N+1, 4): + U = (n-3)//4 + s = zeta_values[4*U+4]*(4*U+7)//4 + for k in xrange(1, U+1): + s -= (zeta_values[4*k] * zeta_values[4*U+4-4*k]) >> wp + zeta_values[n] += (2*s*reciprocal_pi) >> wp + for n in xrange(5, N+1, 4): + U = (n-1)//4 + s = zeta_values[4*U+2]*(2*U+1) + for k in xrange(1, 2*U+1): + s += ((-1)**k*2*k* zeta_values[2*k] * zeta_values[4*U+2-2*k])>>wp + zeta_values[n] += ((s*reciprocal_pi)>>wp)//(2*U) + return [x>>extra for x in zeta_values] + +def gamma_taylor_coefficients(inprec): + """ + Gives the Taylor coefficients of 1/gamma(1+x) as + a list of fixed-point numbers. Enough coefficients are returned + to ensure that the series converges to the given precision + when x is in [0.5, 1.5]. + """ + # Reuse nearby cache values (small case) + if inprec < 400: + prec = inprec + (10-(inprec%10)) + elif inprec < 1000: + prec = inprec + (30-(inprec%30)) + else: + prec = inprec + if prec in gamma_taylor_cache: + return gamma_taylor_cache[prec], prec + + # Experimentally determined bounds + if prec < 1000: + N = int(prec**0.76 + 2) + else: + # Valid to at least 15000 bits + N = int(prec**0.787 + 2) + + # Reuse higher precision values + for cprec in gamma_taylor_cache: + if cprec > prec: + coeffs = [x>>(cprec-prec) for x in gamma_taylor_cache[cprec][-N:]] + if inprec < 1000: + gamma_taylor_cache[prec] = coeffs + return coeffs, prec + + # Cache at a higher precision (large case) + if prec > 1000: + prec = int(prec * 1.2) + + wp = prec + 20 + A = [0] * N + A[0] = MPZ_ZERO + A[1] = MPZ_ONE << wp + A[2] = euler_fixed(wp) + # SLOW, reference implementation + #zeta_values = [0,0]+[to_fixed(mpf_zeta_int(k,wp),wp) for k in xrange(2,N)] + zeta_values = zeta_array(N, wp) + for k in xrange(3, N): + a = (-A[2]*A[k-1])>>wp + for j in xrange(2,k): + a += ((-1)**j * zeta_values[j] * A[k-j]) >> wp + a //= (1-k) + A[k] = a + A = [a>>20 for a in A] + A = A[::-1] + A = A[:-1] + gamma_taylor_cache[prec] = A + #return A, prec + return gamma_taylor_coefficients(inprec) + +def gamma_fixed_taylor(xmpf, x, wp, prec, rnd, type): + # Determine nearest multiple of N/2 + #n = int(x >> (wp-1)) + #steps = (n-1)>>1 + nearest_int = ((x >> (wp-1)) + MPZ_ONE) >> 1 + one = MPZ_ONE << wp + coeffs, cwp = gamma_taylor_coefficients(wp) + if nearest_int > 0: + r = one + for i in xrange(nearest_int-1): + x -= one + r = (r*x) >> wp + x -= one + p = MPZ_ZERO + for c in coeffs: + p = c + ((x*p)>>wp) + p >>= (cwp-wp) + if type == 0: + return from_man_exp((r<> wp + x += one + p = MPZ_ZERO + for c in coeffs: + p = c + ((x*p)>>wp) + p >>= (cwp-wp) + if wp - bitcount(abs(x)) > 10: + # pass very close to 0, so do floating-point multiply + g = mpf_add(xmpf, from_int(-nearest_int)) # exact + r = from_man_exp(p*r,-wp-wp) + r = mpf_mul(r, g, wp) + if type == 0: + return mpf_div(fone, r, prec, rnd) + if type == 2: + return mpf_pos(r, prec, rnd) + if type == 3: + return mpf_log(mpf_abs(mpf_div(fone, r, wp)), prec, rnd) + else: + r = from_man_exp(x*p*r,-3*wp) + if type == 0: return mpf_div(fone, r, prec, rnd) + if type == 2: return mpf_pos(r, prec, rnd) + if type == 3: return mpf_neg(mpf_log(mpf_abs(r), prec, rnd)) + +def stirling_coefficient(n): + if n in gamma_stirling_cache: + return gamma_stirling_cache[n] + p, q = bernfrac(n) + q *= MPZ(n*(n-1)) + gamma_stirling_cache[n] = p, q, bitcount(abs(p)), bitcount(q) + return gamma_stirling_cache[n] + +def real_stirling_series(x, prec): + """ + Sums the rational part of Stirling's expansion, + + log(sqrt(2*pi)) - z + 1/(12*z) - 1/(360*z^3) + ... + + """ + t = (MPZ_ONE<<(prec+prec)) // x # t = 1/x + u = (t*t)>>prec # u = 1/x**2 + s = ln_sqrt2pi_fixed(prec) - x + # Add initial terms of Stirling's series + s += t//12; t = (t*u)>>prec + s -= t//360; t = (t*u)>>prec + s += t//1260; t = (t*u)>>prec + s -= t//1680; t = (t*u)>>prec + if not t: return s + s += t//1188; t = (t*u)>>prec + s -= 691*t//360360; t = (t*u)>>prec + s += t//156; t = (t*u)>>prec + if not t: return s + s -= 3617*t//122400; t = (t*u)>>prec + s += 43867*t//244188; t = (t*u)>>prec + s -= 174611*t//125400; t = (t*u)>>prec + if not t: return s + k = 22 + # From here on, the coefficients are growing, so we + # have to keep t at a roughly constant size + usize = bitcount(abs(u)) + tsize = bitcount(abs(t)) + texp = 0 + while 1: + p, q, pb, qb = stirling_coefficient(k) + term_mag = tsize + pb + texp + shift = -texp + m = pb - term_mag + if m > 0 and shift < m: + p >>= m + shift -= m + m = tsize - term_mag + if m > 0 and shift < m: + w = t >> m + shift -= m + else: + w = t + term = (t*p//q) >> shift + if not term: + break + s += term + t = (t*u) >> usize + texp -= (prec - usize) + k += 2 + return s + +def complex_stirling_series(x, y, prec): + # t = 1/z + _m = (x*x + y*y) >> prec + tre = (x << prec) // _m + tim = (-y << prec) // _m + # u = 1/z**2 + ure = (tre*tre - tim*tim) >> prec + uim = tim*tre >> (prec-1) + # s = log(sqrt(2*pi)) - z + sre = ln_sqrt2pi_fixed(prec) - x + sim = -y + + # Add initial terms of Stirling's series + sre += tre//12; sim += tim//12; + tre, tim = ((tre*ure-tim*uim)>>prec), ((tre*uim+tim*ure)>>prec) + sre -= tre//360; sim -= tim//360; + tre, tim = ((tre*ure-tim*uim)>>prec), ((tre*uim+tim*ure)>>prec) + sre += tre//1260; sim += tim//1260; + tre, tim = ((tre*ure-tim*uim)>>prec), ((tre*uim+tim*ure)>>prec) + sre -= tre//1680; sim -= tim//1680; + tre, tim = ((tre*ure-tim*uim)>>prec), ((tre*uim+tim*ure)>>prec) + if abs(tre) + abs(tim) < 5: return sre, sim + sre += tre//1188; sim += tim//1188; + tre, tim = ((tre*ure-tim*uim)>>prec), ((tre*uim+tim*ure)>>prec) + sre -= 691*tre//360360; sim -= 691*tim//360360; + tre, tim = ((tre*ure-tim*uim)>>prec), ((tre*uim+tim*ure)>>prec) + sre += tre//156; sim += tim//156; + tre, tim = ((tre*ure-tim*uim)>>prec), ((tre*uim+tim*ure)>>prec) + if abs(tre) + abs(tim) < 5: return sre, sim + sre -= 3617*tre//122400; sim -= 3617*tim//122400; + tre, tim = ((tre*ure-tim*uim)>>prec), ((tre*uim+tim*ure)>>prec) + sre += 43867*tre//244188; sim += 43867*tim//244188; + tre, tim = ((tre*ure-tim*uim)>>prec), ((tre*uim+tim*ure)>>prec) + sre -= 174611*tre//125400; sim -= 174611*tim//125400; + tre, tim = ((tre*ure-tim*uim)>>prec), ((tre*uim+tim*ure)>>prec) + if abs(tre) + abs(tim) < 5: return sre, sim + + k = 22 + # From here on, the coefficients are growing, so we + # have to keep t at a roughly constant size + usize = bitcount(max(abs(ure), abs(uim))) + tsize = bitcount(max(abs(tre), abs(tim))) + texp = 0 + while 1: + p, q, pb, qb = stirling_coefficient(k) + term_mag = tsize + pb + texp + shift = -texp + m = pb - term_mag + if m > 0 and shift < m: + p >>= m + shift -= m + m = tsize - term_mag + if m > 0 and shift < m: + wre = tre >> m + wim = tim >> m + shift -= m + else: + wre = tre + wim = tim + termre = (tre*p//q) >> shift + termim = (tim*p//q) >> shift + if abs(termre) + abs(termim) < 5: + break + sre += termre + sim += termim + tre, tim = ((tre*ure - tim*uim)>>usize), \ + ((tre*uim + tim*ure)>>usize) + texp -= (prec - usize) + k += 2 + return sre, sim + + +def mpf_gamma(x, prec, rnd='d', type=0): + """ + This function implements multipurpose evaluation of the gamma + function, G(x), as well as the following versions of the same: + + type = 0 -- G(x) [standard gamma function] + type = 1 -- G(x+1) = x*G(x+1) = x! [factorial] + type = 2 -- 1/G(x) [reciprocal gamma function] + type = 3 -- log(|G(x)|) [log-gamma function, real part] + """ + + # Specal values + sign, man, exp, bc = x + if not man: + if x == fzero: + if type == 1: return fone + if type == 2: return fzero + raise ValueError("gamma function pole") + if x == finf: + if type == 2: return fzero + return finf + return fnan + + # First of all, for log gamma, numbers can be well beyond the fixed-point + # range, so we must take care of huge numbers before e.g. trying + # to convert x to the nearest integer + if type == 3: + wp = prec+20 + if exp+bc > wp and not sign: + return mpf_sub(mpf_mul(x, mpf_log(x, wp), wp), x, prec, rnd) + + # We strongly want to special-case small integers + is_integer = exp >= 0 + if is_integer: + # Poles + if sign: + if type == 2: + return fzero + raise ValueError("gamma function pole") + # n = x + n = man << exp + if n < SMALL_FACTORIAL_CACHE_SIZE: + if type == 0: + return mpf_pos(small_factorial_cache[n-1], prec, rnd) + if type == 1: + return mpf_pos(small_factorial_cache[n], prec, rnd) + if type == 2: + return mpf_div(fone, small_factorial_cache[n-1], prec, rnd) + if type == 3: + return mpf_log(small_factorial_cache[n-1], prec, rnd) + else: + # floor(abs(x)) + n = int(man >> (-exp)) + + # Estimate size and precision + # Estimate log(gamma(|x|),2) as x*log(x,2) + mag = exp + bc + gamma_size = n*mag + + if type == 3: + wp = prec + 20 + else: + wp = prec + bitcount(gamma_size) + 20 + + # Very close to 0, pole + if mag < -wp: + if type == 0: + return mpf_sub(mpf_div(fone,x, wp),mpf_shift(fone,-wp),prec,rnd) + if type == 1: return mpf_sub(fone, x, prec, rnd) + if type == 2: return mpf_add(x, mpf_shift(fone,mag-wp), prec, rnd) + if type == 3: return mpf_neg(mpf_log(mpf_abs(x), prec, rnd)) + + # From now on, we assume having a gamma function + if type == 1: + return mpf_gamma(mpf_add(x, fone), prec, rnd, 0) + + # Special case integers (those not small enough to be caught above, + # but still small enough for an exact factorial to be faster + # than an approximate algorithm), and half-integers + if exp >= -1: + if is_integer: + if gamma_size < 10*wp: + if type == 0: + return from_int(ifac(n-1), prec, rnd) + if type == 2: + return from_rational(MPZ_ONE, ifac(n-1), prec, rnd) + if type == 3: + return mpf_log(from_int(ifac(n-1)), prec, rnd) + # half-integer + if n < 100 or gamma_size < 10*wp: + if sign: + w = sqrtpi_fixed(wp) + if n % 2: f = ifac2(2*n+1) + else: f = -ifac2(2*n+1) + if type == 0: + return mpf_shift(from_rational(w, f, prec, rnd), -wp+n+1) + if type == 2: + return mpf_shift(from_rational(f, w, prec, rnd), wp-n-1) + if type == 3: + return mpf_log(mpf_shift(from_rational(w, abs(f), + prec, rnd), -wp+n+1), prec, rnd) + elif n == 0: + if type == 0: return mpf_sqrtpi(prec, rnd) + if type == 2: return mpf_div(fone, mpf_sqrtpi(wp), prec, rnd) + if type == 3: return mpf_log(mpf_sqrtpi(wp), prec, rnd) + else: + w = sqrtpi_fixed(wp) + w = from_man_exp(w * ifac2(2*n-1), -wp-n) + if type == 0: return mpf_pos(w, prec, rnd) + if type == 2: return mpf_div(fone, w, prec, rnd) + if type == 3: return mpf_log(mpf_abs(w), prec, rnd) + + # Convert to fixed point + offset = exp + wp + if offset >= 0: absxman = man << offset + else: absxman = man >> (-offset) + + # For log gamma, provide accurate evaluation for x = 1+eps and 2+eps + if type == 3 and not sign: + one = MPZ_ONE << wp + one_dist = abs(absxman-one) + two_dist = abs(absxman-2*one) + cancellation = (wp - bitcount(min(one_dist, two_dist))) + if cancellation > 10: + xsub1 = mpf_sub(fone, x) + xsub2 = mpf_sub(ftwo, x) + xsub1mag = xsub1[2]+xsub1[3] + xsub2mag = xsub2[2]+xsub2[3] + if xsub1mag < -wp: + return mpf_mul(mpf_euler(wp), mpf_sub(fone, x), prec, rnd) + if xsub2mag < -wp: + return mpf_mul(mpf_sub(fone, mpf_euler(wp)), + mpf_sub(x, ftwo), prec, rnd) + # Proceed but increase precision + wp += max(-xsub1mag, -xsub2mag) + offset = exp + wp + if offset >= 0: absxman = man << offset + else: absxman = man >> (-offset) + + # Use Taylor series if appropriate + n_for_stirling = int(GAMMA_STIRLING_BETA*wp) + if n < max(100, n_for_stirling) and wp < MAX_GAMMA_TAYLOR_PREC: + if sign: + absxman = -absxman + return gamma_fixed_taylor(x, absxman, wp, prec, rnd, type) + + # Use Stirling's series + # First ensure that |x| is large enough for rapid convergence + xorig = x + + # Argument reduction + r = 0 + if n < n_for_stirling: + r = one = MPZ_ONE << wp + d = n_for_stirling - n + for k in xrange(d): + r = (r * absxman) >> wp + absxman += one + x = xabs = from_man_exp(absxman, -wp) + if sign: + x = mpf_neg(x) + else: + xabs = mpf_abs(x) + + # Asymptotic series + y = real_stirling_series(absxman, wp) + u = to_fixed(mpf_log(xabs, wp), wp) + u = ((absxman - (MPZ_ONE<<(wp-1))) * u) >> wp + y += u + w = from_man_exp(y, -wp) + + # Compute final value + if sign: + # Reflection formula + A = mpf_mul(mpf_sin_pi(xorig, wp), xorig, wp) + B = mpf_neg(mpf_pi(wp)) + if type == 0 or type == 2: + A = mpf_mul(A, mpf_exp(w, wp)) + if r: + B = mpf_mul(B, from_man_exp(r, -wp), wp) + if type == 0: + return mpf_div(B, A, prec, rnd) + if type == 2: + return mpf_div(A, B, prec, rnd) + if type == 3: + if r: + B = mpf_mul(B, from_man_exp(r, -wp), wp) + A = mpf_add(mpf_log(mpf_abs(A), wp), w, wp) + return mpf_sub(mpf_log(mpf_abs(B), wp), A, prec, rnd) + else: + if type == 0: + if r: + return mpf_div(mpf_exp(w, wp), + from_man_exp(r, -wp), prec, rnd) + return mpf_exp(w, prec, rnd) + if type == 2: + if r: + return mpf_div(from_man_exp(r, -wp), + mpf_exp(w, wp), prec, rnd) + return mpf_exp(mpf_neg(w), prec, rnd) + if type == 3: + if r: + return mpf_sub(w, mpf_log(from_man_exp(r,-wp), wp), prec, rnd) + return mpf_pos(w, prec, rnd) + + +def mpc_gamma(z, prec, rnd='d', type=0): + a, b = z + asign, aman, aexp, abc = a + bsign, bman, bexp, bbc = b + + if b == fzero: + # Imaginary part on negative half-axis for log-gamma function + if type == 3 and asign: + re = mpf_gamma(a, prec, rnd, 3) + n = (-aman) >> (-aexp) + im = mpf_mul_int(mpf_pi(prec+10), n, prec, rnd) + return re, im + return mpf_gamma(a, prec, rnd, type), fzero + + # Some kind of complex inf/nan + if (not aman and aexp) or (not bman and bexp): + return (fnan, fnan) + + # Initial working precision + wp = prec + 20 + + amag = aexp+abc + bmag = bexp+bbc + if aman: + mag = max(amag, bmag) + else: + mag = bmag + + # Close to 0 + if mag < -8: + if mag < -wp: + # 1/gamma(z) = z + euler*z^2 + O(z^3) + v = mpc_add(z, mpc_mul_mpf(mpc_mul(z,z,wp),mpf_euler(wp),wp), wp) + if type == 0: return mpc_reciprocal(v, prec, rnd) + if type == 1: return mpc_div(z, v, prec, rnd) + if type == 2: return mpc_pos(v, prec, rnd) + if type == 3: return mpc_log(mpc_reciprocal(v, prec), prec, rnd) + elif type != 1: + wp += (-mag) + + # Handle huge log-gamma values; must do this before converting to + # a fixed-point value. TODO: determine a precise cutoff of validity + # depending on amag and bmag + if type == 3 and mag > wp and ((not asign) or (bmag >= amag)): + return mpc_sub(mpc_mul(z, mpc_log(z, wp), wp), z, prec, rnd) + + # From now on, we assume having a gamma function + if type == 1: + return mpc_gamma((mpf_add(a, fone), b), prec, rnd, 0) + + an = abs(to_int(a)) + bn = abs(to_int(b)) + absn = max(an, bn) + gamma_size = absn*mag + if type == 3: + pass + else: + wp += bitcount(gamma_size) + + # Reflect to the right half-plane. Note that Stirling's expansion + # is valid in the left half-plane too, as long as we're not too close + # to the real axis, but in order to use this argument reduction + # in the negative direction must be implemented. + #need_reflection = asign and ((bmag < 0) or (amag-bmag > 4)) + need_reflection = asign + zorig = z + if need_reflection: + z = mpc_neg(z) + asign, aman, aexp, abc = a = z[0] + bsign, bman, bexp, bbc = b = z[1] + + # Imaginary part very small compared to real one? + yfinal = 0 + balance_prec = 0 + if bmag < -10: + # Check z ~= 1 and z ~= 2 for loggamma + if type == 3: + zsub1 = mpc_sub_mpf(z, fone) + if zsub1[0] == fzero: + cancel1 = -bmag + else: + cancel1 = -max(zsub1[0][2]+zsub1[0][3], bmag) + if cancel1 > wp: + pi = mpf_pi(wp) + x = mpc_mul_mpf(zsub1, pi, wp) + x = mpc_mul(x, x, wp) + x = mpc_div_mpf(x, from_int(12), wp) + y = mpc_mul_mpf(zsub1, mpf_neg(mpf_euler(wp)), wp) + yfinal = mpc_add(x, y, wp) + if not need_reflection: + return mpc_pos(yfinal, prec, rnd) + elif cancel1 > 0: + wp += cancel1 + zsub2 = mpc_sub_mpf(z, ftwo) + if zsub2[0] == fzero: + cancel2 = -bmag + else: + cancel2 = -max(zsub2[0][2]+zsub2[0][3], bmag) + if cancel2 > wp: + pi = mpf_pi(wp) + t = mpf_sub(mpf_mul(pi, pi), from_int(6)) + x = mpc_mul_mpf(mpc_mul(zsub2, zsub2, wp), t, wp) + x = mpc_div_mpf(x, from_int(12), wp) + y = mpc_mul_mpf(zsub2, mpf_sub(fone, mpf_euler(wp)), wp) + yfinal = mpc_add(x, y, wp) + if not need_reflection: + return mpc_pos(yfinal, prec, rnd) + elif cancel2 > 0: + wp += cancel2 + if bmag < -wp: + # Compute directly from the real gamma function. + pp = 2*(wp+10) + aabs = mpf_abs(a) + eps = mpf_shift(fone, amag-wp) + x1 = mpf_gamma(aabs, pp, type=type) + x2 = mpf_gamma(mpf_add(aabs, eps), pp, type=type) + xprime = mpf_div(mpf_sub(x2, x1, pp), eps, pp) + y = mpf_mul(b, xprime, prec, rnd) + yfinal = (x1, y) + # Note: we still need to use the reflection formula for + # near-poles, and the correct branch of the log-gamma function + if not need_reflection: + return mpc_pos(yfinal, prec, rnd) + else: + balance_prec += (-bmag) + + wp += balance_prec + n_for_stirling = int(GAMMA_STIRLING_BETA*wp) + need_reduction = absn < n_for_stirling + + afix = to_fixed(a, wp) + bfix = to_fixed(b, wp) + + r = 0 + if not yfinal: + zprered = z + # Argument reduction + if absn < n_for_stirling: + absn = complex(an, bn) + d = int((1 + n_for_stirling**2 - bn**2)**0.5 - an) + rre = one = MPZ_ONE << wp + rim = MPZ_ZERO + for k in xrange(d): + rre, rim = ((afix*rre-bfix*rim)>>wp), ((afix*rim + bfix*rre)>>wp) + afix += one + r = from_man_exp(rre, -wp), from_man_exp(rim, -wp) + a = from_man_exp(afix, -wp) + z = a, b + + yre, yim = complex_stirling_series(afix, bfix, wp) + # (z-1/2)*log(z) + S + lre, lim = mpc_log(z, wp) + lre = to_fixed(lre, wp) + lim = to_fixed(lim, wp) + yre = ((lre*afix - lim*bfix)>>wp) - (lre>>1) + yre + yim = ((lre*bfix + lim*afix)>>wp) - (lim>>1) + yim + y = from_man_exp(yre, -wp), from_man_exp(yim, -wp) + + if r and type == 3: + # If re(z) > 0 and abs(z) <= 4, the branches of loggamma(z) + # and log(gamma(z)) coincide. Otherwise, use the zeroth order + # Stirling expansion to compute the correct imaginary part. + y = mpc_sub(y, mpc_log(r, wp), wp) + zfa = to_float(zprered[0]) + zfb = to_float(zprered[1]) + zfabs = math.hypot(zfa,zfb) + #if not (zfa > 0.0 and zfabs <= 4): + yfb = to_float(y[1]) + u = math.atan2(zfb, zfa) + if zfabs <= 0.5: + gi = 0.577216*zfb - u + else: + gi = -zfb - 0.5*u + zfa*u + zfb*math.log(zfabs) + n = int(math.floor((gi-yfb)/(2*math.pi)+0.5)) + y = (y[0], mpf_add(y[1], mpf_mul_int(mpf_pi(wp), 2*n, wp), wp)) + + if need_reflection: + if type == 0 or type == 2: + A = mpc_mul(mpc_sin_pi(zorig, wp), zorig, wp) + B = (mpf_neg(mpf_pi(wp)), fzero) + if yfinal: + if type == 2: + A = mpc_div(A, yfinal, wp) + else: + A = mpc_mul(A, yfinal, wp) + else: + A = mpc_mul(A, mpc_exp(y, wp), wp) + if r: + B = mpc_mul(B, r, wp) + if type == 0: return mpc_div(B, A, prec, rnd) + if type == 2: return mpc_div(A, B, prec, rnd) + + # Reflection formula for the log-gamma function with correct branch + # http://functions.wolfram.com/GammaBetaErf/LogGamma/16/01/01/0006/ + # LogGamma[z] == -LogGamma[-z] - Log[-z] + + # Sign[Im[z]] Floor[Re[z]] Pi I + Log[Pi] - + # Log[Sin[Pi (z - Floor[Re[z]])]] - + # Pi I (1 - Abs[Sign[Im[z]]]) Abs[Floor[Re[z]]] + if type == 3: + if yfinal: + s1 = mpc_neg(yfinal) + else: + s1 = mpc_neg(y) + # s -= log(-z) + s1 = mpc_sub(s1, mpc_log(mpc_neg(zorig), wp), wp) + # floor(re(z)) + rezfloor = mpf_floor(zorig[0]) + imzsign = mpf_sign(zorig[1]) + pi = mpf_pi(wp) + t = mpf_mul(pi, rezfloor) + t = mpf_mul_int(t, imzsign, wp) + s1 = (s1[0], mpf_add(s1[1], t, wp)) + s1 = mpc_add_mpf(s1, mpf_log(pi, wp), wp) + t = mpc_sin_pi(mpc_sub_mpf(zorig, rezfloor), wp) + t = mpc_log(t, wp) + s1 = mpc_sub(s1, t, wp) + # Note: may actually be unused, because we fall back + # to the mpf_ function for real arguments + if not imzsign: + t = mpf_mul(pi, mpf_floor(rezfloor), wp) + s1 = (s1[0], mpf_sub(s1[1], t, wp)) + return mpc_pos(s1, prec, rnd) + else: + if type == 0: + if r: + return mpc_div(mpc_exp(y, wp), r, prec, rnd) + return mpc_exp(y, prec, rnd) + if type == 2: + if r: + return mpc_div(r, mpc_exp(y, wp), prec, rnd) + return mpc_exp(mpc_neg(y), prec, rnd) + if type == 3: + return mpc_pos(y, prec, rnd) + +def mpf_factorial(x, prec, rnd='d'): + return mpf_gamma(x, prec, rnd, 1) + +def mpc_factorial(x, prec, rnd='d'): + return mpc_gamma(x, prec, rnd, 1) + +def mpf_rgamma(x, prec, rnd='d'): + return mpf_gamma(x, prec, rnd, 2) + +def mpc_rgamma(x, prec, rnd='d'): + return mpc_gamma(x, prec, rnd, 2) + +def mpf_loggamma(x, prec, rnd='d'): + sign, man, exp, bc = x + if sign: + raise ComplexResult + return mpf_gamma(x, prec, rnd, 3) + +def mpc_loggamma(z, prec, rnd='d'): + a, b = z + asign, aman, aexp, abc = a + bsign, bman, bexp, bbc = b + if b == fzero and asign: + re = mpf_gamma(a, prec, rnd, 3) + n = (-aman) >> (-aexp) + im = mpf_mul_int(mpf_pi(prec+10), n, prec, rnd) + return re, im + return mpc_gamma(z, prec, rnd, 3) + +def mpf_gamma_int(n, prec, rnd=round_fast): + if n < SMALL_FACTORIAL_CACHE_SIZE: + return mpf_pos(small_factorial_cache[n-1], prec, rnd) + return mpf_gamma(from_int(n), prec, rnd) diff --git a/lib/python3.12/site-packages/mpmath/libmp/libelefun.py b/lib/python3.12/site-packages/mpmath/libmp/libelefun.py new file mode 100644 index 0000000000000000000000000000000000000000..3de2e5aaef02296ed03dec3df3021b56823f3728 --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/libmp/libelefun.py @@ -0,0 +1,1428 @@ +""" +This module implements computation of elementary transcendental +functions (powers, logarithms, trigonometric and hyperbolic +functions, inverse trigonometric and hyperbolic) for real +floating-point numbers. + +For complex and interval implementations of the same functions, +see libmpc and libmpi. + +""" + +import math +from bisect import bisect + +from .backend import xrange +from .backend import MPZ, MPZ_ZERO, MPZ_ONE, MPZ_TWO, MPZ_FIVE, BACKEND + +from .libmpf import ( + round_floor, round_ceiling, round_down, round_up, + round_nearest, round_fast, + ComplexResult, + bitcount, bctable, lshift, rshift, giant_steps, sqrt_fixed, + from_int, to_int, from_man_exp, to_fixed, to_float, from_float, + from_rational, normalize, + fzero, fone, fnone, fhalf, finf, fninf, fnan, + mpf_cmp, mpf_sign, mpf_abs, + mpf_pos, mpf_neg, mpf_add, mpf_sub, mpf_mul, mpf_div, mpf_shift, + mpf_rdiv_int, mpf_pow_int, mpf_sqrt, + reciprocal_rnd, negative_rnd, mpf_perturb, + isqrt_fast +) + +from .libintmath import ifib + + +#------------------------------------------------------------------------------- +# Tuning parameters +#------------------------------------------------------------------------------- + +# Cutoff for computing exp from cosh+sinh. This reduces the +# number of terms by half, but also requires a square root which +# is expensive with the pure-Python square root code. +if BACKEND == 'python': + EXP_COSH_CUTOFF = 600 +else: + EXP_COSH_CUTOFF = 400 +# Cutoff for using more than 2 series +EXP_SERIES_U_CUTOFF = 1500 + +# Also basically determined by sqrt +if BACKEND == 'python': + COS_SIN_CACHE_PREC = 400 +else: + COS_SIN_CACHE_PREC = 200 +COS_SIN_CACHE_STEP = 8 +cos_sin_cache = {} + +# Number of integer logarithms to cache (for zeta sums) +MAX_LOG_INT_CACHE = 2000 +log_int_cache = {} + +LOG_TAYLOR_PREC = 2500 # Use Taylor series with caching up to this prec +LOG_TAYLOR_SHIFT = 9 # Cache log values in steps of size 2^-N +log_taylor_cache = {} +# prec/size ratio of x for fastest convergence in AGM formula +LOG_AGM_MAG_PREC_RATIO = 20 + +ATAN_TAYLOR_PREC = 3000 # Same as for log +ATAN_TAYLOR_SHIFT = 7 # steps of size 2^-N +atan_taylor_cache = {} + + +# ~= next power of two + 20 +cache_prec_steps = [22,22] +for k in xrange(1, bitcount(LOG_TAYLOR_PREC)+1): + cache_prec_steps += [min(2**k,LOG_TAYLOR_PREC)+20] * 2**(k-1) + + +#----------------------------------------------------------------------------# +# # +# Elementary mathematical constants # +# # +#----------------------------------------------------------------------------# + +def constant_memo(f): + """ + Decorator for caching computed values of mathematical + constants. This decorator should be applied to a + function taking a single argument prec as input and + returning a fixed-point value with the given precision. + """ + f.memo_prec = -1 + f.memo_val = None + def g(prec, **kwargs): + memo_prec = f.memo_prec + if prec <= memo_prec: + return f.memo_val >> (memo_prec-prec) + newprec = int(prec*1.05+10) + f.memo_val = f(newprec, **kwargs) + f.memo_prec = newprec + return f.memo_val >> (newprec-prec) + g.__name__ = f.__name__ + g.__doc__ = f.__doc__ + return g + +def def_mpf_constant(fixed): + """ + Create a function that computes the mpf value for a mathematical + constant, given a function that computes the fixed-point value. + + Assumptions: the constant is positive and has magnitude ~= 1; + the fixed-point function rounds to floor. + """ + def f(prec, rnd=round_fast): + wp = prec + 20 + v = fixed(wp) + if rnd in (round_up, round_ceiling): + v += 1 + return normalize(0, v, -wp, bitcount(v), prec, rnd) + f.__doc__ = fixed.__doc__ + return f + +def bsp_acot(q, a, b, hyperbolic): + if b - a == 1: + a1 = MPZ(2*a + 3) + if hyperbolic or a&1: + return MPZ_ONE, a1 * q**2, a1 + else: + return -MPZ_ONE, a1 * q**2, a1 + m = (a+b)//2 + p1, q1, r1 = bsp_acot(q, a, m, hyperbolic) + p2, q2, r2 = bsp_acot(q, m, b, hyperbolic) + return q2*p1 + r1*p2, q1*q2, r1*r2 + +# the acoth(x) series converges like the geometric series for x^2 +# N = ceil(p*log(2)/(2*log(x))) +def acot_fixed(a, prec, hyperbolic): + """ + Compute acot(a) or acoth(a) for an integer a with binary splitting; see + http://numbers.computation.free.fr/Constants/Algorithms/splitting.html + """ + N = int(0.35 * prec/math.log(a) + 20) + p, q, r = bsp_acot(a, 0,N, hyperbolic) + return ((p+q)<> extraprec) + +# Logarithms of integers are needed for various computations involving +# logarithms, powers, radix conversion, etc + +@constant_memo +def ln2_fixed(prec): + """ + Computes ln(2). This is done with a hyperbolic Machin-type formula, + with binary splitting at high precision. + """ + return machin([(18, 26), (-2, 4801), (8, 8749)], prec, True) + +@constant_memo +def ln10_fixed(prec): + """ + Computes ln(10). This is done with a hyperbolic Machin-type formula. + """ + return machin([(46, 31), (34, 49), (20, 161)], prec, True) + + +r""" +For computation of pi, we use the Chudnovsky series: + + oo + ___ k + 1 \ (-1) (6 k)! (A + B k) + ----- = ) ----------------------- + 12 pi /___ 3 3k+3/2 + (3 k)! (k!) C + k = 0 + +where A, B, and C are certain integer constants. This series adds roughly +14 digits per term. Note that C^(3/2) can be extracted so that the +series contains only rational terms. This makes binary splitting very +efficient. + +The recurrence formulas for the binary splitting were taken from +ftp://ftp.gmplib.org/pub/src/gmp-chudnovsky.c + +Previously, Machin's formula was used at low precision and the AGM iteration +was used at high precision. However, the Chudnovsky series is essentially as +fast as the Machin formula at low precision and in practice about 3x faster +than the AGM at high precision (despite theoretically having a worse +asymptotic complexity), so there is no reason not to use it in all cases. + +""" + +# Constants in Chudnovsky's series +CHUD_A = MPZ(13591409) +CHUD_B = MPZ(545140134) +CHUD_C = MPZ(640320) +CHUD_D = MPZ(12) + +def bs_chudnovsky(a, b, level, verbose): + """ + Computes the sum from a to b of the series in the Chudnovsky + formula. Returns g, p, q where p/q is the sum as an exact + fraction and g is a temporary value used to save work + for recursive calls. + """ + if b-a == 1: + g = MPZ((6*b-5)*(2*b-1)*(6*b-1)) + p = b**3 * CHUD_C**3 // 24 + q = (-1)**b * g * (CHUD_A+CHUD_B*b) + else: + if verbose and level < 4: + print(" binary splitting", a, b) + mid = (a+b)//2 + g1, p1, q1 = bs_chudnovsky(a, mid, level+1, verbose) + g2, p2, q2 = bs_chudnovsky(mid, b, level+1, verbose) + p = p1*p2 + g = g1*g2 + q = q1*p2 + q2*g1 + return g, p, q + +@constant_memo +def pi_fixed(prec, verbose=False, verbose_base=None): + """ + Compute floor(pi * 2**prec) as a big integer. + + This is done using Chudnovsky's series (see comments in + libelefun.py for details). + """ + # The Chudnovsky series gives 14.18 digits per term + N = int(prec/3.3219280948/14.181647462 + 2) + if verbose: + print("binary splitting with N =", N) + g, p, q = bs_chudnovsky(0, N, 0, verbose) + sqrtC = isqrt_fast(CHUD_C<<(2*prec)) + v = p*CHUD_C*sqrtC//((q+CHUD_A*p)*CHUD_D) + return v + +def degree_fixed(prec): + return pi_fixed(prec)//180 + +def bspe(a, b): + """ + Sum series for exp(1)-1 between a, b, returning the result + as an exact fraction (p, q). + """ + if b-a == 1: + return MPZ_ONE, MPZ(b) + m = (a+b)//2 + p1, q1 = bspe(a, m) + p2, q2 = bspe(m, b) + return p1*q2+p2, q1*q2 + +@constant_memo +def e_fixed(prec): + """ + Computes exp(1). This is done using the ordinary Taylor series for + exp, with binary splitting. For a description of the algorithm, + see: + + http://numbers.computation.free.fr/Constants/ + Algorithms/splitting.html + """ + # Slight overestimate of N needed for 1/N! < 2**(-prec) + # This could be tightened for large N. + N = int(1.1*prec/math.log(prec) + 20) + p, q = bspe(0,N) + return ((p+q)<> 11 + +mpf_phi = def_mpf_constant(phi_fixed) +mpf_pi = def_mpf_constant(pi_fixed) +mpf_e = def_mpf_constant(e_fixed) +mpf_degree = def_mpf_constant(degree_fixed) +mpf_ln2 = def_mpf_constant(ln2_fixed) +mpf_ln10 = def_mpf_constant(ln10_fixed) + + +@constant_memo +def ln_sqrt2pi_fixed(prec): + wp = prec + 10 + # ln(sqrt(2*pi)) = ln(2*pi)/2 + return to_fixed(mpf_log(mpf_shift(mpf_pi(wp), 1), wp), prec-1) + +@constant_memo +def sqrtpi_fixed(prec): + return sqrt_fixed(pi_fixed(prec), prec) + +mpf_sqrtpi = def_mpf_constant(sqrtpi_fixed) +mpf_ln_sqrt2pi = def_mpf_constant(ln_sqrt2pi_fixed) + + +#----------------------------------------------------------------------------# +# # +# Powers # +# # +#----------------------------------------------------------------------------# + +def mpf_pow(s, t, prec, rnd=round_fast): + """ + Compute s**t. Raises ComplexResult if s is negative and t is + fractional. + """ + ssign, sman, sexp, sbc = s + tsign, tman, texp, tbc = t + if ssign and texp < 0: + raise ComplexResult("negative number raised to a fractional power") + if texp >= 0: + return mpf_pow_int(s, (-1)**tsign * (tman<> pbc)] + if pbc > workprec: + pm = pm >> (pbc-workprec) + pe += pbc - workprec + pbc = workprec + n -= 1 + if not n: + break + y = y*y + exp = exp+exp + bc = bc + bc - 2 + bc = bc + bctable[int(y >> bc)] + if bc > workprec: + y = y >> (bc-workprec) + exp += bc - workprec + bc = workprec + n = n // 2 + return pm, pe + +# froot(s, n, prec, rnd) computes the real n-th root of a +# positive mpf tuple s. +# To compute the root we start from a 50-bit estimate for r +# generated with ordinary floating-point arithmetic, and then refine +# the value to full accuracy using the iteration + +# 1 / y \ +# r = --- | (n-1) * r + ---------- | +# n+1 n \ n r_n**(n-1) / + +# which is simply Newton's method applied to the equation r**n = y. +# With giant_steps(start, prec+extra) = [p0,...,pm, prec+extra] +# and y = man * 2**-shift one has +# (man * 2**exp)**(1/n) = +# y**(1/n) * 2**(start-prec/n) * 2**(p0-start) * ... * 2**(prec+extra-pm) * +# 2**((exp+shift-(n-1)*prec)/n -extra)) +# The last factor is accounted for in the last line of froot. + +def nthroot_fixed(y, n, prec, exp1): + start = 50 + try: + y1 = rshift(y, prec - n*start) + r = MPZ(int(y1**(1.0/n))) + except OverflowError: + y1 = from_int(y1, start) + fn = from_int(n) + fn = mpf_rdiv_int(1, fn, start) + r = mpf_pow(y1, fn, start) + r = to_int(r) + extra = 10 + extra1 = n + prevp = start + for p in giant_steps(start, prec+extra): + pm, pe = int_pow_fixed(r, n-1, prevp) + r2 = rshift(pm, (n-1)*prevp - p - pe - extra1) + B = lshift(y, 2*p-prec+extra1)//r2 + r = (B + (n-1) * lshift(r, p-prevp))//n + prevp = p + return r + +def mpf_nthroot(s, n, prec, rnd=round_fast): + """nth-root of a positive number + + Use the Newton method when faster, otherwise use x**(1/n) + """ + sign, man, exp, bc = s + if sign: + raise ComplexResult("nth root of a negative number") + if not man: + if s == fnan: + return fnan + if s == fzero: + if n > 0: + return fzero + if n == 0: + return fone + return finf + # Infinity + if not n: + return fnan + if n < 0: + return fzero + return finf + flag_inverse = False + if n < 2: + if n == 0: + return fone + if n == 1: + return mpf_pos(s, prec, rnd) + if n == -1: + return mpf_div(fone, s, prec, rnd) + # n < 0 + rnd = reciprocal_rnd[rnd] + flag_inverse = True + extra_inverse = 5 + prec += extra_inverse + n = -n + if n > 20 and (n >= 20000 or prec < int(233 + 28.3 * n**0.62)): + prec2 = prec + 10 + fn = from_int(n) + nth = mpf_rdiv_int(1, fn, prec2) + r = mpf_pow(s, nth, prec2, rnd) + s = normalize(r[0], r[1], r[2], r[3], prec, rnd) + if flag_inverse: + return mpf_div(fone, s, prec-extra_inverse, rnd) + else: + return s + # Convert to a fixed-point number with prec2 bits. + prec2 = prec + 2*n - (prec%n) + # a few tests indicate that + # for 10 < n < 10**4 a bit more precision is needed + if n > 10: + prec2 += prec2//10 + prec2 = prec2 - prec2%n + # Mantissa may have more bits than we need. Trim it down. + shift = bc - prec2 + # Adjust exponents to make prec2 and exp+shift multiples of n. + sign1 = 0 + es = exp+shift + if es < 0: + sign1 = 1 + es = -es + if sign1: + shift += es%n + else: + shift -= es%n + man = rshift(man, shift) + extra = 10 + exp1 = ((exp+shift-(n-1)*prec2)//n) - extra + rnd_shift = 0 + if flag_inverse: + if rnd == 'u' or rnd == 'c': + rnd_shift = 1 + else: + if rnd == 'd' or rnd == 'f': + rnd_shift = 1 + man = nthroot_fixed(man+rnd_shift, n, prec2, exp1) + s = from_man_exp(man, exp1, prec, rnd) + if flag_inverse: + return mpf_div(fone, s, prec-extra_inverse, rnd) + else: + return s + +def mpf_cbrt(s, prec, rnd=round_fast): + """cubic root of a positive number""" + return mpf_nthroot(s, 3, prec, rnd) + +#----------------------------------------------------------------------------# +# # +# Logarithms # +# # +#----------------------------------------------------------------------------# + + +def log_int_fixed(n, prec, ln2=None): + """ + Fast computation of log(n), caching the value for small n, + intended for zeta sums. + """ + if n in log_int_cache: + value, vprec = log_int_cache[n] + if vprec >= prec: + return value >> (vprec - prec) + wp = prec + 10 + if wp <= LOG_TAYLOR_SHIFT: + if ln2 is None: + ln2 = ln2_fixed(wp) + r = bitcount(n) + x = n << (wp-r) + v = log_taylor_cached(x, wp) + r*ln2 + else: + v = to_fixed(mpf_log(from_int(n), wp+5), wp) + if n < MAX_LOG_INT_CACHE: + log_int_cache[n] = (v, wp) + return v >> (wp-prec) + +def agm_fixed(a, b, prec): + """ + Fixed-point computation of agm(a,b), assuming + a, b both close to unit magnitude. + """ + i = 0 + while 1: + anew = (a+b)>>1 + if i > 4 and abs(a-anew) < 8: + return a + b = isqrt_fast(a*b) + a = anew + i += 1 + return a + +def log_agm(x, prec): + """ + Fixed-point computation of -log(x) = log(1/x), suitable + for large precision. It is required that 0 < x < 1. The + algorithm used is the Sasaki-Kanada formula + + -log(x) = pi/agm(theta2(x)^2,theta3(x)^2). [1] + + For faster convergence in the theta functions, x should + be chosen closer to 0. + + Guard bits must be added by the caller. + + HYPOTHESIS: if x = 2^(-n), n bits need to be added to + account for the truncation to a fixed-point number, + and this is the only significant cancellation error. + + The number of bits lost to roundoff is small and can be + considered constant. + + [1] Richard P. Brent, "Fast Algorithms for High-Precision + Computation of Elementary Functions (extended abstract)", + http://wwwmaths.anu.edu.au/~brent/pd/RNC7-Brent.pdf + + """ + x2 = (x*x) >> prec + # Compute jtheta2(x)**2 + s = a = b = x2 + while a: + b = (b*x2) >> prec + a = (a*b) >> prec + s += a + s += (MPZ_ONE<>(prec-2) + s = (s*isqrt_fast(x<>prec + # Compute jtheta3(x)**2 + t = a = b = x + while a: + b = (b*x2) >> prec + a = (a*b) >> prec + t += a + t = (MPZ_ONE<>prec + # Final formula + p = agm_fixed(s, t, prec) + return (pi_fixed(prec) << prec) // p + +def log_taylor(x, prec, r=0): + """ + Fixed-point calculation of log(x). It is assumed that x is close + enough to 1 for the Taylor series to converge quickly. Convergence + can be improved by specifying r > 0 to compute + log(x^(1/2^r))*2^r, at the cost of performing r square roots. + + The caller must provide sufficient guard bits. + """ + for i in xrange(r): + x = isqrt_fast(x<> prec + v4 = (v2*v2) >> prec + s0 = v + s1 = v//3 + v = (v*v4) >> prec + k = 5 + while v: + s0 += v // k + k += 2 + s1 += v // k + v = (v*v4) >> prec + k += 2 + s1 = (s1*v2) >> prec + s = (s0+s1) << (1+r) + if sign: + return -s + return s + +def log_taylor_cached(x, prec): + """ + Fixed-point computation of log(x), assuming x in (0.5, 2) + and prec <= LOG_TAYLOR_PREC. + """ + n = x >> (prec-LOG_TAYLOR_SHIFT) + cached_prec = cache_prec_steps[prec] + dprec = cached_prec - prec + if (n, cached_prec) in log_taylor_cache: + a, log_a = log_taylor_cache[n, cached_prec] + else: + a = n << (cached_prec - LOG_TAYLOR_SHIFT) + log_a = log_taylor(a, cached_prec, 8) + log_taylor_cache[n, cached_prec] = (a, log_a) + a >>= dprec + log_a >>= dprec + u = ((x - a) << prec) // a + v = (u << prec) // ((MPZ_TWO << prec) + u) + v2 = (v*v) >> prec + v4 = (v2*v2) >> prec + s0 = v + s1 = v//3 + v = (v*v4) >> prec + k = 5 + while v: + s0 += v//k + k += 2 + s1 += v//k + v = (v*v4) >> prec + k += 2 + s1 = (s1*v2) >> prec + s = (s0+s1) << 1 + return log_a + s + +def mpf_log(x, prec, rnd=round_fast): + """ + Compute the natural logarithm of the mpf value x. If x is negative, + ComplexResult is raised. + """ + sign, man, exp, bc = x + #------------------------------------------------------------------ + # Handle special values + if not man: + if x == fzero: return fninf + if x == finf: return finf + if x == fnan: return fnan + if sign: + raise ComplexResult("logarithm of a negative number") + wp = prec + 20 + #------------------------------------------------------------------ + # Handle log(2^n) = log(n)*2. + # Here we catch the only possible exact value, log(1) = 0 + if man == 1: + if not exp: + return fzero + return from_man_exp(exp*ln2_fixed(wp), -wp, prec, rnd) + mag = exp+bc + abs_mag = abs(mag) + #------------------------------------------------------------------ + # Handle x = 1+eps, where log(x) ~ x. We need to check for + # cancellation when moving to fixed-point math and compensate + # by increasing the precision. Note that abs_mag in (0, 1) <=> + # 0.5 < x < 2 and x != 1 + if abs_mag <= 1: + # Calculate t = x-1 to measure distance from 1 in bits + tsign = 1-abs_mag + if tsign: + tman = (MPZ_ONE< wp: + t = normalize(tsign, tman, abs_mag-bc, tbc, tbc, 'n') + return mpf_perturb(t, tsign, prec, rnd) + else: + wp += cancellation + # TODO: if close enough to 1, we could use Taylor series + # even in the AGM precision range, since the Taylor series + # converges rapidly + #------------------------------------------------------------------ + # Another special case: + # n*log(2) is a good enough approximation + if abs_mag > 10000: + if bitcount(abs_mag) > wp: + return from_man_exp(exp*ln2_fixed(wp), -wp, prec, rnd) + #------------------------------------------------------------------ + # General case. + # Perform argument reduction using log(x) = log(x*2^n) - n*log(2): + # If we are in the Taylor precision range, choose magnitude 0 or 1. + # If we are in the AGM precision range, choose magnitude -m for + # some large m; benchmarking on one machine showed m = prec/20 to be + # optimal between 1000 and 100,000 digits. + if wp <= LOG_TAYLOR_PREC: + m = log_taylor_cached(lshift(man, wp-bc), wp) + if mag: + m += mag*ln2_fixed(wp) + else: + optimal_mag = -wp//LOG_AGM_MAG_PREC_RATIO + n = optimal_mag - mag + x = mpf_shift(x, n) + wp += (-optimal_mag) + m = -log_agm(to_fixed(x, wp), wp) + m -= n*ln2_fixed(wp) + return from_man_exp(m, -wp, prec, rnd) + +def mpf_log_hypot(a, b, prec, rnd): + """ + Computes log(sqrt(a^2+b^2)) accurately. + """ + # If either a or b is inf/nan/0, assume it to be a + if not b[1]: + a, b = b, a + # a is inf/nan/0 + if not a[1]: + # both are inf/nan/0 + if not b[1]: + if a == b == fzero: + return fninf + if fnan in (a, b): + return fnan + # at least one term is (+/- inf)^2 + return finf + # only a is inf/nan/0 + if a == fzero: + # log(sqrt(0+b^2)) = log(|b|) + return mpf_log(mpf_abs(b), prec, rnd) + if a == fnan: + return fnan + return finf + # Exact + a2 = mpf_mul(a,a) + b2 = mpf_mul(b,b) + extra = 20 + # Not exact + h2 = mpf_add(a2, b2, prec+extra) + cancelled = mpf_add(h2, fnone, 10) + mag_cancelled = cancelled[2]+cancelled[3] + # Just redo the sum exactly if necessary (could be smarter + # and avoid memory allocation when a or b is precisely 1 + # and the other is tiny...) + if cancelled == fzero or mag_cancelled < -extra//2: + h2 = mpf_add(a2, b2, prec+extra-min(a2[2],b2[2])) + return mpf_shift(mpf_log(h2, prec, rnd), -1) + + +#---------------------------------------------------------------------- +# Inverse tangent +# + +def atan_newton(x, prec): + if prec >= 100: + r = math.atan(int((x>>(prec-53)))/2.0**53) + else: + r = math.atan(int(x)/2.0**prec) + prevp = 50 + r = MPZ(int(r * 2.0**53) >> (53-prevp)) + extra_p = 50 + for wp in giant_steps(prevp, prec): + wp += extra_p + r = r << (wp-prevp) + cos, sin = cos_sin_fixed(r, wp) + tan = (sin << wp) // cos + a = ((tan-rshift(x, prec-wp)) << wp) // ((MPZ_ONE<>wp)) + r = r - a + prevp = wp + return rshift(r, prevp-prec) + +def atan_taylor_get_cached(n, prec): + # Taylor series with caching wins up to huge precisions + # To avoid unnecessary precomputation at low precision, we + # do it in steps + # Round to next power of 2 + prec2 = (1<<(bitcount(prec-1))) + 20 + dprec = prec2 - prec + if (n, prec2) in atan_taylor_cache: + a, atan_a = atan_taylor_cache[n, prec2] + else: + a = n << (prec2 - ATAN_TAYLOR_SHIFT) + atan_a = atan_newton(a, prec2) + atan_taylor_cache[n, prec2] = (a, atan_a) + return (a >> dprec), (atan_a >> dprec) + +def atan_taylor(x, prec): + n = (x >> (prec-ATAN_TAYLOR_SHIFT)) + a, atan_a = atan_taylor_get_cached(n, prec) + d = x - a + s0 = v = (d << prec) // ((a**2 >> prec) + (a*d >> prec) + (MPZ_ONE << prec)) + v2 = (v**2 >> prec) + v4 = (v2 * v2) >> prec + s1 = v//3 + v = (v * v4) >> prec + k = 5 + while v: + s0 += v // k + k += 2 + s1 += v // k + v = (v * v4) >> prec + k += 2 + s1 = (s1 * v2) >> prec + s = s0 - s1 + return atan_a + s + +def atan_inf(sign, prec, rnd): + if not sign: + return mpf_shift(mpf_pi(prec, rnd), -1) + return mpf_neg(mpf_shift(mpf_pi(prec, negative_rnd[rnd]), -1)) + +def mpf_atan(x, prec, rnd=round_fast): + sign, man, exp, bc = x + if not man: + if x == fzero: return fzero + if x == finf: return atan_inf(0, prec, rnd) + if x == fninf: return atan_inf(1, prec, rnd) + return fnan + mag = exp + bc + # Essentially infinity + if mag > prec+20: + return atan_inf(sign, prec, rnd) + # Essentially ~ x + if -mag > prec+20: + return mpf_perturb(x, 1-sign, prec, rnd) + wp = prec + 30 + abs(mag) + # For large x, use atan(x) = pi/2 - atan(1/x) + if mag >= 2: + x = mpf_rdiv_int(1, x, wp) + reciprocal = True + else: + reciprocal = False + t = to_fixed(x, wp) + if sign: + t = -t + if wp < ATAN_TAYLOR_PREC: + a = atan_taylor(t, wp) + else: + a = atan_newton(t, wp) + if reciprocal: + a = ((pi_fixed(wp)>>1)+1) - a + if sign: + a = -a + return from_man_exp(a, -wp, prec, rnd) + +# TODO: cleanup the special cases +def mpf_atan2(y, x, prec, rnd=round_fast): + xsign, xman, xexp, xbc = x + ysign, yman, yexp, ybc = y + if not yman: + if y == fzero and x != fnan: + if mpf_sign(x) >= 0: + return fzero + return mpf_pi(prec, rnd) + if y in (finf, fninf): + if x in (finf, fninf): + return fnan + # pi/2 + if y == finf: + return mpf_shift(mpf_pi(prec, rnd), -1) + # -pi/2 + return mpf_neg(mpf_shift(mpf_pi(prec, negative_rnd[rnd]), -1)) + return fnan + if ysign: + return mpf_neg(mpf_atan2(mpf_neg(y), x, prec, negative_rnd[rnd])) + if not xman: + if x == fnan: + return fnan + if x == finf: + return fzero + if x == fninf: + return mpf_pi(prec, rnd) + if y == fzero: + return fzero + return mpf_shift(mpf_pi(prec, rnd), -1) + tquo = mpf_atan(mpf_div(y, x, prec+4), prec+4) + if xsign: + return mpf_add(mpf_pi(prec+4), tquo, prec, rnd) + else: + return mpf_pos(tquo, prec, rnd) + +def mpf_asin(x, prec, rnd=round_fast): + sign, man, exp, bc = x + if bc+exp > 0 and x not in (fone, fnone): + raise ComplexResult("asin(x) is real only for -1 <= x <= 1") + # asin(x) = 2*atan(x/(1+sqrt(1-x**2))) + wp = prec + 15 + a = mpf_mul(x, x) + b = mpf_add(fone, mpf_sqrt(mpf_sub(fone, a, wp), wp), wp) + c = mpf_div(x, b, wp) + return mpf_shift(mpf_atan(c, prec, rnd), 1) + +def mpf_acos(x, prec, rnd=round_fast): + # acos(x) = 2*atan(sqrt(1-x**2)/(1+x)) + sign, man, exp, bc = x + if bc + exp > 0: + if x not in (fone, fnone): + raise ComplexResult("acos(x) is real only for -1 <= x <= 1") + if x == fnone: + return mpf_pi(prec, rnd) + wp = prec + 15 + a = mpf_mul(x, x) + b = mpf_sqrt(mpf_sub(fone, a, wp), wp) + c = mpf_div(b, mpf_add(fone, x, wp), wp) + return mpf_shift(mpf_atan(c, prec, rnd), 1) + +def mpf_asinh(x, prec, rnd=round_fast): + wp = prec + 20 + sign, man, exp, bc = x + mag = exp+bc + if mag < -8: + if mag < -wp: + return mpf_perturb(x, 1-sign, prec, rnd) + wp += (-mag) + # asinh(x) = log(x+sqrt(x**2+1)) + # use reflection symmetry to avoid cancellation + q = mpf_sqrt(mpf_add(mpf_mul(x, x), fone, wp), wp) + q = mpf_add(mpf_abs(x), q, wp) + if sign: + return mpf_neg(mpf_log(q, prec, negative_rnd[rnd])) + else: + return mpf_log(q, prec, rnd) + +def mpf_acosh(x, prec, rnd=round_fast): + # acosh(x) = log(x+sqrt(x**2-1)) + wp = prec + 15 + if mpf_cmp(x, fone) == -1: + raise ComplexResult("acosh(x) is real only for x >= 1") + q = mpf_sqrt(mpf_add(mpf_mul(x,x), fnone, wp), wp) + return mpf_log(mpf_add(x, q, wp), prec, rnd) + +def mpf_atanh(x, prec, rnd=round_fast): + # atanh(x) = log((1+x)/(1-x))/2 + sign, man, exp, bc = x + if (not man) and exp: + if x in (fzero, fnan): + return x + raise ComplexResult("atanh(x) is real only for -1 <= x <= 1") + mag = bc + exp + if mag > 0: + if mag == 1 and man == 1: + return [finf, fninf][sign] + raise ComplexResult("atanh(x) is real only for -1 <= x <= 1") + wp = prec + 15 + if mag < -8: + if mag < -wp: + return mpf_perturb(x, sign, prec, rnd) + wp += (-mag) + a = mpf_add(x, fone, wp) + b = mpf_sub(fone, x, wp) + return mpf_shift(mpf_log(mpf_div(a, b, wp), prec, rnd), -1) + +def mpf_fibonacci(x, prec, rnd=round_fast): + sign, man, exp, bc = x + if not man: + if x == fninf: + return fnan + return x + # F(2^n) ~= 2^(2^n) + size = abs(exp+bc) + if exp >= 0: + # Exact + if size < 10 or size <= bitcount(prec): + return from_int(ifib(to_int(x)), prec, rnd) + # Use the modified Binet formula + wp = prec + size + 20 + a = mpf_phi(wp) + b = mpf_add(mpf_shift(a, 1), fnone, wp) + u = mpf_pow(a, x, wp) + v = mpf_cos_pi(x, wp) + v = mpf_div(v, u, wp) + u = mpf_sub(u, v, wp) + u = mpf_div(u, b, prec, rnd) + return u + + +#------------------------------------------------------------------------------- +# Exponential-type functions +#------------------------------------------------------------------------------- + +def exponential_series(x, prec, type=0): + """ + Taylor series for cosh/sinh or cos/sin. + + type = 0 -- returns exp(x) (slightly faster than cosh+sinh) + type = 1 -- returns (cosh(x), sinh(x)) + type = 2 -- returns (cos(x), sin(x)) + """ + if x < 0: + x = -x + sign = 1 + else: + sign = 0 + r = int(0.5*prec**0.5) + xmag = bitcount(x) - prec + r = max(0, xmag + r) + extra = 10 + 2*max(r,-xmag) + wp = prec + extra + x <<= (extra - r) + one = MPZ_ONE << wp + alt = (type == 2) + if prec < EXP_SERIES_U_CUTOFF: + x2 = a = (x*x) >> wp + x4 = (x2*x2) >> wp + s0 = s1 = MPZ_ZERO + k = 2 + while a: + a //= (k-1)*k; s0 += a; k += 2 + a //= (k-1)*k; s1 += a; k += 2 + a = (a*x4) >> wp + s1 = (x2*s1) >> wp + if alt: + c = s1 - s0 + one + else: + c = s1 + s0 + one + else: + u = int(0.3*prec**0.35) + x2 = a = (x*x) >> wp + xpowers = [one, x2] + for i in xrange(1, u): + xpowers.append((xpowers[-1]*x2)>>wp) + sums = [MPZ_ZERO] * u + k = 2 + while a: + for i in xrange(u): + a //= (k-1)*k + if alt and k & 2: sums[i] -= a + else: sums[i] += a + k += 2 + a = (a*xpowers[-1]) >> wp + for i in xrange(1, u): + sums[i] = (sums[i]*xpowers[i]) >> wp + c = sum(sums) + one + if type == 0: + s = isqrt_fast(c*c - (one<> wp + return v >> extra + else: + # Repeatedly apply the double-angle formula + # cosh(2*x) = 2*cosh(x)^2 - 1 + # cos(2*x) = 2*cos(x)^2 - 1 + pshift = wp-1 + for i in xrange(r): + c = ((c*c) >> pshift) - one + # With the abs, this is the same for sinh and sin + s = isqrt_fast(abs((one<>extra), (s>>extra) + +def exp_basecase(x, prec): + """ + Compute exp(x) as a fixed-point number. Works for any x, + but for speed should have |x| < 1. For an arbitrary number, + use exp(x) = exp(x-m*log(2)) * 2^m where m = floor(x/log(2)). + """ + if prec > EXP_COSH_CUTOFF: + return exponential_series(x, prec, 0) + r = int(prec**0.5) + prec += r + s0 = s1 = (MPZ_ONE << prec) + k = 2 + a = x2 = (x*x) >> prec + while a: + a //= k; s0 += a; k += 1 + a //= k; s1 += a; k += 1 + a = (a*x2) >> prec + s1 = (s1*x) >> prec + s = s0 + s1 + u = r + while r: + s = (s*s) >> prec + r -= 1 + return s >> u + +def exp_expneg_basecase(x, prec): + """ + Computation of exp(x), exp(-x) + """ + if prec > EXP_COSH_CUTOFF: + cosh, sinh = exponential_series(x, prec, 1) + return cosh+sinh, cosh-sinh + a = exp_basecase(x, prec) + b = (MPZ_ONE << (prec+prec)) // a + return a, b + +def cos_sin_basecase(x, prec): + """ + Compute cos(x), sin(x) as fixed-point numbers, assuming x + in [0, pi/2). For an arbitrary number, use x' = x - m*(pi/2) + where m = floor(x/(pi/2)) along with quarter-period symmetries. + """ + if prec > COS_SIN_CACHE_PREC: + return exponential_series(x, prec, 2) + precs = prec - COS_SIN_CACHE_STEP + t = x >> precs + n = int(t) + if n not in cos_sin_cache: + w = t<<(10+COS_SIN_CACHE_PREC-COS_SIN_CACHE_STEP) + cos_t, sin_t = exponential_series(w, 10+COS_SIN_CACHE_PREC, 2) + cos_sin_cache[n] = (cos_t>>10), (sin_t>>10) + cos_t, sin_t = cos_sin_cache[n] + offset = COS_SIN_CACHE_PREC - prec + cos_t >>= offset + sin_t >>= offset + x -= t << precs + cos = MPZ_ONE << prec + sin = x + k = 2 + a = -((x*x) >> prec) + while a: + a //= k; cos += a; k += 1; a = (a*x) >> prec + a //= k; sin += a; k += 1; a = -((a*x) >> prec) + return ((cos*cos_t-sin*sin_t) >> prec), ((sin*cos_t+cos*sin_t) >> prec) + +def mpf_exp(x, prec, rnd=round_fast): + sign, man, exp, bc = x + if man: + mag = bc + exp + wp = prec + 14 + if sign: + man = -man + # TODO: the best cutoff depends on both x and the precision. + if prec > 600 and exp >= 0: + # Need about log2(exp(n)) ~= 1.45*mag extra precision + e = mpf_e(wp+int(1.45*mag)) + return mpf_pow_int(e, man<= 2 + if mag > 1: + # For large arguments: exp(2^mag*(1+eps)) = + # exp(2^mag)*exp(2^mag*eps) = exp(2^mag)*(1 + 2^mag*eps + ...) + # so about mag extra bits is required. + wpmod = wp + mag + offset = exp + wpmod + if offset >= 0: + t = man << offset + else: + t = man >> (-offset) + lg2 = ln2_fixed(wpmod) + n, t = divmod(t, lg2) + n = int(n) + t >>= mag + else: + offset = exp + wp + if offset >= 0: + t = man << offset + else: + t = man >> (-offset) + n = 0 + man = exp_basecase(t, wp) + return from_man_exp(man, n-wp, prec, rnd) + if not exp: + return fone + if x == fninf: + return fzero + return x + + +def mpf_cosh_sinh(x, prec, rnd=round_fast, tanh=0): + """Simultaneously compute (cosh(x), sinh(x)) for real x""" + sign, man, exp, bc = x + if (not man) and exp: + if tanh: + if x == finf: return fone + if x == fninf: return fnone + return fnan + if x == finf: return (finf, finf) + if x == fninf: return (finf, fninf) + return fnan, fnan + mag = exp+bc + wp = prec+14 + if mag < -4: + # Extremely close to 0, sinh(x) ~= x and cosh(x) ~= 1 + if mag < -wp: + if tanh: + return mpf_perturb(x, 1-sign, prec, rnd) + cosh = mpf_perturb(fone, 0, prec, rnd) + sinh = mpf_perturb(x, sign, prec, rnd) + return cosh, sinh + # Fix for cancellation when computing sinh + wp += (-mag) + # Does exp(-2*x) vanish? + if mag > 10: + if 3*(1<<(mag-1)) > wp: + # XXX: rounding + if tanh: + return mpf_perturb([fone,fnone][sign], 1-sign, prec, rnd) + c = s = mpf_shift(mpf_exp(mpf_abs(x), prec, rnd), -1) + if sign: + s = mpf_neg(s) + return c, s + # |x| > 1 + if mag > 1: + wpmod = wp + mag + offset = exp + wpmod + if offset >= 0: + t = man << offset + else: + t = man >> (-offset) + lg2 = ln2_fixed(wpmod) + n, t = divmod(t, lg2) + n = int(n) + t >>= mag + else: + offset = exp + wp + if offset >= 0: + t = man << offset + else: + t = man >> (-offset) + n = 0 + a, b = exp_expneg_basecase(t, wp) + # TODO: optimize division precision + cosh = a + (b>>(2*n)) + sinh = a - (b>>(2*n)) + if sign: + sinh = -sinh + if tanh: + man = (sinh << wp) // cosh + return from_man_exp(man, -wp, prec, rnd) + else: + cosh = from_man_exp(cosh, n-wp-1, prec, rnd) + sinh = from_man_exp(sinh, n-wp-1, prec, rnd) + return cosh, sinh + + +def mod_pi2(man, exp, mag, wp): + # Reduce to standard interval + if mag > 0: + i = 0 + while 1: + cancellation_prec = 20 << i + wpmod = wp + mag + cancellation_prec + pi2 = pi_fixed(wpmod-1) + pi4 = pi2 >> 1 + offset = wpmod + exp + if offset >= 0: + t = man << offset + else: + t = man >> (-offset) + n, y = divmod(t, pi2) + if y > pi4: + small = pi2 - y + else: + small = y + if small >> (wp+mag-10): + n = int(n) + t = y >> mag + wp = wpmod - mag + break + i += 1 + else: + wp += (-mag) + offset = exp + wp + if offset >= 0: + t = man << offset + else: + t = man >> (-offset) + n = 0 + return t, n, wp + + +def mpf_cos_sin(x, prec, rnd=round_fast, which=0, pi=False): + """ + which: + 0 -- return cos(x), sin(x) + 1 -- return cos(x) + 2 -- return sin(x) + 3 -- return tan(x) + + if pi=True, compute for pi*x + """ + sign, man, exp, bc = x + if not man: + if exp: + c, s = fnan, fnan + else: + c, s = fone, fzero + if which == 0: return c, s + if which == 1: return c + if which == 2: return s + if which == 3: return s + + mag = bc + exp + wp = prec + 10 + + # Extremely small? + if mag < 0: + if mag < -wp: + if pi: + x = mpf_mul(x, mpf_pi(wp)) + c = mpf_perturb(fone, 1, prec, rnd) + s = mpf_perturb(x, 1-sign, prec, rnd) + if which == 0: return c, s + if which == 1: return c + if which == 2: return s + if which == 3: return mpf_perturb(x, sign, prec, rnd) + if pi: + if exp >= -1: + if exp == -1: + c = fzero + s = (fone, fnone)[bool(man & 2) ^ sign] + elif exp == 0: + c, s = (fnone, fzero) + else: + c, s = (fone, fzero) + if which == 0: return c, s + if which == 1: return c + if which == 2: return s + if which == 3: return mpf_div(s, c, prec, rnd) + # Subtract nearest half-integer (= mod by pi/2) + n = ((man >> (-exp-2)) + 1) >> 1 + man = man - (n << (-exp-1)) + mag2 = bitcount(man) + exp + wp = prec + 10 - mag2 + offset = exp + wp + if offset >= 0: + t = man << offset + else: + t = man >> (-offset) + t = (t*pi_fixed(wp)) >> wp + else: + t, n, wp = mod_pi2(man, exp, mag, wp) + c, s = cos_sin_basecase(t, wp) + m = n & 3 + if m == 1: c, s = -s, c + elif m == 2: c, s = -c, -s + elif m == 3: c, s = s, -c + if sign: + s = -s + if which == 0: + c = from_man_exp(c, -wp, prec, rnd) + s = from_man_exp(s, -wp, prec, rnd) + return c, s + if which == 1: + return from_man_exp(c, -wp, prec, rnd) + if which == 2: + return from_man_exp(s, -wp, prec, rnd) + if which == 3: + return from_rational(s, c, prec, rnd) + +def mpf_cos(x, prec, rnd=round_fast): return mpf_cos_sin(x, prec, rnd, 1) +def mpf_sin(x, prec, rnd=round_fast): return mpf_cos_sin(x, prec, rnd, 2) +def mpf_tan(x, prec, rnd=round_fast): return mpf_cos_sin(x, prec, rnd, 3) +def mpf_cos_sin_pi(x, prec, rnd=round_fast): return mpf_cos_sin(x, prec, rnd, 0, 1) +def mpf_cos_pi(x, prec, rnd=round_fast): return mpf_cos_sin(x, prec, rnd, 1, 1) +def mpf_sin_pi(x, prec, rnd=round_fast): return mpf_cos_sin(x, prec, rnd, 2, 1) +def mpf_cosh(x, prec, rnd=round_fast): return mpf_cosh_sinh(x, prec, rnd)[0] +def mpf_sinh(x, prec, rnd=round_fast): return mpf_cosh_sinh(x, prec, rnd)[1] +def mpf_tanh(x, prec, rnd=round_fast): return mpf_cosh_sinh(x, prec, rnd, tanh=1) + + +# Low-overhead fixed-point versions + +def cos_sin_fixed(x, prec, pi2=None): + if pi2 is None: + pi2 = pi_fixed(prec-1) + n, t = divmod(x, pi2) + n = int(n) + c, s = cos_sin_basecase(t, prec) + m = n & 3 + if m == 0: return c, s + if m == 1: return -s, c + if m == 2: return -c, -s + if m == 3: return s, -c + +def exp_fixed(x, prec, ln2=None): + if ln2 is None: + ln2 = ln2_fixed(prec) + n, t = divmod(x, ln2) + n = int(n) + v = exp_basecase(t, prec) + if n >= 0: + return v << n + else: + return v >> (-n) + + +if BACKEND == 'sage': + try: + import sage.libs.mpmath.ext_libmp as _lbmp + mpf_sqrt = _lbmp.mpf_sqrt + mpf_exp = _lbmp.mpf_exp + mpf_log = _lbmp.mpf_log + mpf_cos = _lbmp.mpf_cos + mpf_sin = _lbmp.mpf_sin + mpf_pow = _lbmp.mpf_pow + exp_fixed = _lbmp.exp_fixed + cos_sin_fixed = _lbmp.cos_sin_fixed + log_int_fixed = _lbmp.log_int_fixed + except (ImportError, AttributeError): + print("Warning: Sage imports in libelefun failed") diff --git a/lib/python3.12/site-packages/mpmath/libmp/libhyper.py b/lib/python3.12/site-packages/mpmath/libmp/libhyper.py new file mode 100644 index 0000000000000000000000000000000000000000..04f52d59710be77819066aea5c1cf4b0883f72d7 --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/libmp/libhyper.py @@ -0,0 +1,1150 @@ +""" +This module implements computation of hypergeometric and related +functions. In particular, it provides code for generic summation +of hypergeometric series. Optimized versions for various special +cases are also provided. +""" + +import operator +import math + +from .backend import MPZ_ZERO, MPZ_ONE, BACKEND, xrange, exec_ + +from .libintmath import gcd + +from .libmpf import (\ + ComplexResult, round_fast, round_nearest, + negative_rnd, bitcount, to_fixed, from_man_exp, from_int, to_int, + from_rational, + fzero, fone, fnone, ftwo, finf, fninf, fnan, + mpf_sign, mpf_add, mpf_abs, mpf_pos, + mpf_cmp, mpf_lt, mpf_le, mpf_gt, mpf_min_max, + mpf_perturb, mpf_neg, mpf_shift, mpf_sub, mpf_mul, mpf_div, + sqrt_fixed, mpf_sqrt, mpf_rdiv_int, mpf_pow_int, + to_rational, +) + +from .libelefun import (\ + mpf_pi, mpf_exp, mpf_log, pi_fixed, mpf_cos_sin, mpf_cos, mpf_sin, + mpf_sqrt, agm_fixed, +) + +from .libmpc import (\ + mpc_one, mpc_sub, mpc_mul_mpf, mpc_mul, mpc_neg, complex_int_pow, + mpc_div, mpc_add_mpf, mpc_sub_mpf, + mpc_log, mpc_add, mpc_pos, mpc_shift, + mpc_is_infnan, mpc_zero, mpc_sqrt, mpc_abs, + mpc_mpf_div, mpc_square, mpc_exp +) + +from .libintmath import ifac +from .gammazeta import mpf_gamma_int, mpf_euler, euler_fixed + +class NoConvergence(Exception): + pass + + +#-----------------------------------------------------------------------# +# # +# Generic hypergeometric series # +# # +#-----------------------------------------------------------------------# + +""" +TODO: + +1. proper mpq parsing +2. imaginary z special-cased (also: rational, integer?) +3. more clever handling of series that don't converge because of stupid + upwards rounding +4. checking for cancellation + +""" + +def make_hyp_summator(key): + """ + Returns a function that sums a generalized hypergeometric series, + for given parameter types (integer, rational, real, complex). + + """ + p, q, param_types, ztype = key + + pstring = "".join(param_types) + fname = "hypsum_%i_%i_%s_%s_%s" % (p, q, pstring[:p], pstring[p:], ztype) + #print "generating hypsum", fname + + have_complex_param = 'C' in param_types + have_complex_arg = ztype == 'C' + have_complex = have_complex_param or have_complex_arg + + source = [] + add = source.append + + aint = [] + arat = [] + bint = [] + brat = [] + areal = [] + breal = [] + acomplex = [] + bcomplex = [] + + #add("wp = prec + 40") + add("MAX = kwargs.get('maxterms', wp*100)") + add("HIGH = MPZ_ONE<= 0:") + add(" ZRE = xm << offset") + add("else:") + add(" ZRE = xm >> (-offset)") + if have_complex_arg: + add("offset = ye + wp") + add("if offset >= 0:") + add(" ZIM = ym << offset") + add("else:") + add(" ZIM = ym >> (-offset)") + + for i, flag in enumerate(param_types): + W = ["A", "B"][i >= p] + if flag == 'Z': + ([aint,bint][i >= p]).append(i) + add("%sINT_%i = coeffs[%i]" % (W, i, i)) + elif flag == 'Q': + ([arat,brat][i >= p]).append(i) + add("%sP_%i, %sQ_%i = coeffs[%i]._mpq_" % (W, i, W, i, i)) + elif flag == 'R': + ([areal,breal][i >= p]).append(i) + add("xsign, xm, xe, xbc = coeffs[%i]._mpf_" % i) + add("if xsign: xm = -xm") + add("offset = xe + wp") + add("if offset >= 0:") + add(" %sREAL_%i = xm << offset" % (W, i)) + add("else:") + add(" %sREAL_%i = xm >> (-offset)" % (W, i)) + elif flag == 'C': + ([acomplex,bcomplex][i >= p]).append(i) + add("__re, __im = coeffs[%i]._mpc_" % i) + add("xsign, xm, xe, xbc = __re") + add("if xsign: xm = -xm") + add("ysign, ym, ye, ybc = __im") + add("if ysign: ym = -ym") + + add("offset = xe + wp") + add("if offset >= 0:") + add(" %sCRE_%i = xm << offset" % (W, i)) + add("else:") + add(" %sCRE_%i = xm >> (-offset)" % (W, i)) + add("offset = ye + wp") + add("if offset >= 0:") + add(" %sCIM_%i = ym << offset" % (W, i)) + add("else:") + add(" %sCIM_%i = ym >> (-offset)" % (W, i)) + else: + raise ValueError + + l_areal = len(areal) + l_breal = len(breal) + cancellable_real = min(l_areal, l_breal) + noncancellable_real_num = areal[cancellable_real:] + noncancellable_real_den = breal[cancellable_real:] + + # LOOP + add("for n in xrange(1,10**8):") + + add(" if n in magnitude_check:") + add(" p_mag = bitcount(abs(PRE))") + if have_complex: + add(" p_mag = max(p_mag, bitcount(abs(PIM)))") + add(" magnitude_check[n] = wp-p_mag") + + # Real factors + multiplier = " * ".join(["AINT_#".replace("#", str(i)) for i in aint] + \ + ["AP_#".replace("#", str(i)) for i in arat] + \ + ["BQ_#".replace("#", str(i)) for i in brat]) + + divisor = " * ".join(["BINT_#".replace("#", str(i)) for i in bint] + \ + ["BP_#".replace("#", str(i)) for i in brat] + \ + ["AQ_#".replace("#", str(i)) for i in arat] + ["n"]) + + if multiplier: + add(" mul = " + multiplier) + add(" div = " + divisor) + + # Check for singular terms + add(" if not div:") + if multiplier: + add(" if not mul:") + add(" break") + add(" raise ZeroDivisionError") + + # Update product + if have_complex: + + # TODO: when there are several real parameters and just a few complex + # (maybe just the complex argument), we only need to do about + # half as many ops if we accumulate the real factor in a single real variable + for k in range(cancellable_real): add(" PRE = PRE * AREAL_%i // BREAL_%i" % (areal[k], breal[k])) + for i in noncancellable_real_num: add(" PRE = (PRE * AREAL_#) >> wp".replace("#", str(i))) + for i in noncancellable_real_den: add(" PRE = (PRE << wp) // BREAL_#".replace("#", str(i))) + for k in range(cancellable_real): add(" PIM = PIM * AREAL_%i // BREAL_%i" % (areal[k], breal[k])) + for i in noncancellable_real_num: add(" PIM = (PIM * AREAL_#) >> wp".replace("#", str(i))) + for i in noncancellable_real_den: add(" PIM = (PIM << wp) // BREAL_#".replace("#", str(i))) + + if multiplier: + if have_complex_arg: + add(" PRE, PIM = (mul*(PRE*ZRE-PIM*ZIM))//div, (mul*(PIM*ZRE+PRE*ZIM))//div") + add(" PRE >>= wp") + add(" PIM >>= wp") + else: + add(" PRE = ((mul * PRE * ZRE) >> wp) // div") + add(" PIM = ((mul * PIM * ZRE) >> wp) // div") + else: + if have_complex_arg: + add(" PRE, PIM = (PRE*ZRE-PIM*ZIM)//div, (PIM*ZRE+PRE*ZIM)//div") + add(" PRE >>= wp") + add(" PIM >>= wp") + else: + add(" PRE = ((PRE * ZRE) >> wp) // div") + add(" PIM = ((PIM * ZRE) >> wp) // div") + + for i in acomplex: + add(" PRE, PIM = PRE*ACRE_#-PIM*ACIM_#, PIM*ACRE_#+PRE*ACIM_#".replace("#", str(i))) + add(" PRE >>= wp") + add(" PIM >>= wp") + + for i in bcomplex: + add(" mag = BCRE_#*BCRE_#+BCIM_#*BCIM_#".replace("#", str(i))) + add(" re = PRE*BCRE_# + PIM*BCIM_#".replace("#", str(i))) + add(" im = PIM*BCRE_# - PRE*BCIM_#".replace("#", str(i))) + add(" PRE = (re << wp) // mag".replace("#", str(i))) + add(" PIM = (im << wp) // mag".replace("#", str(i))) + + else: + for k in range(cancellable_real): add(" PRE = PRE * AREAL_%i // BREAL_%i" % (areal[k], breal[k])) + for i in noncancellable_real_num: add(" PRE = (PRE * AREAL_#) >> wp".replace("#", str(i))) + for i in noncancellable_real_den: add(" PRE = (PRE << wp) // BREAL_#".replace("#", str(i))) + if multiplier: + add(" PRE = ((PRE * mul * ZRE) >> wp) // div") + else: + add(" PRE = ((PRE * ZRE) >> wp) // div") + + # Add product to sum + if have_complex: + add(" SRE += PRE") + add(" SIM += PIM") + add(" if (HIGH > PRE > LOW) and (HIGH > PIM > LOW):") + add(" break") + else: + add(" SRE += PRE") + add(" if HIGH > PRE > LOW:") + add(" break") + + #add(" from mpmath import nprint, log, ldexp") + #add(" nprint([n, log(abs(PRE),2), ldexp(PRE,-wp)])") + + add(" if n > MAX:") + add(" raise NoConvergence('Hypergeometric series converges too slowly. Try increasing maxterms.')") + + # +1 all parameters for next loop + for i in aint: add(" AINT_# += 1".replace("#", str(i))) + for i in bint: add(" BINT_# += 1".replace("#", str(i))) + for i in arat: add(" AP_# += AQ_#".replace("#", str(i))) + for i in brat: add(" BP_# += BQ_#".replace("#", str(i))) + for i in areal: add(" AREAL_# += one".replace("#", str(i))) + for i in breal: add(" BREAL_# += one".replace("#", str(i))) + for i in acomplex: add(" ACRE_# += one".replace("#", str(i))) + for i in bcomplex: add(" BCRE_# += one".replace("#", str(i))) + + if have_complex: + add("a = from_man_exp(SRE, -wp, prec, 'n')") + add("b = from_man_exp(SIM, -wp, prec, 'n')") + + add("if SRE:") + add(" if SIM:") + add(" magn = max(a[2]+a[3], b[2]+b[3])") + add(" else:") + add(" magn = a[2]+a[3]") + add("elif SIM:") + add(" magn = b[2]+b[3]") + add("else:") + add(" magn = -wp+1") + + add("return (a, b), True, magn") + else: + add("a = from_man_exp(SRE, -wp, prec, 'n')") + + add("if SRE:") + add(" magn = a[2]+a[3]") + add("else:") + add(" magn = -wp+1") + + add("return a, False, magn") + + source = "\n".join((" " + line) for line in source) + source = ("def %s(coeffs, z, prec, wp, epsshift, magnitude_check, **kwargs):\n" % fname) + source + + namespace = {} + + exec_(source, globals(), namespace) + + #print source + return source, namespace[fname] + + +if BACKEND == 'sage': + + def make_hyp_summator(key): + """ + Returns a function that sums a generalized hypergeometric series, + for given parameter types (integer, rational, real, complex). + """ + from sage.libs.mpmath.ext_main import hypsum_internal + p, q, param_types, ztype = key + def _hypsum(coeffs, z, prec, wp, epsshift, magnitude_check, **kwargs): + return hypsum_internal(p, q, param_types, ztype, coeffs, z, + prec, wp, epsshift, magnitude_check, kwargs) + + return "(none)", _hypsum + + +#-----------------------------------------------------------------------# +# # +# Error functions # +# # +#-----------------------------------------------------------------------# + +# TODO: mpf_erf should call mpf_erfc when appropriate (currently +# only the converse delegation is implemented) + +def mpf_erf(x, prec, rnd=round_fast): + sign, man, exp, bc = x + if not man: + if x == fzero: return fzero + if x == finf: return fone + if x== fninf: return fnone + return fnan + size = exp + bc + lg = math.log + # The approximation erf(x) = 1 is accurate to > x^2 * log(e,2) bits + if size > 3 and 2*(size-1) + 0.528766 > lg(prec,2): + if sign: + return mpf_perturb(fnone, 0, prec, rnd) + else: + return mpf_perturb(fone, 1, prec, rnd) + # erf(x) ~ 2*x/sqrt(pi) close to 0 + if size < -prec: + # 2*x + x = mpf_shift(x,1) + c = mpf_sqrt(mpf_pi(prec+20), prec+20) + # TODO: interval rounding + return mpf_div(x, c, prec, rnd) + wp = prec + abs(size) + 25 + # Taylor series for erf, fixed-point summation + t = abs(to_fixed(x, wp)) + t2 = (t*t) >> wp + s, term, k = t, 12345, 1 + while term: + t = ((t * t2) >> wp) // k + term = t // (2*k+1) + if k & 1: + s -= term + else: + s += term + k += 1 + s = (s << (wp+1)) // sqrt_fixed(pi_fixed(wp), wp) + if sign: + s = -s + return from_man_exp(s, -wp, prec, rnd) + +# If possible, we use the asymptotic series for erfc. +# This is an alternating divergent asymptotic series, so +# the error is at most equal to the first omitted term. +# Here we check if the smallest term is small enough +# for a given x and precision +def erfc_check_series(x, prec): + n = to_int(x) + if n**2 * 1.44 > prec: + return True + return False + +def mpf_erfc(x, prec, rnd=round_fast): + sign, man, exp, bc = x + if not man: + if x == fzero: return fone + if x == finf: return fzero + if x == fninf: return ftwo + return fnan + wp = prec + 20 + mag = bc+exp + # Preserve full accuracy when exponent grows huge + wp += max(0, 2*mag) + regular_erf = sign or mag < 2 + if regular_erf or not erfc_check_series(x, wp): + if regular_erf: + return mpf_sub(fone, mpf_erf(x, prec+10, negative_rnd[rnd]), prec, rnd) + # 1-erf(x) ~ exp(-x^2), increase prec to deal with cancellation + n = to_int(x)+1 + return mpf_sub(fone, mpf_erf(x, prec + int(n**2*1.44) + 10), prec, rnd) + s = term = MPZ_ONE << wp + term_prev = 0 + t = (2 * to_fixed(x, wp) ** 2) >> wp + k = 1 + while 1: + term = ((term * (2*k - 1)) << wp) // t + if k > 4 and term > term_prev or not term: + break + if k & 1: + s -= term + else: + s += term + term_prev = term + #print k, to_str(from_man_exp(term, -wp, 50), 10) + k += 1 + s = (s << wp) // sqrt_fixed(pi_fixed(wp), wp) + s = from_man_exp(s, -wp, wp) + z = mpf_exp(mpf_neg(mpf_mul(x,x,wp),wp),wp) + y = mpf_div(mpf_mul(z, s, wp), x, prec, rnd) + return y + + +#-----------------------------------------------------------------------# +# # +# Exponential integrals # +# # +#-----------------------------------------------------------------------# + +def ei_taylor(x, prec): + s = t = x + k = 2 + while t: + t = ((t*x) >> prec) // k + s += t // k + k += 1 + return s + +def complex_ei_taylor(zre, zim, prec): + _abs = abs + sre = tre = zre + sim = tim = zim + k = 2 + while _abs(tre) + _abs(tim) > 5: + tre, tim = ((tre*zre-tim*zim)//k)>>prec, ((tre*zim+tim*zre)//k)>>prec + sre += tre // k + sim += tim // k + k += 1 + return sre, sim + +def ei_asymptotic(x, prec): + one = MPZ_ONE << prec + x = t = ((one << prec) // x) + s = one + x + k = 2 + while t: + t = (k*t*x) >> prec + s += t + k += 1 + return s + +def complex_ei_asymptotic(zre, zim, prec): + _abs = abs + one = MPZ_ONE << prec + M = (zim*zim + zre*zre) >> prec + # 1 / z + xre = tre = (zre << prec) // M + xim = tim = ((-zim) << prec) // M + sre = one + xre + sim = xim + k = 2 + while _abs(tre) + _abs(tim) > 1000: + #print tre, tim + tre, tim = ((tre*xre-tim*xim)*k)>>prec, ((tre*xim+tim*xre)*k)>>prec + sre += tre + sim += tim + k += 1 + if k > prec: + raise NoConvergence + return sre, sim + +def mpf_ei(x, prec, rnd=round_fast, e1=False): + if e1: + x = mpf_neg(x) + sign, man, exp, bc = x + if e1 and not sign: + if x == fzero: + return finf + raise ComplexResult("E1(x) for x < 0") + if man: + xabs = 0, man, exp, bc + xmag = exp+bc + wp = prec + 20 + can_use_asymp = xmag > wp + if not can_use_asymp: + if exp >= 0: + xabsint = man << exp + else: + xabsint = man >> (-exp) + can_use_asymp = xabsint > int(wp*0.693) + 10 + if can_use_asymp: + if xmag > wp: + v = fone + else: + v = from_man_exp(ei_asymptotic(to_fixed(x, wp), wp), -wp) + v = mpf_mul(v, mpf_exp(x, wp), wp) + v = mpf_div(v, x, prec, rnd) + else: + wp += 2*int(to_int(xabs)) + u = to_fixed(x, wp) + v = ei_taylor(u, wp) + euler_fixed(wp) + t1 = from_man_exp(v,-wp) + t2 = mpf_log(xabs,wp) + v = mpf_add(t1, t2, prec, rnd) + else: + if x == fzero: v = fninf + elif x == finf: v = finf + elif x == fninf: v = fzero + else: v = fnan + if e1: + v = mpf_neg(v) + return v + +def mpc_ei(z, prec, rnd=round_fast, e1=False): + if e1: + z = mpc_neg(z) + a, b = z + asign, aman, aexp, abc = a + bsign, bman, bexp, bbc = b + if b == fzero: + if e1: + x = mpf_neg(mpf_ei(a, prec, rnd)) + if not asign: + y = mpf_neg(mpf_pi(prec, rnd)) + else: + y = fzero + return x, y + else: + return mpf_ei(a, prec, rnd), fzero + if a != fzero: + if not aman or not bman: + return (fnan, fnan) + wp = prec + 40 + amag = aexp+abc + bmag = bexp+bbc + zmag = max(amag, bmag) + can_use_asymp = zmag > wp + if not can_use_asymp: + zabsint = abs(to_int(a)) + abs(to_int(b)) + can_use_asymp = zabsint > int(wp*0.693) + 20 + try: + if can_use_asymp: + if zmag > wp: + v = fone, fzero + else: + zre = to_fixed(a, wp) + zim = to_fixed(b, wp) + vre, vim = complex_ei_asymptotic(zre, zim, wp) + v = from_man_exp(vre, -wp), from_man_exp(vim, -wp) + v = mpc_mul(v, mpc_exp(z, wp), wp) + v = mpc_div(v, z, wp) + if e1: + v = mpc_neg(v, prec, rnd) + else: + x, y = v + if bsign: + v = mpf_pos(x, prec, rnd), mpf_sub(y, mpf_pi(wp), prec, rnd) + else: + v = mpf_pos(x, prec, rnd), mpf_add(y, mpf_pi(wp), prec, rnd) + return v + except NoConvergence: + pass + #wp += 2*max(0,zmag) + wp += 2*int(to_int(mpc_abs(z, 5))) + zre = to_fixed(a, wp) + zim = to_fixed(b, wp) + vre, vim = complex_ei_taylor(zre, zim, wp) + vre += euler_fixed(wp) + v = from_man_exp(vre,-wp), from_man_exp(vim,-wp) + if e1: + u = mpc_log(mpc_neg(z),wp) + else: + u = mpc_log(z,wp) + v = mpc_add(v, u, prec, rnd) + if e1: + v = mpc_neg(v) + return v + +def mpf_e1(x, prec, rnd=round_fast): + return mpf_ei(x, prec, rnd, True) + +def mpc_e1(x, prec, rnd=round_fast): + return mpc_ei(x, prec, rnd, True) + +def mpf_expint(n, x, prec, rnd=round_fast, gamma=False): + """ + E_n(x), n an integer, x real + + With gamma=True, computes Gamma(n,x) (upper incomplete gamma function) + + Returns (real, None) if real, otherwise (real, imag) + The imaginary part is an optional branch cut term + + """ + sign, man, exp, bc = x + if not man: + if gamma: + if x == fzero: + # Actually gamma function pole + if n <= 0: + return finf, None + return mpf_gamma_int(n, prec, rnd), None + if x == finf: + return fzero, None + # TODO: could return finite imaginary value at -inf + return fnan, fnan + else: + if x == fzero: + if n > 1: + return from_rational(1, n-1, prec, rnd), None + else: + return finf, None + if x == finf: + return fzero, None + return fnan, fnan + n_orig = n + if gamma: + n = 1-n + wp = prec + 20 + xmag = exp + bc + # Beware of near-poles + if xmag < -10: + raise NotImplementedError + nmag = bitcount(abs(n)) + have_imag = n > 0 and sign + negx = mpf_neg(x) + # Skip series if direct convergence + if n == 0 or 2*nmag - xmag < -wp: + if gamma: + v = mpf_exp(negx, wp) + re = mpf_mul(v, mpf_pow_int(x, n_orig-1, wp), prec, rnd) + else: + v = mpf_exp(negx, wp) + re = mpf_div(v, x, prec, rnd) + else: + # Finite number of terms, or... + can_use_asymptotic_series = -3*wp < n <= 0 + # ...large enough? + if not can_use_asymptotic_series: + xi = abs(to_int(x)) + m = min(max(1, xi-n), 2*wp) + siz = -n*nmag + (m+n)*bitcount(abs(m+n)) - m*xmag - (144*m//100) + tol = -wp-10 + can_use_asymptotic_series = siz < tol + if can_use_asymptotic_series: + r = ((-MPZ_ONE) << (wp+wp)) // to_fixed(x, wp) + m = n + t = r*m + s = MPZ_ONE << wp + while m and t: + s += t + m += 1 + t = (m*r*t) >> wp + v = mpf_exp(negx, wp) + if gamma: + # ~ exp(-x) * x^(n-1) * (1 + ...) + v = mpf_mul(v, mpf_pow_int(x, n_orig-1, wp), wp) + else: + # ~ exp(-x)/x * (1 + ...) + v = mpf_div(v, x, wp) + re = mpf_mul(v, from_man_exp(s, -wp), prec, rnd) + elif n == 1: + re = mpf_neg(mpf_ei(negx, prec, rnd)) + elif n > 0 and n < 3*wp: + T1 = mpf_neg(mpf_ei(negx, wp)) + if gamma: + if n_orig & 1: + T1 = mpf_neg(T1) + else: + T1 = mpf_mul(T1, mpf_pow_int(negx, n-1, wp), wp) + r = t = to_fixed(x, wp) + facs = [1] * (n-1) + for k in range(1,n-1): + facs[k] = facs[k-1] * k + facs = facs[::-1] + s = facs[0] << wp + for k in range(1, n-1): + if k & 1: + s -= facs[k] * t + else: + s += facs[k] * t + t = (t*r) >> wp + T2 = from_man_exp(s, -wp, wp) + T2 = mpf_mul(T2, mpf_exp(negx, wp)) + if gamma: + T2 = mpf_mul(T2, mpf_pow_int(x, n_orig, wp), wp) + R = mpf_add(T1, T2) + re = mpf_div(R, from_int(ifac(n-1)), prec, rnd) + else: + raise NotImplementedError + if have_imag: + M = from_int(-ifac(n-1)) + if gamma: + im = mpf_div(mpf_pi(wp), M, prec, rnd) + if n_orig & 1: + im = mpf_neg(im) + else: + im = mpf_div(mpf_mul(mpf_pi(wp), mpf_pow_int(negx, n_orig-1, wp), wp), M, prec, rnd) + return re, im + else: + return re, None + +def mpf_ci_si_taylor(x, wp, which=0): + """ + 0 - Ci(x) - (euler+log(x)) + 1 - Si(x) + """ + x = to_fixed(x, wp) + x2 = -(x*x) >> wp + if which == 0: + s, t, k = 0, (MPZ_ONE<>wp + s += t//k + k += 2 + return from_man_exp(s, -wp) + +def mpc_ci_si_taylor(re, im, wp, which=0): + # The following code is only designed for small arguments, + # and not too small arguments (for relative accuracy) + if re[1]: + mag = re[2]+re[3] + elif im[1]: + mag = im[2]+im[3] + if im[1]: + mag = max(mag, im[2]+im[3]) + if mag > 2 or mag < -wp: + raise NotImplementedError + wp += (2-mag) + zre = to_fixed(re, wp) + zim = to_fixed(im, wp) + z2re = (zim*zim-zre*zre)>>wp + z2im = (-2*zre*zim)>>wp + tre = zre + tim = zim + one = MPZ_ONE< 2: + f = k*(k-1) + tre, tim = ((tre*z2re-tim*z2im)//f)>>wp, ((tre*z2im+tim*z2re)//f)>>wp + sre += tre//k + sim += tim//k + k += 2 + return from_man_exp(sre, -wp), from_man_exp(sim, -wp) + +def mpf_ci_si(x, prec, rnd=round_fast, which=2): + """ + Calculation of Ci(x), Si(x) for real x. + + which = 0 -- returns (Ci(x), -) + which = 1 -- returns (Si(x), -) + which = 2 -- returns (Ci(x), Si(x)) + + Note: if x < 0, Ci(x) needs an additional imaginary term, pi*i. + """ + wp = prec + 20 + sign, man, exp, bc = x + ci, si = None, None + if not man: + if x == fzero: + return (fninf, fzero) + if x == fnan: + return (x, x) + ci = fzero + if which != 0: + if x == finf: + si = mpf_shift(mpf_pi(prec, rnd), -1) + if x == fninf: + si = mpf_neg(mpf_shift(mpf_pi(prec, negative_rnd[rnd]), -1)) + return (ci, si) + # For small x: Ci(x) ~ euler + log(x), Si(x) ~ x + mag = exp+bc + if mag < -wp: + if which != 0: + si = mpf_perturb(x, 1-sign, prec, rnd) + if which != 1: + y = mpf_euler(wp) + xabs = mpf_abs(x) + ci = mpf_add(y, mpf_log(xabs, wp), prec, rnd) + return ci, si + # For huge x: Ci(x) ~ sin(x)/x, Si(x) ~ pi/2 + elif mag > wp: + if which != 0: + if sign: + si = mpf_neg(mpf_pi(prec, negative_rnd[rnd])) + else: + si = mpf_pi(prec, rnd) + si = mpf_shift(si, -1) + if which != 1: + ci = mpf_div(mpf_sin(x, wp), x, prec, rnd) + return ci, si + else: + wp += abs(mag) + # Use an asymptotic series? The smallest value of n!/x^n + # occurs for n ~ x, where the magnitude is ~ exp(-x). + asymptotic = mag-1 > math.log(wp, 2) + # Case 1: convergent series near 0 + if not asymptotic: + if which != 0: + si = mpf_pos(mpf_ci_si_taylor(x, wp, 1), prec, rnd) + if which != 1: + ci = mpf_ci_si_taylor(x, wp, 0) + ci = mpf_add(ci, mpf_euler(wp), wp) + ci = mpf_add(ci, mpf_log(mpf_abs(x), wp), prec, rnd) + return ci, si + x = mpf_abs(x) + # Case 2: asymptotic series for x >> 1 + xf = to_fixed(x, wp) + xr = (MPZ_ONE<<(2*wp)) // xf # 1/x + s1 = (MPZ_ONE << wp) + s2 = xr + t = xr + k = 2 + while t: + t = -t + t = (t*xr*k)>>wp + k += 1 + s1 += t + t = (t*xr*k)>>wp + k += 1 + s2 += t + s1 = from_man_exp(s1, -wp) + s2 = from_man_exp(s2, -wp) + s1 = mpf_div(s1, x, wp) + s2 = mpf_div(s2, x, wp) + cos, sin = mpf_cos_sin(x, wp) + # Ci(x) = sin(x)*s1-cos(x)*s2 + # Si(x) = pi/2-cos(x)*s1-sin(x)*s2 + if which != 0: + si = mpf_add(mpf_mul(cos, s1), mpf_mul(sin, s2), wp) + si = mpf_sub(mpf_shift(mpf_pi(wp), -1), si, wp) + if sign: + si = mpf_neg(si) + si = mpf_pos(si, prec, rnd) + if which != 1: + ci = mpf_sub(mpf_mul(sin, s1), mpf_mul(cos, s2), prec, rnd) + return ci, si + +def mpf_ci(x, prec, rnd=round_fast): + if mpf_sign(x) < 0: + raise ComplexResult + return mpf_ci_si(x, prec, rnd, 0)[0] + +def mpf_si(x, prec, rnd=round_fast): + return mpf_ci_si(x, prec, rnd, 1)[1] + +def mpc_ci(z, prec, rnd=round_fast): + re, im = z + if im == fzero: + ci = mpf_ci_si(re, prec, rnd, 0)[0] + if mpf_sign(re) < 0: + return (ci, mpf_pi(prec, rnd)) + return (ci, fzero) + wp = prec + 20 + cre, cim = mpc_ci_si_taylor(re, im, wp, 0) + cre = mpf_add(cre, mpf_euler(wp), wp) + ci = mpc_add((cre, cim), mpc_log(z, wp), prec, rnd) + return ci + +def mpc_si(z, prec, rnd=round_fast): + re, im = z + if im == fzero: + return (mpf_ci_si(re, prec, rnd, 1)[1], fzero) + wp = prec + 20 + z = mpc_ci_si_taylor(re, im, wp, 1) + return mpc_pos(z, prec, rnd) + + +#-----------------------------------------------------------------------# +# # +# Bessel functions # +# # +#-----------------------------------------------------------------------# + +# A Bessel function of the first kind of integer order, J_n(x), is +# given by the power series + +# oo +# ___ k 2 k + n +# \ (-1) / x \ +# J_n(x) = ) ----------- | - | +# /___ k! (k + n)! \ 2 / +# k = 0 + +# Simplifying the quotient between two successive terms gives the +# ratio x^2 / (-4*k*(k+n)). Hence, we only need one full-precision +# multiplication and one division by a small integer per term. +# The complex version is very similar, the only difference being +# that the multiplication is actually 4 multiplies. + +# In the general case, we have +# J_v(x) = (x/2)**v / v! * 0F1(v+1, (-1/4)*z**2) + +# TODO: for extremely large x, we could use an asymptotic +# trigonometric approximation. + +# TODO: recompute at higher precision if the fixed-point mantissa +# is very small + +def mpf_besseljn(n, x, prec, rounding=round_fast): + prec += 50 + negate = n < 0 and n & 1 + mag = x[2]+x[3] + n = abs(n) + wp = prec + 20 + n*bitcount(n) + if mag < 0: + wp -= n * mag + x = to_fixed(x, wp) + x2 = (x**2) >> wp + if not n: + s = t = MPZ_ONE << wp + else: + s = t = (x**n // ifac(n)) >> ((n-1)*wp + n) + k = 1 + while t: + t = ((t * x2) // (-4*k*(k+n))) >> wp + s += t + k += 1 + if negate: + s = -s + return from_man_exp(s, -wp, prec, rounding) + +def mpc_besseljn(n, z, prec, rounding=round_fast): + negate = n < 0 and n & 1 + n = abs(n) + origprec = prec + zre, zim = z + mag = max(zre[2]+zre[3], zim[2]+zim[3]) + prec += 20 + n*bitcount(n) + abs(mag) + if mag < 0: + prec -= n * mag + zre = to_fixed(zre, prec) + zim = to_fixed(zim, prec) + z2re = (zre**2 - zim**2) >> prec + z2im = (zre*zim) >> (prec-1) + if not n: + sre = tre = MPZ_ONE << prec + sim = tim = MPZ_ZERO + else: + re, im = complex_int_pow(zre, zim, n) + sre = tre = (re // ifac(n)) >> ((n-1)*prec + n) + sim = tim = (im // ifac(n)) >> ((n-1)*prec + n) + k = 1 + while abs(tre) + abs(tim) > 3: + p = -4*k*(k+n) + tre, tim = tre*z2re - tim*z2im, tim*z2re + tre*z2im + tre = (tre // p) >> prec + tim = (tim // p) >> prec + sre += tre + sim += tim + k += 1 + if negate: + sre = -sre + sim = -sim + re = from_man_exp(sre, -prec, origprec, rounding) + im = from_man_exp(sim, -prec, origprec, rounding) + return (re, im) + +def mpf_agm(a, b, prec, rnd=round_fast): + """ + Computes the arithmetic-geometric mean agm(a,b) for + nonnegative mpf values a, b. + """ + asign, aman, aexp, abc = a + bsign, bman, bexp, bbc = b + if asign or bsign: + raise ComplexResult("agm of a negative number") + # Handle inf, nan or zero in either operand + if not (aman and bman): + if a == fnan or b == fnan: + return fnan + if a == finf: + if b == fzero: + return fnan + return finf + if b == finf: + if a == fzero: + return fnan + return finf + # agm(0,x) = agm(x,0) = 0 + return fzero + wp = prec + 20 + amag = aexp+abc + bmag = bexp+bbc + mag_delta = amag - bmag + # Reduce to roughly the same magnitude using floating-point AGM + abs_mag_delta = abs(mag_delta) + if abs_mag_delta > 10: + while abs_mag_delta > 10: + a, b = mpf_shift(mpf_add(a,b,wp),-1), \ + mpf_sqrt(mpf_mul(a,b,wp),wp) + abs_mag_delta //= 2 + asign, aman, aexp, abc = a + bsign, bman, bexp, bbc = b + amag = aexp+abc + bmag = bexp+bbc + mag_delta = amag - bmag + #print to_float(a), to_float(b) + # Use agm(a,b) = agm(x*a,x*b)/x to obtain a, b ~= 1 + min_mag = min(amag,bmag) + max_mag = max(amag,bmag) + n = 0 + # If too small, we lose precision when going to fixed-point + if min_mag < -8: + n = -min_mag + # If too large, we waste time using fixed-point with large numbers + elif max_mag > 20: + n = -max_mag + if n: + a = mpf_shift(a, n) + b = mpf_shift(b, n) + #print to_float(a), to_float(b) + af = to_fixed(a, wp) + bf = to_fixed(b, wp) + g = agm_fixed(af, bf, wp) + return from_man_exp(g, -wp-n, prec, rnd) + +def mpf_agm1(a, prec, rnd=round_fast): + """ + Computes the arithmetic-geometric mean agm(1,a) for a nonnegative + mpf value a. + """ + return mpf_agm(fone, a, prec, rnd) + +def mpc_agm(a, b, prec, rnd=round_fast): + """ + Complex AGM. + + TODO: + * check that convergence works as intended + * optimize + * select a nonarbitrary branch + """ + if mpc_is_infnan(a) or mpc_is_infnan(b): + return fnan, fnan + if mpc_zero in (a, b): + return fzero, fzero + if mpc_neg(a) == b: + return fzero, fzero + wp = prec+20 + eps = mpf_shift(fone, -wp+10) + while 1: + a1 = mpc_shift(mpc_add(a, b, wp), -1) + b1 = mpc_sqrt(mpc_mul(a, b, wp), wp) + a, b = a1, b1 + size = mpf_min_max([mpc_abs(a,10), mpc_abs(b,10)])[1] + err = mpc_abs(mpc_sub(a, b, 10), 10) + if size == fzero or mpf_lt(err, mpf_mul(eps, size)): + return a + +def mpc_agm1(a, prec, rnd=round_fast): + return mpc_agm(mpc_one, a, prec, rnd) + +def mpf_ellipk(x, prec, rnd=round_fast): + if not x[1]: + if x == fzero: + return mpf_shift(mpf_pi(prec, rnd), -1) + if x == fninf: + return fzero + if x == fnan: + return x + if x == fone: + return finf + # TODO: for |x| << 1/2, one could use fall back to + # pi/2 * hyp2f1_rat((1,2),(1,2),(1,1), x) + wp = prec + 15 + # Use K(x) = pi/2/agm(1,a) where a = sqrt(1-x) + # The sqrt raises ComplexResult if x > 0 + a = mpf_sqrt(mpf_sub(fone, x, wp), wp) + v = mpf_agm1(a, wp) + r = mpf_div(mpf_pi(wp), v, prec, rnd) + return mpf_shift(r, -1) + +def mpc_ellipk(z, prec, rnd=round_fast): + re, im = z + if im == fzero: + if re == finf: + return mpc_zero + if mpf_le(re, fone): + return mpf_ellipk(re, prec, rnd), fzero + wp = prec + 15 + a = mpc_sqrt(mpc_sub(mpc_one, z, wp), wp) + v = mpc_agm1(a, wp) + r = mpc_mpf_div(mpf_pi(wp), v, prec, rnd) + return mpc_shift(r, -1) + +def mpf_ellipe(x, prec, rnd=round_fast): + # http://functions.wolfram.com/EllipticIntegrals/ + # EllipticK/20/01/0001/ + # E = (1-m)*(K'(m)*2*m + K(m)) + sign, man, exp, bc = x + if not man: + if x == fzero: + return mpf_shift(mpf_pi(prec, rnd), -1) + if x == fninf: + return finf + if x == fnan: + return x + if x == finf: + raise ComplexResult + if x == fone: + return fone + wp = prec+20 + mag = exp+bc + if mag < -wp: + return mpf_shift(mpf_pi(prec, rnd), -1) + # Compute a finite difference for K' + p = max(mag, 0) - wp + h = mpf_shift(fone, p) + K = mpf_ellipk(x, 2*wp) + Kh = mpf_ellipk(mpf_sub(x, h), 2*wp) + Kdiff = mpf_shift(mpf_sub(K, Kh), -p) + t = mpf_sub(fone, x) + b = mpf_mul(Kdiff, mpf_shift(x,1), wp) + return mpf_mul(t, mpf_add(K, b), prec, rnd) + +def mpc_ellipe(z, prec, rnd=round_fast): + re, im = z + if im == fzero: + if re == finf: + return (fzero, finf) + if mpf_le(re, fone): + return mpf_ellipe(re, prec, rnd), fzero + wp = prec + 15 + mag = mpc_abs(z, 1) + p = max(mag[2]+mag[3], 0) - wp + h = mpf_shift(fone, p) + K = mpc_ellipk(z, 2*wp) + Kh = mpc_ellipk(mpc_add_mpf(z, h, 2*wp), 2*wp) + Kdiff = mpc_shift(mpc_sub(Kh, K, wp), -p) + t = mpc_sub(mpc_one, z, wp) + b = mpc_mul(Kdiff, mpc_shift(z,1), wp) + return mpc_mul(t, mpc_add(K, b, wp), prec, rnd) diff --git a/lib/python3.12/site-packages/mpmath/libmp/libintmath.py b/lib/python3.12/site-packages/mpmath/libmp/libintmath.py new file mode 100644 index 0000000000000000000000000000000000000000..7880546e135639208d136488408b102ad41682a2 --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/libmp/libintmath.py @@ -0,0 +1,584 @@ +""" +Utility functions for integer math. + +TODO: rename, cleanup, perhaps move the gmpy wrapper code +here from settings.py + +""" + +import math +from bisect import bisect + +from .backend import xrange +from .backend import BACKEND, gmpy, sage, sage_utils, MPZ, MPZ_ONE, MPZ_ZERO + +small_trailing = [0] * 256 +for j in range(1,8): + small_trailing[1<>> giant_steps(50,1000) + [66, 128, 253, 502, 1000] + >>> giant_steps(50,1000,4) + [65, 252, 1000] + + """ + L = [target] + while L[-1] > start*n: + L = L + [L[-1]//n + 2] + return L[::-1] + +def rshift(x, n): + """For an integer x, calculate x >> n with the fastest (floor) + rounding. Unlike the plain Python expression (x >> n), n is + allowed to be negative, in which case a left shift is performed.""" + if n >= 0: return x >> n + else: return x << (-n) + +def lshift(x, n): + """For an integer x, calculate x << n. Unlike the plain Python + expression (x << n), n is allowed to be negative, in which case a + right shift with default (floor) rounding is performed.""" + if n >= 0: return x << n + else: return x >> (-n) + +if BACKEND == 'sage': + import operator + rshift = operator.rshift + lshift = operator.lshift + +def python_trailing(n): + """Count the number of trailing zero bits in abs(n).""" + if not n: + return 0 + low_byte = n & 0xff + if low_byte: + return small_trailing[low_byte] + t = 8 + n >>= 8 + while not n & 0xff: + n >>= 8 + t += 8 + return t + small_trailing[n & 0xff] + +if BACKEND == 'gmpy': + if gmpy.version() >= '2': + def gmpy_trailing(n): + """Count the number of trailing zero bits in abs(n) using gmpy.""" + if n: return MPZ(n).bit_scan1() + else: return 0 + else: + def gmpy_trailing(n): + """Count the number of trailing zero bits in abs(n) using gmpy.""" + if n: return MPZ(n).scan1() + else: return 0 + +# Small powers of 2 +powers = [1<<_ for _ in range(300)] + +def python_bitcount(n): + """Calculate bit size of the nonnegative integer n.""" + bc = bisect(powers, n) + if bc != 300: + return bc + bc = int(math.log(n, 2)) - 4 + return bc + bctable[n>>bc] + +def gmpy_bitcount(n): + """Calculate bit size of the nonnegative integer n.""" + if n: return MPZ(n).numdigits(2) + else: return 0 + +#def sage_bitcount(n): +# if n: return MPZ(n).nbits() +# else: return 0 + +def sage_trailing(n): + return MPZ(n).trailing_zero_bits() + +if BACKEND == 'gmpy': + bitcount = gmpy_bitcount + trailing = gmpy_trailing +elif BACKEND == 'sage': + sage_bitcount = sage_utils.bitcount + bitcount = sage_bitcount + trailing = sage_trailing +else: + bitcount = python_bitcount + trailing = python_trailing + +if BACKEND == 'gmpy' and 'bit_length' in dir(gmpy): + bitcount = gmpy.bit_length + +# Used to avoid slow function calls as far as possible +trailtable = [trailing(n) for n in range(256)] +bctable = [bitcount(n) for n in range(1024)] + +# TODO: speed up for bases 2, 4, 8, 16, ... + +def bin_to_radix(x, xbits, base, bdigits): + """Changes radix of a fixed-point number; i.e., converts + x * 2**xbits to floor(x * 10**bdigits).""" + return x * (MPZ(base)**bdigits) >> xbits + +stddigits = '0123456789abcdefghijklmnopqrstuvwxyz' + +def small_numeral(n, base=10, digits=stddigits): + """Return the string numeral of a positive integer in an arbitrary + base. Most efficient for small input.""" + if base == 10: + return str(n) + digs = [] + while n: + n, digit = divmod(n, base) + digs.append(digits[digit]) + return "".join(digs[::-1]) + +def numeral_python(n, base=10, size=0, digits=stddigits): + """Represent the integer n as a string of digits in the given base. + Recursive division is used to make this function about 3x faster + than Python's str() for converting integers to decimal strings. + + The 'size' parameters specifies the number of digits in n; this + number is only used to determine splitting points and need not be + exact.""" + if n <= 0: + if not n: + return "0" + return "-" + numeral(-n, base, size, digits) + # Fast enough to do directly + if size < 250: + return small_numeral(n, base, digits) + # Divide in half + half = (size // 2) + (size & 1) + A, B = divmod(n, base**half) + ad = numeral(A, base, half, digits) + bd = numeral(B, base, half, digits).rjust(half, "0") + return ad + bd + +def numeral_gmpy(n, base=10, size=0, digits=stddigits): + """Represent the integer n as a string of digits in the given base. + Recursive division is used to make this function about 3x faster + than Python's str() for converting integers to decimal strings. + + The 'size' parameters specifies the number of digits in n; this + number is only used to determine splitting points and need not be + exact.""" + if n < 0: + return "-" + numeral(-n, base, size, digits) + # gmpy.digits() may cause a segmentation fault when trying to convert + # extremely large values to a string. The size limit may need to be + # adjusted on some platforms, but 1500000 works on Windows and Linux. + if size < 1500000: + return gmpy.digits(n, base) + # Divide in half + half = (size // 2) + (size & 1) + A, B = divmod(n, MPZ(base)**half) + ad = numeral(A, base, half, digits) + bd = numeral(B, base, half, digits).rjust(half, "0") + return ad + bd + +if BACKEND == "gmpy": + numeral = numeral_gmpy +else: + numeral = numeral_python + +_1_800 = 1<<800 +_1_600 = 1<<600 +_1_400 = 1<<400 +_1_200 = 1<<200 +_1_100 = 1<<100 +_1_50 = 1<<50 + +def isqrt_small_python(x): + """ + Correctly (floor) rounded integer square root, using + division. Fast up to ~200 digits. + """ + if not x: + return x + if x < _1_800: + # Exact with IEEE double precision arithmetic + if x < _1_50: + return int(x**0.5) + # Initial estimate can be any integer >= the true root; round up + r = int(x**0.5 * 1.00000000000001) + 1 + else: + bc = bitcount(x) + n = bc//2 + r = int((x>>(2*n-100))**0.5+2)<<(n-50) # +2 is to round up + # The following iteration now precisely computes floor(sqrt(x)) + # See e.g. Crandall & Pomerance, "Prime Numbers: A Computational + # Perspective" + while 1: + y = (r+x//r)>>1 + if y >= r: + return r + r = y + +def isqrt_fast_python(x): + """ + Fast approximate integer square root, computed using division-free + Newton iteration for large x. For random integers the result is almost + always correct (floor(sqrt(x))), but is 1 ulp too small with a roughly + 0.1% probability. If x is very close to an exact square, the answer is + 1 ulp wrong with high probability. + + With 0 guard bits, the largest error over a set of 10^5 random + inputs of size 1-10^5 bits was 3 ulp. The use of 10 guard bits + almost certainly guarantees a max 1 ulp error. + """ + # Use direct division-based iteration if sqrt(x) < 2^400 + # Assume floating-point square root accurate to within 1 ulp, then: + # 0 Newton iterations good to 52 bits + # 1 Newton iterations good to 104 bits + # 2 Newton iterations good to 208 bits + # 3 Newton iterations good to 416 bits + if x < _1_800: + y = int(x**0.5) + if x >= _1_100: + y = (y + x//y) >> 1 + if x >= _1_200: + y = (y + x//y) >> 1 + if x >= _1_400: + y = (y + x//y) >> 1 + return y + bc = bitcount(x) + guard_bits = 10 + x <<= 2*guard_bits + bc += 2*guard_bits + bc += (bc&1) + hbc = bc//2 + startprec = min(50, hbc) + # Newton iteration for 1/sqrt(x), with floating-point starting value + r = int(2.0**(2*startprec) * (x >> (bc-2*startprec)) ** -0.5) + pp = startprec + for p in giant_steps(startprec, hbc): + # r**2, scaled from real size 2**(-bc) to 2**p + r2 = (r*r) >> (2*pp - p) + # x*r**2, scaled from real size ~1.0 to 2**p + xr2 = ((x >> (bc-p)) * r2) >> p + # New value of r, scaled from real size 2**(-bc/2) to 2**p + r = (r * ((3<> (pp+1) + pp = p + # (1/sqrt(x))*x = sqrt(x) + return (r*(x>>hbc)) >> (p+guard_bits) + +def sqrtrem_python(x): + """Correctly rounded integer (floor) square root with remainder.""" + # to check cutoff: + # plot(lambda x: timing(isqrt, 2**int(x)), [0,2000]) + if x < _1_600: + y = isqrt_small_python(x) + return y, x - y*y + y = isqrt_fast_python(x) + 1 + rem = x - y*y + # Correct remainder + while rem < 0: + y -= 1 + rem += (1+2*y) + else: + if rem: + while rem > 2*(1+y): + y += 1 + rem -= (1+2*y) + return y, rem + +def isqrt_python(x): + """Integer square root with correct (floor) rounding.""" + return sqrtrem_python(x)[0] + +def sqrt_fixed(x, prec): + return isqrt_fast(x<= '2': + isqrt_small = isqrt_fast = isqrt = gmpy.isqrt + sqrtrem = gmpy.isqrt_rem + else: + isqrt_small = isqrt_fast = isqrt = gmpy.sqrt + sqrtrem = gmpy.sqrtrem +elif BACKEND == 'sage': + isqrt_small = isqrt_fast = isqrt = \ + getattr(sage_utils, "isqrt", lambda n: MPZ(n).isqrt()) + sqrtrem = lambda n: MPZ(n).sqrtrem() +else: + isqrt_small = isqrt_small_python + isqrt_fast = isqrt_fast_python + isqrt = isqrt_python + sqrtrem = sqrtrem_python + + +def ifib(n, _cache={}): + """Computes the nth Fibonacci number as an integer, for + integer n.""" + if n < 0: + return (-1)**(-n+1) * ifib(-n) + if n in _cache: + return _cache[n] + m = n + # Use Dijkstra's logarithmic algorithm + # The following implementation is basically equivalent to + # http://en.literateprograms.org/Fibonacci_numbers_(Scheme) + a, b, p, q = MPZ_ONE, MPZ_ZERO, MPZ_ZERO, MPZ_ONE + while n: + if n & 1: + aq = a*q + a, b = b*q+aq+a*p, b*p+aq + n -= 1 + else: + qq = q*q + p, q = p*p+qq, qq+2*p*q + n >>= 1 + if m < 250: + _cache[m] = b + return b + +MAX_FACTORIAL_CACHE = 1000 + +def ifac(n, memo={0:1, 1:1}): + """Return n factorial (for integers n >= 0 only).""" + f = memo.get(n) + if f: + return f + k = len(memo) + p = memo[k-1] + MAX = MAX_FACTORIAL_CACHE + while k <= n: + p *= k + if k <= MAX: + memo[k] = p + k += 1 + return p + +def ifac2(n, memo_pair=[{0:1}, {1:1}]): + """Return n!! (double factorial), integers n >= 0 only.""" + memo = memo_pair[n&1] + f = memo.get(n) + if f: + return f + k = max(memo) + p = memo[k] + MAX = MAX_FACTORIAL_CACHE + while k < n: + k += 2 + p *= k + if k <= MAX: + memo[k] = p + return p + +if BACKEND == 'gmpy': + ifac = gmpy.fac +elif BACKEND == 'sage': + ifac = lambda n: int(sage.factorial(n)) + ifib = sage.fibonacci + +def list_primes(n): + n = n + 1 + sieve = list(xrange(n)) + sieve[:2] = [0, 0] + for i in xrange(2, int(n**0.5)+1): + if sieve[i]: + for j in xrange(i**2, n, i): + sieve[j] = 0 + return [p for p in sieve if p] + +if BACKEND == 'sage': + # Note: it is *VERY* important for performance that we convert + # the list to Python ints. + def list_primes(n): + return [int(_) for _ in sage.primes(n+1)] + +small_odd_primes = (3,5,7,11,13,17,19,23,29,31,37,41,43,47) +small_odd_primes_set = set(small_odd_primes) + +def isprime(n): + """ + Determines whether n is a prime number. A probabilistic test is + performed if n is very large. No special trick is used for detecting + perfect powers. + + >>> sum(list_primes(100000)) + 454396537 + >>> sum(n*isprime(n) for n in range(100000)) + 454396537 + + """ + n = int(n) + if not n & 1: + return n == 2 + if n < 50: + return n in small_odd_primes_set + for p in small_odd_primes: + if not n % p: + return False + m = n-1 + s = trailing(m) + d = m >> s + def test(a): + x = pow(a,d,n) + if x == 1 or x == m: + return True + for r in xrange(1,s): + x = x**2 % n + if x == m: + return True + return False + # See http://primes.utm.edu/prove/prove2_3.html + if n < 1373653: + witnesses = [2,3] + elif n < 341550071728321: + witnesses = [2,3,5,7,11,13,17] + else: + witnesses = small_odd_primes + for a in witnesses: + if not test(a): + return False + return True + +def moebius(n): + """ + Evaluates the Moebius function which is `mu(n) = (-1)^k` if `n` + is a product of `k` distinct primes and `mu(n) = 0` otherwise. + + TODO: speed up using factorization + """ + n = abs(int(n)) + if n < 2: + return n + factors = [] + for p in xrange(2, n+1): + if not (n % p): + if not (n % p**2): + return 0 + if not sum(p % f for f in factors): + factors.append(p) + return (-1)**len(factors) + +def gcd(*args): + a = 0 + for b in args: + if a: + while b: + a, b = b, a % b + else: + a = b + return a + + +# Comment by Juan Arias de Reyna: +# +# I learn this method to compute EulerE[2n] from van de Lune. +# +# We apply the formula EulerE[2n] = (-1)^n 2**(-2n) sum_{j=0}^n a(2n,2j+1) +# +# where the numbers a(n,j) vanish for j > n+1 or j <= -1 and satisfies +# +# a(0,-1) = a(0,0) = 0; a(0,1)= 1; a(0,2) = a(0,3) = 0 +# +# a(n,j) = a(n-1,j) when n+j is even +# a(n,j) = (j-1) a(n-1,j-1) + (j+1) a(n-1,j+1) when n+j is odd +# +# +# But we can use only one array unidimensional a(j) since to compute +# a(n,j) we only need to know a(n-1,k) where k and j are of different parity +# and we have not to conserve the used values. +# +# We cached up the values of Euler numbers to sufficiently high order. +# +# Important Observation: If we pretend to use the numbers +# EulerE[1], EulerE[2], ... , EulerE[n] +# it is convenient to compute first EulerE[n], since the algorithm +# computes first all +# the previous ones, and keeps them in the CACHE + +MAX_EULER_CACHE = 500 + +def eulernum(m, _cache={0:MPZ_ONE}): + r""" + Computes the Euler numbers `E(n)`, which can be defined as + coefficients of the Taylor expansion of `1/cosh x`: + + .. math :: + + \frac{1}{\cosh x} = \sum_{n=0}^\infty \frac{E_n}{n!} x^n + + Example:: + + >>> [int(eulernum(n)) for n in range(11)] + [1, 0, -1, 0, 5, 0, -61, 0, 1385, 0, -50521] + >>> [int(eulernum(n)) for n in range(11)] # test cache + [1, 0, -1, 0, 5, 0, -61, 0, 1385, 0, -50521] + + """ + # for odd m > 1, the Euler numbers are zero + if m & 1: + return MPZ_ZERO + f = _cache.get(m) + if f: + return f + MAX = MAX_EULER_CACHE + n = m + a = [MPZ(_) for _ in [0,0,1,0,0,0]] + for n in range(1, m+1): + for j in range(n+1, -1, -2): + a[j+1] = (j-1)*a[j] + (j+1)*a[j+2] + a.append(0) + suma = 0 + for k in range(n+1, -1, -2): + suma += a[k+1] + if n <= MAX: + _cache[n] = ((-1)**(n//2))*(suma // 2**n) + if n == m: + return ((-1)**(n//2))*suma // 2**n + +def stirling1(n, k): + """ + Stirling number of the first kind. + """ + if n < 0 or k < 0: + raise ValueError + if k >= n: + return MPZ(n == k) + if k < 1: + return MPZ_ZERO + L = [MPZ_ZERO] * (k+1) + L[1] = MPZ_ONE + for m in xrange(2, n+1): + for j in xrange(min(k, m), 0, -1): + L[j] = (m-1) * L[j] + L[j-1] + return (-1)**(n+k) * L[k] + +def stirling2(n, k): + """ + Stirling number of the second kind. + """ + if n < 0 or k < 0: + raise ValueError + if k >= n: + return MPZ(n == k) + if k <= 1: + return MPZ(k == 1) + s = MPZ_ZERO + t = MPZ_ONE + for j in xrange(k+1): + if (k + j) & 1: + s -= t * MPZ(j)**n + else: + s += t * MPZ(j)**n + t = t * (k - j) // (j + 1) + return s // ifac(k) diff --git a/lib/python3.12/site-packages/mpmath/libmp/libmpc.py b/lib/python3.12/site-packages/mpmath/libmp/libmpc.py new file mode 100644 index 0000000000000000000000000000000000000000..cc22d0e73674676c8a9249ebc2d48da7f3be8b0d --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/libmp/libmpc.py @@ -0,0 +1,835 @@ +""" +Low-level functions for complex arithmetic. +""" + +import sys + +from .backend import MPZ, MPZ_ZERO, MPZ_ONE, MPZ_TWO, BACKEND + +from .libmpf import (\ + round_floor, round_ceiling, round_down, round_up, + round_nearest, round_fast, bitcount, + bctable, normalize, normalize1, reciprocal_rnd, rshift, lshift, giant_steps, + negative_rnd, + to_str, to_fixed, from_man_exp, from_float, to_float, from_int, to_int, + fzero, fone, ftwo, fhalf, finf, fninf, fnan, fnone, + mpf_abs, mpf_pos, mpf_neg, mpf_add, mpf_sub, mpf_mul, + mpf_div, mpf_mul_int, mpf_shift, mpf_sqrt, mpf_hypot, + mpf_rdiv_int, mpf_floor, mpf_ceil, mpf_nint, mpf_frac, + mpf_sign, mpf_hash, + ComplexResult +) + +from .libelefun import (\ + mpf_pi, mpf_exp, mpf_log, mpf_cos_sin, mpf_cosh_sinh, mpf_tan, mpf_pow_int, + mpf_log_hypot, + mpf_cos_sin_pi, mpf_phi, + mpf_cos, mpf_sin, mpf_cos_pi, mpf_sin_pi, + mpf_atan, mpf_atan2, mpf_cosh, mpf_sinh, mpf_tanh, + mpf_asin, mpf_acos, mpf_acosh, mpf_nthroot, mpf_fibonacci +) + +# An mpc value is a (real, imag) tuple +mpc_one = fone, fzero +mpc_zero = fzero, fzero +mpc_two = ftwo, fzero +mpc_half = (fhalf, fzero) + +_infs = (finf, fninf) +_infs_nan = (finf, fninf, fnan) + +def mpc_is_inf(z): + """Check if either real or imaginary part is infinite""" + re, im = z + if re in _infs: return True + if im in _infs: return True + return False + +def mpc_is_infnan(z): + """Check if either real or imaginary part is infinite or nan""" + re, im = z + if re in _infs_nan: return True + if im in _infs_nan: return True + return False + +def mpc_to_str(z, dps, **kwargs): + re, im = z + rs = to_str(re, dps) + if im[0]: + return rs + " - " + to_str(mpf_neg(im), dps, **kwargs) + "j" + else: + return rs + " + " + to_str(im, dps, **kwargs) + "j" + +def mpc_to_complex(z, strict=False, rnd=round_fast): + re, im = z + return complex(to_float(re, strict, rnd), to_float(im, strict, rnd)) + +def mpc_hash(z): + if sys.version_info >= (3, 2): + re, im = z + h = mpf_hash(re) + sys.hash_info.imag * mpf_hash(im) + # Need to reduce either module 2^32 or 2^64 + h = h % (2**sys.hash_info.width) + return int(h) + else: + try: + return hash(mpc_to_complex(z, strict=True)) + except OverflowError: + return hash(z) + +def mpc_conjugate(z, prec, rnd=round_fast): + re, im = z + return re, mpf_neg(im, prec, rnd) + +def mpc_is_nonzero(z): + return z != mpc_zero + +def mpc_add(z, w, prec, rnd=round_fast): + a, b = z + c, d = w + return mpf_add(a, c, prec, rnd), mpf_add(b, d, prec, rnd) + +def mpc_add_mpf(z, x, prec, rnd=round_fast): + a, b = z + return mpf_add(a, x, prec, rnd), b + +def mpc_sub(z, w, prec=0, rnd=round_fast): + a, b = z + c, d = w + return mpf_sub(a, c, prec, rnd), mpf_sub(b, d, prec, rnd) + +def mpc_sub_mpf(z, p, prec=0, rnd=round_fast): + a, b = z + return mpf_sub(a, p, prec, rnd), b + +def mpc_pos(z, prec, rnd=round_fast): + a, b = z + return mpf_pos(a, prec, rnd), mpf_pos(b, prec, rnd) + +def mpc_neg(z, prec=None, rnd=round_fast): + a, b = z + return mpf_neg(a, prec, rnd), mpf_neg(b, prec, rnd) + +def mpc_shift(z, n): + a, b = z + return mpf_shift(a, n), mpf_shift(b, n) + +def mpc_abs(z, prec, rnd=round_fast): + """Absolute value of a complex number, |a+bi|. + Returns an mpf value.""" + a, b = z + return mpf_hypot(a, b, prec, rnd) + +def mpc_arg(z, prec, rnd=round_fast): + """Argument of a complex number. Returns an mpf value.""" + a, b = z + return mpf_atan2(b, a, prec, rnd) + +def mpc_floor(z, prec, rnd=round_fast): + a, b = z + return mpf_floor(a, prec, rnd), mpf_floor(b, prec, rnd) + +def mpc_ceil(z, prec, rnd=round_fast): + a, b = z + return mpf_ceil(a, prec, rnd), mpf_ceil(b, prec, rnd) + +def mpc_nint(z, prec, rnd=round_fast): + a, b = z + return mpf_nint(a, prec, rnd), mpf_nint(b, prec, rnd) + +def mpc_frac(z, prec, rnd=round_fast): + a, b = z + return mpf_frac(a, prec, rnd), mpf_frac(b, prec, rnd) + + +def mpc_mul(z, w, prec, rnd=round_fast): + """ + Complex multiplication. + + Returns the real and imaginary part of (a+bi)*(c+di), rounded to + the specified precision. The rounding mode applies to the real and + imaginary parts separately. + """ + a, b = z + c, d = w + p = mpf_mul(a, c) + q = mpf_mul(b, d) + r = mpf_mul(a, d) + s = mpf_mul(b, c) + re = mpf_sub(p, q, prec, rnd) + im = mpf_add(r, s, prec, rnd) + return re, im + +def mpc_square(z, prec, rnd=round_fast): + # (a+b*I)**2 == a**2 - b**2 + 2*I*a*b + a, b = z + p = mpf_mul(a,a) + q = mpf_mul(b,b) + r = mpf_mul(a,b, prec, rnd) + re = mpf_sub(p, q, prec, rnd) + im = mpf_shift(r, 1) + return re, im + +def mpc_mul_mpf(z, p, prec, rnd=round_fast): + a, b = z + re = mpf_mul(a, p, prec, rnd) + im = mpf_mul(b, p, prec, rnd) + return re, im + +def mpc_mul_imag_mpf(z, x, prec, rnd=round_fast): + """ + Multiply the mpc value z by I*x where x is an mpf value. + """ + a, b = z + re = mpf_neg(mpf_mul(b, x, prec, rnd)) + im = mpf_mul(a, x, prec, rnd) + return re, im + +def mpc_mul_int(z, n, prec, rnd=round_fast): + a, b = z + re = mpf_mul_int(a, n, prec, rnd) + im = mpf_mul_int(b, n, prec, rnd) + return re, im + +def mpc_div(z, w, prec, rnd=round_fast): + a, b = z + c, d = w + wp = prec + 10 + # mag = c*c + d*d + mag = mpf_add(mpf_mul(c, c), mpf_mul(d, d), wp) + # (a*c+b*d)/mag, (b*c-a*d)/mag + t = mpf_add(mpf_mul(a,c), mpf_mul(b,d), wp) + u = mpf_sub(mpf_mul(b,c), mpf_mul(a,d), wp) + return mpf_div(t,mag,prec,rnd), mpf_div(u,mag,prec,rnd) + +def mpc_div_mpf(z, p, prec, rnd=round_fast): + """Calculate z/p where p is real""" + a, b = z + re = mpf_div(a, p, prec, rnd) + im = mpf_div(b, p, prec, rnd) + return re, im + +def mpc_reciprocal(z, prec, rnd=round_fast): + """Calculate 1/z efficiently""" + a, b = z + m = mpf_add(mpf_mul(a,a),mpf_mul(b,b),prec+10) + re = mpf_div(a, m, prec, rnd) + im = mpf_neg(mpf_div(b, m, prec, rnd)) + return re, im + +def mpc_mpf_div(p, z, prec, rnd=round_fast): + """Calculate p/z where p is real efficiently""" + a, b = z + m = mpf_add(mpf_mul(a,a),mpf_mul(b,b), prec+10) + re = mpf_div(mpf_mul(a,p), m, prec, rnd) + im = mpf_div(mpf_neg(mpf_mul(b,p)), m, prec, rnd) + return re, im + +def complex_int_pow(a, b, n): + """Complex integer power: computes (a+b*I)**n exactly for + nonnegative n (a and b must be Python ints).""" + wre = 1 + wim = 0 + while n: + if n & 1: + wre, wim = wre*a - wim*b, wim*a + wre*b + n -= 1 + a, b = a*a - b*b, 2*a*b + n //= 2 + return wre, wim + +def mpc_pow(z, w, prec, rnd=round_fast): + if w[1] == fzero: + return mpc_pow_mpf(z, w[0], prec, rnd) + return mpc_exp(mpc_mul(mpc_log(z, prec+10), w, prec+10), prec, rnd) + +def mpc_pow_mpf(z, p, prec, rnd=round_fast): + psign, pman, pexp, pbc = p + if pexp >= 0: + return mpc_pow_int(z, (-1)**psign * (pman< 0: + aman <<= de + aexp = bexp + else: + bman <<= (-de) + bexp = aexp + re, im = complex_int_pow(aman, bman, n) + re = from_man_exp(re, int(n*aexp), prec, rnd) + im = from_man_exp(im, int(n*bexp), prec, rnd) + return re, im + return mpc_exp(mpc_mul_int(mpc_log(z, prec+10), n, prec+10), prec, rnd) + +def mpc_sqrt(z, prec, rnd=round_fast): + """Complex square root (principal branch). + + We have sqrt(a+bi) = sqrt((r+a)/2) + b/sqrt(2*(r+a))*i where + r = abs(a+bi), when a+bi is not a negative real number.""" + a, b = z + if b == fzero: + if a == fzero: + return (a, b) + # When a+bi is a negative real number, we get a real sqrt times i + if a[0]: + im = mpf_sqrt(mpf_neg(a), prec, rnd) + return (fzero, im) + else: + re = mpf_sqrt(a, prec, rnd) + return (re, fzero) + wp = prec+20 + if not a[0]: # case a positive + t = mpf_add(mpc_abs((a, b), wp), a, wp) # t = abs(a+bi) + a + u = mpf_shift(t, -1) # u = t/2 + re = mpf_sqrt(u, prec, rnd) # re = sqrt(u) + v = mpf_shift(t, 1) # v = 2*t + w = mpf_sqrt(v, wp) # w = sqrt(v) + im = mpf_div(b, w, prec, rnd) # im = b / w + else: # case a negative + t = mpf_sub(mpc_abs((a, b), wp), a, wp) # t = abs(a+bi) - a + u = mpf_shift(t, -1) # u = t/2 + im = mpf_sqrt(u, prec, rnd) # im = sqrt(u) + v = mpf_shift(t, 1) # v = 2*t + w = mpf_sqrt(v, wp) # w = sqrt(v) + re = mpf_div(b, w, prec, rnd) # re = b/w + if b[0]: + re = mpf_neg(re) + im = mpf_neg(im) + return re, im + +def mpc_nthroot_fixed(a, b, n, prec): + # a, b signed integers at fixed precision prec + start = 50 + a1 = int(rshift(a, prec - n*start)) + b1 = int(rshift(b, prec - n*start)) + try: + r = (a1 + 1j * b1)**(1.0/n) + re = r.real + im = r.imag + re = MPZ(int(re)) + im = MPZ(int(im)) + except OverflowError: + a1 = from_int(a1, start) + b1 = from_int(b1, start) + fn = from_int(n) + nth = mpf_rdiv_int(1, fn, start) + re, im = mpc_pow((a1, b1), (nth, fzero), start) + re = to_int(re) + im = to_int(im) + extra = 10 + prevp = start + extra1 = n + for p in giant_steps(start, prec+extra): + # this is slow for large n, unlike int_pow_fixed + re2, im2 = complex_int_pow(re, im, n-1) + re2 = rshift(re2, (n-1)*prevp - p - extra1) + im2 = rshift(im2, (n-1)*prevp - p - extra1) + r4 = (re2*re2 + im2*im2) >> (p + extra1) + ap = rshift(a, prec - p) + bp = rshift(b, prec - p) + rec = (ap * re2 + bp * im2) >> p + imc = (-ap * im2 + bp * re2) >> p + reb = (rec << p) // r4 + imb = (imc << p) // r4 + re = (reb + (n-1)*lshift(re, p-prevp))//n + im = (imb + (n-1)*lshift(im, p-prevp))//n + prevp = p + return re, im + +def mpc_nthroot(z, n, prec, rnd=round_fast): + """ + Complex n-th root. + + Use Newton method as in the real case when it is faster, + otherwise use z**(1/n) + """ + a, b = z + if a[0] == 0 and b == fzero: + re = mpf_nthroot(a, n, prec, rnd) + return (re, fzero) + if n < 2: + if n == 0: + return mpc_one + if n == 1: + return mpc_pos((a, b), prec, rnd) + if n == -1: + return mpc_div(mpc_one, (a, b), prec, rnd) + inverse = mpc_nthroot((a, b), -n, prec+5, reciprocal_rnd[rnd]) + return mpc_div(mpc_one, inverse, prec, rnd) + if n <= 20: + prec2 = int(1.2 * (prec + 10)) + asign, aman, aexp, abc = a + bsign, bman, bexp, bbc = b + pf = mpc_abs((a,b), prec) + if pf[-2] + pf[-1] > -10 and pf[-2] + pf[-1] < prec: + af = to_fixed(a, prec2) + bf = to_fixed(b, prec2) + re, im = mpc_nthroot_fixed(af, bf, n, prec2) + extra = 10 + re = from_man_exp(re, -prec2-extra, prec2, rnd) + im = from_man_exp(im, -prec2-extra, prec2, rnd) + return re, im + fn = from_int(n) + prec2 = prec+10 + 10 + nth = mpf_rdiv_int(1, fn, prec2) + re, im = mpc_pow((a, b), (nth, fzero), prec2, rnd) + re = normalize(re[0], re[1], re[2], re[3], prec, rnd) + im = normalize(im[0], im[1], im[2], im[3], prec, rnd) + return re, im + +def mpc_cbrt(z, prec, rnd=round_fast): + """ + Complex cubic root. + """ + return mpc_nthroot(z, 3, prec, rnd) + +def mpc_exp(z, prec, rnd=round_fast): + """ + Complex exponential function. + + We use the direct formula exp(a+bi) = exp(a) * (cos(b) + sin(b)*i) + for the computation. This formula is very nice because it is + pefectly stable; since we just do real multiplications, the only + numerical errors that can creep in are single-ulp rounding errors. + + The formula is efficient since mpmath's real exp is quite fast and + since we can compute cos and sin simultaneously. + + It is no problem if a and b are large; if the implementations of + exp/cos/sin are accurate and efficient for all real numbers, then + so is this function for all complex numbers. + """ + a, b = z + if a == fzero: + return mpf_cos_sin(b, prec, rnd) + if b == fzero: + return mpf_exp(a, prec, rnd), fzero + mag = mpf_exp(a, prec+4, rnd) + c, s = mpf_cos_sin(b, prec+4, rnd) + re = mpf_mul(mag, c, prec, rnd) + im = mpf_mul(mag, s, prec, rnd) + return re, im + +def mpc_log(z, prec, rnd=round_fast): + re = mpf_log_hypot(z[0], z[1], prec, rnd) + im = mpc_arg(z, prec, rnd) + return re, im + +def mpc_cos(z, prec, rnd=round_fast): + """Complex cosine. The formula used is cos(a+bi) = cos(a)*cosh(b) - + sin(a)*sinh(b)*i. + + The same comments apply as for the complex exp: only real + multiplications are pewrormed, so no cancellation errors are + possible. The formula is also efficient since we can compute both + pairs (cos, sin) and (cosh, sinh) in single stwps.""" + a, b = z + if b == fzero: + return mpf_cos(a, prec, rnd), fzero + if a == fzero: + return mpf_cosh(b, prec, rnd), fzero + wp = prec + 6 + c, s = mpf_cos_sin(a, wp) + ch, sh = mpf_cosh_sinh(b, wp) + re = mpf_mul(c, ch, prec, rnd) + im = mpf_mul(s, sh, prec, rnd) + return re, mpf_neg(im) + +def mpc_sin(z, prec, rnd=round_fast): + """Complex sine. We have sin(a+bi) = sin(a)*cosh(b) + + cos(a)*sinh(b)*i. See the docstring for mpc_cos for additional + comments.""" + a, b = z + if b == fzero: + return mpf_sin(a, prec, rnd), fzero + if a == fzero: + return fzero, mpf_sinh(b, prec, rnd) + wp = prec + 6 + c, s = mpf_cos_sin(a, wp) + ch, sh = mpf_cosh_sinh(b, wp) + re = mpf_mul(s, ch, prec, rnd) + im = mpf_mul(c, sh, prec, rnd) + return re, im + +def mpc_tan(z, prec, rnd=round_fast): + """Complex tangent. Computed as tan(a+bi) = sin(2a)/M + sinh(2b)/M*i + where M = cos(2a) + cosh(2b).""" + a, b = z + asign, aman, aexp, abc = a + bsign, bman, bexp, bbc = b + if b == fzero: return mpf_tan(a, prec, rnd), fzero + if a == fzero: return fzero, mpf_tanh(b, prec, rnd) + wp = prec + 15 + a = mpf_shift(a, 1) + b = mpf_shift(b, 1) + c, s = mpf_cos_sin(a, wp) + ch, sh = mpf_cosh_sinh(b, wp) + # TODO: handle cancellation when c ~= -1 and ch ~= 1 + mag = mpf_add(c, ch, wp) + re = mpf_div(s, mag, prec, rnd) + im = mpf_div(sh, mag, prec, rnd) + return re, im + +def mpc_cos_pi(z, prec, rnd=round_fast): + a, b = z + if b == fzero: + return mpf_cos_pi(a, prec, rnd), fzero + b = mpf_mul(b, mpf_pi(prec+5), prec+5) + if a == fzero: + return mpf_cosh(b, prec, rnd), fzero + wp = prec + 6 + c, s = mpf_cos_sin_pi(a, wp) + ch, sh = mpf_cosh_sinh(b, wp) + re = mpf_mul(c, ch, prec, rnd) + im = mpf_mul(s, sh, prec, rnd) + return re, mpf_neg(im) + +def mpc_sin_pi(z, prec, rnd=round_fast): + a, b = z + if b == fzero: + return mpf_sin_pi(a, prec, rnd), fzero + b = mpf_mul(b, mpf_pi(prec+5), prec+5) + if a == fzero: + return fzero, mpf_sinh(b, prec, rnd) + wp = prec + 6 + c, s = mpf_cos_sin_pi(a, wp) + ch, sh = mpf_cosh_sinh(b, wp) + re = mpf_mul(s, ch, prec, rnd) + im = mpf_mul(c, sh, prec, rnd) + return re, im + +def mpc_cos_sin(z, prec, rnd=round_fast): + a, b = z + if a == fzero: + ch, sh = mpf_cosh_sinh(b, prec, rnd) + return (ch, fzero), (fzero, sh) + if b == fzero: + c, s = mpf_cos_sin(a, prec, rnd) + return (c, fzero), (s, fzero) + wp = prec + 6 + c, s = mpf_cos_sin(a, wp) + ch, sh = mpf_cosh_sinh(b, wp) + cre = mpf_mul(c, ch, prec, rnd) + cim = mpf_mul(s, sh, prec, rnd) + sre = mpf_mul(s, ch, prec, rnd) + sim = mpf_mul(c, sh, prec, rnd) + return (cre, mpf_neg(cim)), (sre, sim) + +def mpc_cos_sin_pi(z, prec, rnd=round_fast): + a, b = z + if b == fzero: + c, s = mpf_cos_sin_pi(a, prec, rnd) + return (c, fzero), (s, fzero) + b = mpf_mul(b, mpf_pi(prec+5), prec+5) + if a == fzero: + ch, sh = mpf_cosh_sinh(b, prec, rnd) + return (ch, fzero), (fzero, sh) + wp = prec + 6 + c, s = mpf_cos_sin_pi(a, wp) + ch, sh = mpf_cosh_sinh(b, wp) + cre = mpf_mul(c, ch, prec, rnd) + cim = mpf_mul(s, sh, prec, rnd) + sre = mpf_mul(s, ch, prec, rnd) + sim = mpf_mul(c, sh, prec, rnd) + return (cre, mpf_neg(cim)), (sre, sim) + +def mpc_cosh(z, prec, rnd=round_fast): + """Complex hyperbolic cosine. Computed as cosh(z) = cos(z*i).""" + a, b = z + return mpc_cos((b, mpf_neg(a)), prec, rnd) + +def mpc_sinh(z, prec, rnd=round_fast): + """Complex hyperbolic sine. Computed as sinh(z) = -i*sin(z*i).""" + a, b = z + b, a = mpc_sin((b, a), prec, rnd) + return a, b + +def mpc_tanh(z, prec, rnd=round_fast): + """Complex hyperbolic tangent. Computed as tanh(z) = -i*tan(z*i).""" + a, b = z + b, a = mpc_tan((b, a), prec, rnd) + return a, b + +# TODO: avoid loss of accuracy +def mpc_atan(z, prec, rnd=round_fast): + a, b = z + # atan(z) = (I/2)*(log(1-I*z) - log(1+I*z)) + # x = 1-I*z = 1 + b - I*a + # y = 1+I*z = 1 - b + I*a + wp = prec + 15 + x = mpf_add(fone, b, wp), mpf_neg(a) + y = mpf_sub(fone, b, wp), a + l1 = mpc_log(x, wp) + l2 = mpc_log(y, wp) + a, b = mpc_sub(l1, l2, prec, rnd) + # (I/2) * (a+b*I) = (-b/2 + a/2*I) + v = mpf_neg(mpf_shift(b,-1)), mpf_shift(a,-1) + # Subtraction at infinity gives correct real part but + # wrong imaginary part (should be zero) + if v[1] == fnan and mpc_is_inf(z): + v = (v[0], fzero) + return v + +beta_crossover = from_float(0.6417) +alpha_crossover = from_float(1.5) + +def acos_asin(z, prec, rnd, n): + """ complex acos for n = 0, asin for n = 1 + The algorithm is described in + T.E. Hull, T.F. Fairgrieve and P.T.P. Tang + 'Implementing the Complex Arcsine and Arcosine Functions + using Exception Handling', + ACM Trans. on Math. Software Vol. 23 (1997), p299 + The complex acos and asin can be defined as + acos(z) = acos(beta) - I*sign(a)* log(alpha + sqrt(alpha**2 -1)) + asin(z) = asin(beta) + I*sign(a)* log(alpha + sqrt(alpha**2 -1)) + where z = a + I*b + alpha = (1/2)*(r + s); beta = (1/2)*(r - s) = a/alpha + r = sqrt((a+1)**2 + y**2); s = sqrt((a-1)**2 + y**2) + These expressions are rewritten in different ways in different + regions, delimited by two crossovers alpha_crossover and beta_crossover, + and by abs(a) <= 1, in order to improve the numerical accuracy. + """ + a, b = z + wp = prec + 10 + # special cases with real argument + if b == fzero: + am = mpf_sub(fone, mpf_abs(a), wp) + # case abs(a) <= 1 + if not am[0]: + if n == 0: + return mpf_acos(a, prec, rnd), fzero + else: + return mpf_asin(a, prec, rnd), fzero + # cases abs(a) > 1 + else: + # case a < -1 + if a[0]: + pi = mpf_pi(prec, rnd) + c = mpf_acosh(mpf_neg(a), prec, rnd) + if n == 0: + return pi, mpf_neg(c) + else: + return mpf_neg(mpf_shift(pi, -1)), c + # case a > 1 + else: + c = mpf_acosh(a, prec, rnd) + if n == 0: + return fzero, c + else: + pi = mpf_pi(prec, rnd) + return mpf_shift(pi, -1), mpf_neg(c) + asign = bsign = 0 + if a[0]: + a = mpf_neg(a) + asign = 1 + if b[0]: + b = mpf_neg(b) + bsign = 1 + am = mpf_sub(fone, a, wp) + ap = mpf_add(fone, a, wp) + r = mpf_hypot(ap, b, wp) + s = mpf_hypot(am, b, wp) + alpha = mpf_shift(mpf_add(r, s, wp), -1) + beta = mpf_div(a, alpha, wp) + b2 = mpf_mul(b,b, wp) + # case beta <= beta_crossover + if not mpf_sub(beta_crossover, beta, wp)[0]: + if n == 0: + re = mpf_acos(beta, wp) + else: + re = mpf_asin(beta, wp) + else: + # to compute the real part in this region use the identity + # asin(beta) = atan(beta/sqrt(1-beta**2)) + # beta/sqrt(1-beta**2) = (alpha + a) * (alpha - a) + # alpha + a is numerically accurate; alpha - a can have + # cancellations leading to numerical inaccuracies, so rewrite + # it in differente ways according to the region + Ax = mpf_add(alpha, a, wp) + # case a <= 1 + if not am[0]: + # c = b*b/(r + (a+1)); d = (s + (1-a)) + # alpha - a = (1/2)*(c + d) + # case n=0: re = atan(sqrt((1/2) * Ax * (c + d))/a) + # case n=1: re = atan(a/sqrt((1/2) * Ax * (c + d))) + c = mpf_div(b2, mpf_add(r, ap, wp), wp) + d = mpf_add(s, am, wp) + re = mpf_shift(mpf_mul(Ax, mpf_add(c, d, wp), wp), -1) + if n == 0: + re = mpf_atan(mpf_div(mpf_sqrt(re, wp), a, wp), wp) + else: + re = mpf_atan(mpf_div(a, mpf_sqrt(re, wp), wp), wp) + else: + # c = Ax/(r + (a+1)); d = Ax/(s - (1-a)) + # alpha - a = (1/2)*(c + d) + # case n = 0: re = atan(b*sqrt(c + d)/2/a) + # case n = 1: re = atan(a/(b*sqrt(c + d)/2) + c = mpf_div(Ax, mpf_add(r, ap, wp), wp) + d = mpf_div(Ax, mpf_sub(s, am, wp), wp) + re = mpf_shift(mpf_add(c, d, wp), -1) + re = mpf_mul(b, mpf_sqrt(re, wp), wp) + if n == 0: + re = mpf_atan(mpf_div(re, a, wp), wp) + else: + re = mpf_atan(mpf_div(a, re, wp), wp) + # to compute alpha + sqrt(alpha**2 - 1), if alpha <= alpha_crossover + # replace it with 1 + Am1 + sqrt(Am1*(alpha+1))) + # where Am1 = alpha -1 + # if alpha <= alpha_crossover: + if not mpf_sub(alpha_crossover, alpha, wp)[0]: + c1 = mpf_div(b2, mpf_add(r, ap, wp), wp) + # case a < 1 + if mpf_neg(am)[0]: + # Am1 = (1/2) * (b*b/(r + (a+1)) + b*b/(s + (1-a)) + c2 = mpf_add(s, am, wp) + c2 = mpf_div(b2, c2, wp) + Am1 = mpf_shift(mpf_add(c1, c2, wp), -1) + else: + # Am1 = (1/2) * (b*b/(r + (a+1)) + (s - (1-a))) + c2 = mpf_sub(s, am, wp) + Am1 = mpf_shift(mpf_add(c1, c2, wp), -1) + # im = log(1 + Am1 + sqrt(Am1*(alpha+1))) + im = mpf_mul(Am1, mpf_add(alpha, fone, wp), wp) + im = mpf_log(mpf_add(fone, mpf_add(Am1, mpf_sqrt(im, wp), wp), wp), wp) + else: + # im = log(alpha + sqrt(alpha*alpha - 1)) + im = mpf_sqrt(mpf_sub(mpf_mul(alpha, alpha, wp), fone, wp), wp) + im = mpf_log(mpf_add(alpha, im, wp), wp) + if asign: + if n == 0: + re = mpf_sub(mpf_pi(wp), re, wp) + else: + re = mpf_neg(re) + if not bsign and n == 0: + im = mpf_neg(im) + if bsign and n == 1: + im = mpf_neg(im) + re = normalize(re[0], re[1], re[2], re[3], prec, rnd) + im = normalize(im[0], im[1], im[2], im[3], prec, rnd) + return re, im + +def mpc_acos(z, prec, rnd=round_fast): + return acos_asin(z, prec, rnd, 0) + +def mpc_asin(z, prec, rnd=round_fast): + return acos_asin(z, prec, rnd, 1) + +def mpc_asinh(z, prec, rnd=round_fast): + # asinh(z) = I * asin(-I z) + a, b = z + a, b = mpc_asin((b, mpf_neg(a)), prec, rnd) + return mpf_neg(b), a + +def mpc_acosh(z, prec, rnd=round_fast): + # acosh(z) = -I * acos(z) for Im(acos(z)) <= 0 + # +I * acos(z) otherwise + a, b = mpc_acos(z, prec, rnd) + if b[0] or b == fzero: + return mpf_neg(b), a + else: + return b, mpf_neg(a) + +def mpc_atanh(z, prec, rnd=round_fast): + # atanh(z) = (log(1+z)-log(1-z))/2 + wp = prec + 15 + a = mpc_add(z, mpc_one, wp) + b = mpc_sub(mpc_one, z, wp) + a = mpc_log(a, wp) + b = mpc_log(b, wp) + v = mpc_shift(mpc_sub(a, b, wp), -1) + # Subtraction at infinity gives correct imaginary part but + # wrong real part (should be zero) + if v[0] == fnan and mpc_is_inf(z): + v = (fzero, v[1]) + return v + +def mpc_fibonacci(z, prec, rnd=round_fast): + re, im = z + if im == fzero: + return (mpf_fibonacci(re, prec, rnd), fzero) + size = max(abs(re[2]+re[3]), abs(re[2]+re[3])) + wp = prec + size + 20 + a = mpf_phi(wp) + b = mpf_add(mpf_shift(a, 1), fnone, wp) + u = mpc_pow((a, fzero), z, wp) + v = mpc_cos_pi(z, wp) + v = mpc_div(v, u, wp) + u = mpc_sub(u, v, wp) + u = mpc_div_mpf(u, b, prec, rnd) + return u + +def mpf_expj(x, prec, rnd='f'): + raise ComplexResult + +def mpc_expj(z, prec, rnd='f'): + re, im = z + if im == fzero: + return mpf_cos_sin(re, prec, rnd) + if re == fzero: + return mpf_exp(mpf_neg(im), prec, rnd), fzero + ey = mpf_exp(mpf_neg(im), prec+10) + c, s = mpf_cos_sin(re, prec+10) + re = mpf_mul(ey, c, prec, rnd) + im = mpf_mul(ey, s, prec, rnd) + return re, im + +def mpf_expjpi(x, prec, rnd='f'): + raise ComplexResult + +def mpc_expjpi(z, prec, rnd='f'): + re, im = z + if im == fzero: + return mpf_cos_sin_pi(re, prec, rnd) + sign, man, exp, bc = im + wp = prec+10 + if man: + wp += max(0, exp+bc) + im = mpf_neg(mpf_mul(mpf_pi(wp), im, wp)) + if re == fzero: + return mpf_exp(im, prec, rnd), fzero + ey = mpf_exp(im, prec+10) + c, s = mpf_cos_sin_pi(re, prec+10) + re = mpf_mul(ey, c, prec, rnd) + im = mpf_mul(ey, s, prec, rnd) + return re, im + + +if BACKEND == 'sage': + try: + import sage.libs.mpmath.ext_libmp as _lbmp + mpc_exp = _lbmp.mpc_exp + mpc_sqrt = _lbmp.mpc_sqrt + except (ImportError, AttributeError): + print("Warning: Sage imports in libmpc failed") diff --git a/lib/python3.12/site-packages/mpmath/libmp/libmpf.py b/lib/python3.12/site-packages/mpmath/libmp/libmpf.py new file mode 100644 index 0000000000000000000000000000000000000000..5c162e17d4f688c71dc3476b944e2d31c65faab7 --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/libmp/libmpf.py @@ -0,0 +1,1414 @@ +""" +Low-level functions for arbitrary-precision floating-point arithmetic. +""" + +__docformat__ = 'plaintext' + +import math + +from bisect import bisect + +import sys + +# Importing random is slow +#from random import getrandbits +getrandbits = None + +from .backend import (MPZ, MPZ_TYPE, MPZ_ZERO, MPZ_ONE, MPZ_TWO, MPZ_FIVE, + BACKEND, STRICT, HASH_MODULUS, HASH_BITS, gmpy, sage, sage_utils) + +from .libintmath import (giant_steps, + trailtable, bctable, lshift, rshift, bitcount, trailing, + sqrt_fixed, numeral, isqrt, isqrt_fast, sqrtrem, + bin_to_radix) + +# We don't pickle tuples directly for the following reasons: +# 1: pickle uses str() for ints, which is inefficient when they are large +# 2: pickle doesn't work for gmpy mpzs +# Both problems are solved by using hex() + +if BACKEND == 'sage': + def to_pickable(x): + sign, man, exp, bc = x + return sign, hex(man), exp, bc +else: + def to_pickable(x): + sign, man, exp, bc = x + return sign, hex(man)[2:], exp, bc + +def from_pickable(x): + sign, man, exp, bc = x + return (sign, MPZ(man, 16), exp, bc) + +class ComplexResult(ValueError): + pass + +try: + intern +except NameError: + intern = lambda x: x + +# All supported rounding modes +round_nearest = intern('n') +round_floor = intern('f') +round_ceiling = intern('c') +round_up = intern('u') +round_down = intern('d') +round_fast = round_down + +def prec_to_dps(n): + """Return number of accurate decimals that can be represented + with a precision of n bits.""" + return max(1, int(round(int(n)/3.3219280948873626)-1)) + +def dps_to_prec(n): + """Return the number of bits required to represent n decimals + accurately.""" + return max(1, int(round((int(n)+1)*3.3219280948873626))) + +def repr_dps(n): + """Return the number of decimal digits required to represent + a number with n-bit precision so that it can be uniquely + reconstructed from the representation.""" + dps = prec_to_dps(n) + if dps == 15: + return 17 + return dps + 3 + +#----------------------------------------------------------------------------# +# Some commonly needed float values # +#----------------------------------------------------------------------------# + +# Regular number format: +# (-1)**sign * mantissa * 2**exponent, plus bitcount of mantissa +fzero = (0, MPZ_ZERO, 0, 0) +fnzero = (1, MPZ_ZERO, 0, 0) +fone = (0, MPZ_ONE, 0, 1) +fnone = (1, MPZ_ONE, 0, 1) +ftwo = (0, MPZ_ONE, 1, 1) +ften = (0, MPZ_FIVE, 1, 3) +fhalf = (0, MPZ_ONE, -1, 1) + +# Arbitrary encoding for special numbers: zero mantissa, nonzero exponent +fnan = (0, MPZ_ZERO, -123, -1) +finf = (0, MPZ_ZERO, -456, -2) +fninf = (1, MPZ_ZERO, -789, -3) + +# Was 1e1000; this is broken in Python 2.4 +math_float_inf = 1e300 * 1e300 + + +#----------------------------------------------------------------------------# +# Rounding # +#----------------------------------------------------------------------------# + +# This function can be used to round a mantissa generally. However, +# we will try to do most rounding inline for efficiency. +def round_int(x, n, rnd): + if rnd == round_nearest: + if x >= 0: + t = x >> (n-1) + if t & 1 and ((t & 2) or (x & h_mask[n<300][n])): + return (t>>1)+1 + else: + return t>>1 + else: + return -round_int(-x, n, rnd) + if rnd == round_floor: + return x >> n + if rnd == round_ceiling: + return -((-x) >> n) + if rnd == round_down: + if x >= 0: + return x >> n + return -((-x) >> n) + if rnd == round_up: + if x >= 0: + return -((-x) >> n) + return x >> n + +# These masks are used to pick out segments of numbers to determine +# which direction to round when rounding to nearest. +class h_mask_big: + def __getitem__(self, n): + return (MPZ_ONE<<(n-1))-1 + +h_mask_small = [0]+[((MPZ_ONE<<(_-1))-1) for _ in range(1, 300)] +h_mask = [h_mask_big(), h_mask_small] + +# The >> operator rounds to floor. shifts_down[rnd][sign] +# tells whether this is the right direction to use, or if the +# number should be negated before shifting +shifts_down = {round_floor:(1,0), round_ceiling:(0,1), + round_down:(1,1), round_up:(0,0)} + + +#----------------------------------------------------------------------------# +# Normalization of raw mpfs # +#----------------------------------------------------------------------------# + +# This function is called almost every time an mpf is created. +# It has been optimized accordingly. + +def _normalize(sign, man, exp, bc, prec, rnd): + """ + Create a raw mpf tuple with value (-1)**sign * man * 2**exp and + normalized mantissa. The mantissa is rounded in the specified + direction if its size exceeds the precision. Trailing zero bits + are also stripped from the mantissa to ensure that the + representation is canonical. + + Conditions on the input: + * The input must represent a regular (finite) number + * The sign bit must be 0 or 1 + * The mantissa must be positive + * The exponent must be an integer + * The bitcount must be exact + + If these conditions are not met, use from_man_exp, mpf_pos, or any + of the conversion functions to create normalized raw mpf tuples. + """ + if not man: + return fzero + # Cut mantissa down to size if larger than target precision + n = bc - prec + if n > 0: + if rnd == round_nearest: + t = man >> (n-1) + if t & 1 and ((t & 2) or (man & h_mask[n<300][n])): + man = (t>>1)+1 + else: + man = t>>1 + elif shifts_down[rnd][sign]: + man >>= n + else: + man = -((-man)>>n) + exp += n + bc = prec + # Strip trailing bits + if not man & 1: + t = trailtable[int(man & 255)] + if not t: + while not man & 255: + man >>= 8 + exp += 8 + bc -= 8 + t = trailtable[int(man & 255)] + man >>= t + exp += t + bc -= t + # Bit count can be wrong if the input mantissa was 1 less than + # a power of 2 and got rounded up, thereby adding an extra bit. + # With trailing bits removed, all powers of two have mantissa 1, + # so this is easy to check for. + if man == 1: + bc = 1 + return sign, man, exp, bc + +def _normalize1(sign, man, exp, bc, prec, rnd): + """same as normalize, but with the added condition that + man is odd or zero + """ + if not man: + return fzero + if bc <= prec: + return sign, man, exp, bc + n = bc - prec + if rnd == round_nearest: + t = man >> (n-1) + if t & 1 and ((t & 2) or (man & h_mask[n<300][n])): + man = (t>>1)+1 + else: + man = t>>1 + elif shifts_down[rnd][sign]: + man >>= n + else: + man = -((-man)>>n) + exp += n + bc = prec + # Strip trailing bits + if not man & 1: + t = trailtable[int(man & 255)] + if not t: + while not man & 255: + man >>= 8 + exp += 8 + bc -= 8 + t = trailtable[int(man & 255)] + man >>= t + exp += t + bc -= t + # Bit count can be wrong if the input mantissa was 1 less than + # a power of 2 and got rounded up, thereby adding an extra bit. + # With trailing bits removed, all powers of two have mantissa 1, + # so this is easy to check for. + if man == 1: + bc = 1 + return sign, man, exp, bc + +try: + _exp_types = (int, long) +except NameError: + _exp_types = (int,) + +def strict_normalize(sign, man, exp, bc, prec, rnd): + """Additional checks on the components of an mpf. Enable tests by setting + the environment variable MPMATH_STRICT to Y.""" + assert type(man) == MPZ_TYPE + assert type(bc) in _exp_types + assert type(exp) in _exp_types + assert bc == bitcount(man) + return _normalize(sign, man, exp, bc, prec, rnd) + +def strict_normalize1(sign, man, exp, bc, prec, rnd): + """Additional checks on the components of an mpf. Enable tests by setting + the environment variable MPMATH_STRICT to Y.""" + assert type(man) == MPZ_TYPE + assert type(bc) in _exp_types + assert type(exp) in _exp_types + assert bc == bitcount(man) + assert (not man) or (man & 1) + return _normalize1(sign, man, exp, bc, prec, rnd) + +if BACKEND == 'gmpy' and '_mpmath_normalize' in dir(gmpy): + _normalize = gmpy._mpmath_normalize + _normalize1 = gmpy._mpmath_normalize + +if BACKEND == 'sage': + _normalize = _normalize1 = sage_utils.normalize + +if STRICT: + normalize = strict_normalize + normalize1 = strict_normalize1 +else: + normalize = _normalize + normalize1 = _normalize1 + +#----------------------------------------------------------------------------# +# Conversion functions # +#----------------------------------------------------------------------------# + +def from_man_exp(man, exp, prec=None, rnd=round_fast): + """Create raw mpf from (man, exp) pair. The mantissa may be signed. + If no precision is specified, the mantissa is stored exactly.""" + man = MPZ(man) + sign = 0 + if man < 0: + sign = 1 + man = -man + if man < 1024: + bc = bctable[int(man)] + else: + bc = bitcount(man) + if not prec: + if not man: + return fzero + if not man & 1: + if man & 2: + return (sign, man >> 1, exp + 1, bc - 1) + t = trailtable[int(man & 255)] + if not t: + while not man & 255: + man >>= 8 + exp += 8 + bc -= 8 + t = trailtable[int(man & 255)] + man >>= t + exp += t + bc -= t + return (sign, man, exp, bc) + return normalize(sign, man, exp, bc, prec, rnd) + +int_cache = dict((n, from_man_exp(n, 0)) for n in range(-10, 257)) + +if BACKEND == 'gmpy' and '_mpmath_create' in dir(gmpy): + from_man_exp = gmpy._mpmath_create + +if BACKEND == 'sage': + from_man_exp = sage_utils.from_man_exp + +def from_int(n, prec=0, rnd=round_fast): + """Create a raw mpf from an integer. If no precision is specified, + the mantissa is stored exactly.""" + if not prec: + if n in int_cache: + return int_cache[n] + return from_man_exp(n, 0, prec, rnd) + +def to_man_exp(s): + """Return (man, exp) of a raw mpf. Raise an error if inf/nan.""" + sign, man, exp, bc = s + if (not man) and exp: + raise ValueError("mantissa and exponent are undefined for %s" % man) + return man, exp + +def to_int(s, rnd=None): + """Convert a raw mpf to the nearest int. Rounding is done down by + default (same as int(float) in Python), but can be changed. If the + input is inf/nan, an exception is raised.""" + sign, man, exp, bc = s + if (not man) and exp: + raise ValueError("cannot convert inf or nan to int") + if exp >= 0: + if sign: + return (-man) << exp + return man << exp + # Make default rounding fast + if not rnd: + if sign: + return -(man >> (-exp)) + else: + return man >> (-exp) + if sign: + return round_int(-man, -exp, rnd) + else: + return round_int(man, -exp, rnd) + +def mpf_round_int(s, rnd): + sign, man, exp, bc = s + if (not man) and exp: + return s + if exp >= 0: + return s + mag = exp+bc + if mag < 1: + if rnd == round_ceiling: + if sign: return fzero + else: return fone + elif rnd == round_floor: + if sign: return fnone + else: return fzero + elif rnd == round_nearest: + if mag < 0 or man == MPZ_ONE: return fzero + elif sign: return fnone + else: return fone + else: + raise NotImplementedError + return mpf_pos(s, min(bc, mag), rnd) + +def mpf_floor(s, prec=0, rnd=round_fast): + v = mpf_round_int(s, round_floor) + if prec: + v = mpf_pos(v, prec, rnd) + return v + +def mpf_ceil(s, prec=0, rnd=round_fast): + v = mpf_round_int(s, round_ceiling) + if prec: + v = mpf_pos(v, prec, rnd) + return v + +def mpf_nint(s, prec=0, rnd=round_fast): + v = mpf_round_int(s, round_nearest) + if prec: + v = mpf_pos(v, prec, rnd) + return v + +def mpf_frac(s, prec=0, rnd=round_fast): + return mpf_sub(s, mpf_floor(s), prec, rnd) + +def from_float(x, prec=53, rnd=round_fast): + """Create a raw mpf from a Python float, rounding if necessary. + If prec >= 53, the result is guaranteed to represent exactly the + same number as the input. If prec is not specified, use prec=53.""" + # frexp only raises an exception for nan on some platforms + if x != x: + return fnan + # in Python2.5 math.frexp gives an exception for float infinity + # in Python2.6 it returns (float infinity, 0) + try: + m, e = math.frexp(x) + except: + if x == math_float_inf: return finf + if x == -math_float_inf: return fninf + return fnan + if x == math_float_inf: return finf + if x == -math_float_inf: return fninf + return from_man_exp(int(m*(1<<53)), e-53, prec, rnd) + +def from_npfloat(x, prec=113, rnd=round_fast): + """Create a raw mpf from a numpy float, rounding if necessary. + If prec >= 113, the result is guaranteed to represent exactly the + same number as the input. If prec is not specified, use prec=113.""" + y = float(x) + if x == y: # ldexp overflows for float16 + return from_float(y, prec, rnd) + import numpy as np + if np.isfinite(x): + m, e = np.frexp(x) + return from_man_exp(int(np.ldexp(m, 113)), int(e-113), prec, rnd) + if np.isposinf(x): return finf + if np.isneginf(x): return fninf + return fnan + +def from_Decimal(x, prec=None, rnd=round_fast): + """Create a raw mpf from a Decimal, rounding if necessary. + If prec is not specified, use the equivalent bit precision + of the number of significant digits in x.""" + if x.is_nan(): return fnan + if x.is_infinite(): return fninf if x.is_signed() else finf + if prec is None: + prec = int(len(x.as_tuple()[1])*3.3219280948873626) + return from_str(str(x), prec, rnd) + +def to_float(s, strict=False, rnd=round_fast): + """ + Convert a raw mpf to a Python float. The result is exact if the + bitcount of s is <= 53 and no underflow/overflow occurs. + + If the number is too large or too small to represent as a regular + float, it will be converted to inf or 0.0. Setting strict=True + forces an OverflowError to be raised instead. + + Warning: with a directed rounding mode, the correct nearest representable + floating-point number in the specified direction might not be computed + in case of overflow or (gradual) underflow. + """ + sign, man, exp, bc = s + if not man: + if s == fzero: return 0.0 + if s == finf: return math_float_inf + if s == fninf: return -math_float_inf + return math_float_inf/math_float_inf + if bc > 53: + sign, man, exp, bc = normalize1(sign, man, exp, bc, 53, rnd) + if sign: + man = -man + try: + return math.ldexp(man, exp) + except OverflowError: + if strict: + raise + # Overflow to infinity + if exp + bc > 0: + if sign: + return -math_float_inf + else: + return math_float_inf + # Underflow to zero + return 0.0 + +def from_rational(p, q, prec, rnd=round_fast): + """Create a raw mpf from a rational number p/q, round if + necessary.""" + return mpf_div(from_int(p), from_int(q), prec, rnd) + +def to_rational(s): + """Convert a raw mpf to a rational number. Return integers (p, q) + such that s = p/q exactly.""" + sign, man, exp, bc = s + if sign: + man = -man + if bc == -1: + raise ValueError("cannot convert %s to a rational number" % man) + if exp >= 0: + return man * (1<= 0: return (-man) << offset + else: return (-man) >> (-offset) + else: + if offset >= 0: return man << offset + else: return man >> (-offset) + + +############################################################################## +############################################################################## + +#----------------------------------------------------------------------------# +# Arithmetic operations, etc. # +#----------------------------------------------------------------------------# + +def mpf_rand(prec): + """Return a raw mpf chosen randomly from [0, 1), with prec bits + in the mantissa.""" + global getrandbits + if not getrandbits: + import random + getrandbits = random.getrandbits + return from_man_exp(getrandbits(prec), -prec, prec, round_floor) + +def mpf_eq(s, t): + """Test equality of two raw mpfs. This is simply tuple comparison + unless either number is nan, in which case the result is False.""" + if not s[1] or not t[1]: + if s == fnan or t == fnan: + return False + return s == t + +def mpf_hash(s): + # Duplicate the new hash algorithm introduces in Python 3.2. + if sys.version_info >= (3, 2): + ssign, sman, sexp, sbc = s + + # Handle special numbers + if not sman: + if s == fnan: return sys.hash_info.nan + if s == finf: return sys.hash_info.inf + if s == fninf: return -sys.hash_info.inf + h = sman % HASH_MODULUS + if sexp >= 0: + sexp = sexp % HASH_BITS + else: + sexp = HASH_BITS - 1 - ((-1 - sexp) % HASH_BITS) + h = (h << sexp) % HASH_MODULUS + if ssign: h = -h + if h == -1: h = -2 + return int(h) + else: + try: + # Try to be compatible with hash values for floats and ints + return hash(to_float(s, strict=1)) + except OverflowError: + # We must unfortunately sacrifice compatibility with ints here. + # We could do hash(man << exp) when the exponent is positive, but + # this would cause unreasonable inefficiency for large numbers. + return hash(s) + +def mpf_cmp(s, t): + """Compare the raw mpfs s and t. Return -1 if s < t, 0 if s == t, + and 1 if s > t. (Same convention as Python's cmp() function.)""" + + # In principle, a comparison amounts to determining the sign of s-t. + # A full subtraction is relatively slow, however, so we first try to + # look at the components. + ssign, sman, sexp, sbc = s + tsign, tman, texp, tbc = t + + # Handle zeros and special numbers + if not sman or not tman: + if s == fzero: return -mpf_sign(t) + if t == fzero: return mpf_sign(s) + if s == t: return 0 + # Follow same convention as Python's cmp for float nan + if t == fnan: return 1 + if s == finf: return 1 + if t == fninf: return 1 + return -1 + # Different sides of zero + if ssign != tsign: + if not ssign: return 1 + return -1 + # This reduces to direct integer comparison + if sexp == texp: + if sman == tman: + return 0 + if sman > tman: + if ssign: return -1 + else: return 1 + else: + if ssign: return 1 + else: return -1 + # Check position of the highest set bit in each number. If + # different, there is certainly an inequality. + a = sbc + sexp + b = tbc + texp + if ssign: + if a < b: return 1 + if a > b: return -1 + else: + if a < b: return -1 + if a > b: return 1 + + # Both numbers have the same highest bit. Subtract to find + # how the lower bits compare. + delta = mpf_sub(s, t, 5, round_floor) + if delta[0]: + return -1 + return 1 + +def mpf_lt(s, t): + if s == fnan or t == fnan: + return False + return mpf_cmp(s, t) < 0 + +def mpf_le(s, t): + if s == fnan or t == fnan: + return False + return mpf_cmp(s, t) <= 0 + +def mpf_gt(s, t): + if s == fnan or t == fnan: + return False + return mpf_cmp(s, t) > 0 + +def mpf_ge(s, t): + if s == fnan or t == fnan: + return False + return mpf_cmp(s, t) >= 0 + +def mpf_min_max(seq): + min = max = seq[0] + for x in seq[1:]: + if mpf_lt(x, min): min = x + if mpf_gt(x, max): max = x + return min, max + +def mpf_pos(s, prec=0, rnd=round_fast): + """Calculate 0+s for a raw mpf (i.e., just round s to the specified + precision).""" + if prec: + sign, man, exp, bc = s + if (not man) and exp: + return s + return normalize1(sign, man, exp, bc, prec, rnd) + return s + +def mpf_neg(s, prec=None, rnd=round_fast): + """Negate a raw mpf (return -s), rounding the result to the + specified precision. The prec argument can be omitted to do the + operation exactly.""" + sign, man, exp, bc = s + if not man: + if exp: + if s == finf: return fninf + if s == fninf: return finf + return s + if not prec: + return (1-sign, man, exp, bc) + return normalize1(1-sign, man, exp, bc, prec, rnd) + +def mpf_abs(s, prec=None, rnd=round_fast): + """Return abs(s) of the raw mpf s, rounded to the specified + precision. The prec argument can be omitted to generate an + exact result.""" + sign, man, exp, bc = s + if (not man) and exp: + if s == fninf: + return finf + return s + if not prec: + if sign: + return (0, man, exp, bc) + return s + return normalize1(0, man, exp, bc, prec, rnd) + +def mpf_sign(s): + """Return -1, 0, or 1 (as a Python int, not a raw mpf) depending on + whether s is negative, zero, or positive. (Nan is taken to give 0.)""" + sign, man, exp, bc = s + if not man: + if s == finf: return 1 + if s == fninf: return -1 + return 0 + return (-1) ** sign + +def mpf_add(s, t, prec=0, rnd=round_fast, _sub=0): + """ + Add the two raw mpf values s and t. + + With prec=0, no rounding is performed. Note that this can + produce a very large mantissa (potentially too large to fit + in memory) if exponents are far apart. + """ + ssign, sman, sexp, sbc = s + tsign, tman, texp, tbc = t + tsign ^= _sub + # Standard case: two nonzero, regular numbers + if sman and tman: + offset = sexp - texp + if offset: + if offset > 0: + # Outside precision range; only need to perturb + if offset > 100 and prec: + delta = sbc + sexp - tbc - texp + if delta > prec + 4: + offset = prec + 4 + sman <<= offset + if tsign == ssign: sman += 1 + else: sman -= 1 + return normalize1(ssign, sman, sexp-offset, + bitcount(sman), prec, rnd) + # Add + if ssign == tsign: + man = tman + (sman << offset) + # Subtract + else: + if ssign: man = tman - (sman << offset) + else: man = (sman << offset) - tman + if man >= 0: + ssign = 0 + else: + man = -man + ssign = 1 + bc = bitcount(man) + return normalize1(ssign, man, texp, bc, prec or bc, rnd) + elif offset < 0: + # Outside precision range; only need to perturb + if offset < -100 and prec: + delta = tbc + texp - sbc - sexp + if delta > prec + 4: + offset = prec + 4 + tman <<= offset + if ssign == tsign: tman += 1 + else: tman -= 1 + return normalize1(tsign, tman, texp-offset, + bitcount(tman), prec, rnd) + # Add + if ssign == tsign: + man = sman + (tman << -offset) + # Subtract + else: + if tsign: man = sman - (tman << -offset) + else: man = (tman << -offset) - sman + if man >= 0: + ssign = 0 + else: + man = -man + ssign = 1 + bc = bitcount(man) + return normalize1(ssign, man, sexp, bc, prec or bc, rnd) + # Equal exponents; no shifting necessary + if ssign == tsign: + man = tman + sman + else: + if ssign: man = tman - sman + else: man = sman - tman + if man >= 0: + ssign = 0 + else: + man = -man + ssign = 1 + bc = bitcount(man) + return normalize(ssign, man, texp, bc, prec or bc, rnd) + # Handle zeros and special numbers + if _sub: + t = mpf_neg(t) + if not sman: + if sexp: + if s == t or tman or not texp: + return s + return fnan + if tman: + return normalize1(tsign, tman, texp, tbc, prec or tbc, rnd) + return t + if texp: + return t + if sman: + return normalize1(ssign, sman, sexp, sbc, prec or sbc, rnd) + return s + +def mpf_sub(s, t, prec=0, rnd=round_fast): + """Return the difference of two raw mpfs, s-t. This function is + simply a wrapper of mpf_add that changes the sign of t.""" + return mpf_add(s, t, prec, rnd, 1) + +def mpf_sum(xs, prec=0, rnd=round_fast, absolute=False): + """ + Sum a list of mpf values efficiently and accurately + (typically no temporary roundoff occurs). If prec=0, + the final result will not be rounded either. + + There may be roundoff error or cancellation if extremely + large exponent differences occur. + + With absolute=True, sums the absolute values. + """ + man = 0 + exp = 0 + max_extra_prec = prec*2 or 1000000 # XXX + special = None + for x in xs: + xsign, xman, xexp, xbc = x + if xman: + if xsign and not absolute: + xman = -xman + delta = xexp - exp + if xexp >= exp: + # x much larger than existing sum? + # first: quick test + if (delta > max_extra_prec) and \ + ((not man) or delta-bitcount(abs(man)) > max_extra_prec): + man = xman + exp = xexp + else: + man += (xman << delta) + else: + delta = -delta + # x much smaller than existing sum? + if delta-xbc > max_extra_prec: + if not man: + man, exp = xman, xexp + else: + man = (man << delta) + xman + exp = xexp + elif xexp: + if absolute: + x = mpf_abs(x) + special = mpf_add(special or fzero, x, 1) + # Will be inf or nan + if special: + return special + return from_man_exp(man, exp, prec, rnd) + +def gmpy_mpf_mul(s, t, prec=0, rnd=round_fast): + """Multiply two raw mpfs""" + ssign, sman, sexp, sbc = s + tsign, tman, texp, tbc = t + sign = ssign ^ tsign + man = sman*tman + if man: + bc = bitcount(man) + if prec: + return normalize1(sign, man, sexp+texp, bc, prec, rnd) + else: + return (sign, man, sexp+texp, bc) + s_special = (not sman) and sexp + t_special = (not tman) and texp + if not s_special and not t_special: + return fzero + if fnan in (s, t): return fnan + if (not tman) and texp: s, t = t, s + if t == fzero: return fnan + return {1:finf, -1:fninf}[mpf_sign(s) * mpf_sign(t)] + +def gmpy_mpf_mul_int(s, n, prec, rnd=round_fast): + """Multiply by a Python integer.""" + sign, man, exp, bc = s + if not man: + return mpf_mul(s, from_int(n), prec, rnd) + if not n: + return fzero + if n < 0: + sign ^= 1 + n = -n + man *= n + return normalize(sign, man, exp, bitcount(man), prec, rnd) + +def python_mpf_mul(s, t, prec=0, rnd=round_fast): + """Multiply two raw mpfs""" + ssign, sman, sexp, sbc = s + tsign, tman, texp, tbc = t + sign = ssign ^ tsign + man = sman*tman + if man: + bc = sbc + tbc - 1 + bc += int(man>>bc) + if prec: + return normalize1(sign, man, sexp+texp, bc, prec, rnd) + else: + return (sign, man, sexp+texp, bc) + s_special = (not sman) and sexp + t_special = (not tman) and texp + if not s_special and not t_special: + return fzero + if fnan in (s, t): return fnan + if (not tman) and texp: s, t = t, s + if t == fzero: return fnan + return {1:finf, -1:fninf}[mpf_sign(s) * mpf_sign(t)] + +def python_mpf_mul_int(s, n, prec, rnd=round_fast): + """Multiply by a Python integer.""" + sign, man, exp, bc = s + if not man: + return mpf_mul(s, from_int(n), prec, rnd) + if not n: + return fzero + if n < 0: + sign ^= 1 + n = -n + man *= n + # Generally n will be small + if n < 1024: + bc += bctable[int(n)] - 1 + else: + bc += bitcount(n) - 1 + bc += int(man>>bc) + return normalize(sign, man, exp, bc, prec, rnd) + + +if BACKEND == 'gmpy': + mpf_mul = gmpy_mpf_mul + mpf_mul_int = gmpy_mpf_mul_int +else: + mpf_mul = python_mpf_mul + mpf_mul_int = python_mpf_mul_int + +def mpf_shift(s, n): + """Quickly multiply the raw mpf s by 2**n without rounding.""" + sign, man, exp, bc = s + if not man: + return s + return sign, man, exp+n, bc + +def mpf_frexp(x): + """Convert x = y*2**n to (y, n) with abs(y) in [0.5, 1) if nonzero""" + sign, man, exp, bc = x + if not man: + if x == fzero: + return (fzero, 0) + else: + raise ValueError + return mpf_shift(x, -bc-exp), bc+exp + +def mpf_div(s, t, prec, rnd=round_fast): + """Floating-point division""" + ssign, sman, sexp, sbc = s + tsign, tman, texp, tbc = t + if not sman or not tman: + if s == fzero: + if t == fzero: raise ZeroDivisionError + if t == fnan: return fnan + return fzero + if t == fzero: + raise ZeroDivisionError + s_special = (not sman) and sexp + t_special = (not tman) and texp + if s_special and t_special: + return fnan + if s == fnan or t == fnan: + return fnan + if not t_special: + if t == fzero: + return fnan + return {1:finf, -1:fninf}[mpf_sign(s) * mpf_sign(t)] + return fzero + sign = ssign ^ tsign + if tman == 1: + return normalize1(sign, sman, sexp-texp, sbc, prec, rnd) + # Same strategy as for addition: if there is a remainder, perturb + # the result a few bits outside the precision range before rounding + extra = prec - sbc + tbc + 5 + if extra < 5: + extra = 5 + quot, rem = divmod(sman< sexp+sbc: + return s + # Another important special case: this allows us to do e.g. x % 1.0 + # to find the fractional part of x, and it will work when x is huge. + if tman == 1 and sexp > texp+tbc: + return fzero + base = min(sexp, texp) + sman = (-1)**ssign * sman + tman = (-1)**tsign * tman + man = (sman << (sexp-base)) % (tman << (texp-base)) + if man >= 0: + sign = 0 + else: + man = -man + sign = 1 + return normalize(sign, man, base, bitcount(man), prec, rnd) + +reciprocal_rnd = { + round_down : round_up, + round_up : round_down, + round_floor : round_ceiling, + round_ceiling : round_floor, + round_nearest : round_nearest +} + +negative_rnd = { + round_down : round_down, + round_up : round_up, + round_floor : round_ceiling, + round_ceiling : round_floor, + round_nearest : round_nearest +} + +def mpf_pow_int(s, n, prec, rnd=round_fast): + """Compute s**n, where s is a raw mpf and n is a Python integer.""" + sign, man, exp, bc = s + + if (not man) and exp: + if s == finf: + if n > 0: return s + if n == 0: return fnan + return fzero + if s == fninf: + if n > 0: return [finf, fninf][n & 1] + if n == 0: return fnan + return fzero + return fnan + + n = int(n) + if n == 0: return fone + if n == 1: return mpf_pos(s, prec, rnd) + if n == 2: + _, man, exp, bc = s + if not man: + return fzero + man = man*man + if man == 1: + return (0, MPZ_ONE, exp+exp, 1) + bc = bc + bc - 2 + bc += bctable[int(man>>bc)] + return normalize1(0, man, exp+exp, bc, prec, rnd) + if n == -1: return mpf_div(fone, s, prec, rnd) + if n < 0: + inverse = mpf_pow_int(s, -n, prec+5, reciprocal_rnd[rnd]) + return mpf_div(fone, inverse, prec, rnd) + + result_sign = sign & n + + # Use exact integer power when the exact mantissa is small + if man == 1: + return (result_sign, MPZ_ONE, exp*n, 1) + if bc*n < 1000: + man **= n + return normalize1(result_sign, man, exp*n, bitcount(man), prec, rnd) + + # Use directed rounding all the way through to maintain rigorous + # bounds for interval arithmetic + rounds_down = (rnd == round_nearest) or \ + shifts_down[rnd][result_sign] + + # Now we perform binary exponentiation. Need to estimate precision + # to avoid rounding errors from temporary operations. Roughly log_2(n) + # operations are performed. + workprec = prec + 4*bitcount(n) + 4 + _, pm, pe, pbc = fone + while 1: + if n & 1: + pm = pm*man + pe = pe+exp + pbc += bc - 2 + pbc = pbc + bctable[int(pm >> pbc)] + if pbc > workprec: + if rounds_down: + pm = pm >> (pbc-workprec) + else: + pm = -((-pm) >> (pbc-workprec)) + pe += pbc - workprec + pbc = workprec + n -= 1 + if not n: + break + man = man*man + exp = exp+exp + bc = bc + bc - 2 + bc = bc + bctable[int(man >> bc)] + if bc > workprec: + if rounds_down: + man = man >> (bc-workprec) + else: + man = -((-man) >> (bc-workprec)) + exp += bc - workprec + bc = workprec + n = n // 2 + + return normalize(result_sign, pm, pe, pbc, prec, rnd) + + +def mpf_perturb(x, eps_sign, prec, rnd): + """ + For nonzero x, calculate x + eps with directed rounding, where + eps < prec relatively and eps has the given sign (0 for + positive, 1 for negative). + + With rounding to nearest, this is taken to simply normalize + x to the given precision. + """ + if rnd == round_nearest: + return mpf_pos(x, prec, rnd) + sign, man, exp, bc = x + eps = (eps_sign, MPZ_ONE, exp+bc-prec-1, 1) + if sign: + away = (rnd in (round_down, round_ceiling)) ^ eps_sign + else: + away = (rnd in (round_up, round_ceiling)) ^ eps_sign + if away: + return mpf_add(x, eps, prec, rnd) + else: + return mpf_pos(x, prec, rnd) + + +#----------------------------------------------------------------------------# +# Radix conversion # +#----------------------------------------------------------------------------# + +def to_digits_exp(s, dps): + """Helper function for representing the floating-point number s as + a decimal with dps digits. Returns (sign, string, exponent) where + sign is '' or '-', string is the digit string, and exponent is + the decimal exponent as an int. + + If inexact, the decimal representation is rounded toward zero.""" + + # Extract sign first so it doesn't mess up the string digit count + if s[0]: + sign = '-' + s = mpf_neg(s) + else: + sign = '' + _sign, man, exp, bc = s + + if not man: + return '', '0', 0 + + bitprec = int(dps * math.log(10,2)) + 10 + + # Cut down to size + # TODO: account for precision when doing this + exp_from_1 = exp + bc + if abs(exp_from_1) > 3500: + from .libelefun import mpf_ln2, mpf_ln10 + # Set b = int(exp * log(2)/log(10)) + # If exp is huge, we must use high-precision arithmetic to + # find the nearest power of ten + expprec = bitcount(abs(exp)) + 5 + tmp = from_int(exp) + tmp = mpf_mul(tmp, mpf_ln2(expprec)) + tmp = mpf_div(tmp, mpf_ln10(expprec), expprec) + b = to_int(tmp) + s = mpf_div(s, mpf_pow_int(ften, b, bitprec), bitprec) + _sign, man, exp, bc = s + exponent = b + else: + exponent = 0 + + # First, calculate mantissa digits by converting to a binary + # fixed-point number and then converting that number to + # a decimal fixed-point number. + fixprec = max(bitprec - exp - bc, 0) + fixdps = int(fixprec / math.log(10,2) + 0.5) + sf = to_fixed(s, fixprec) + sd = bin_to_radix(sf, fixprec, 10, fixdps) + digits = numeral(sd, base=10, size=dps) + + exponent += len(digits) - fixdps - 1 + return sign, digits, exponent + +def to_str(s, dps, strip_zeros=True, min_fixed=None, max_fixed=None, + show_zero_exponent=False): + """ + Convert a raw mpf to a decimal floating-point literal with at + most `dps` decimal digits in the mantissa (not counting extra zeros + that may be inserted for visual purposes). + + The number will be printed in fixed-point format if the position + of the leading digit is strictly between min_fixed + (default = min(-dps/3,-5)) and max_fixed (default = dps). + + To force fixed-point format always, set min_fixed = -inf, + max_fixed = +inf. To force floating-point format, set + min_fixed >= max_fixed. + + The literal is formatted so that it can be parsed back to a number + by to_str, float() or Decimal(). + """ + + # Special numbers + if not s[1]: + if s == fzero: + if dps: t = '0.0' + else: t = '.0' + if show_zero_exponent: + t += 'e+0' + return t + if s == finf: return '+inf' + if s == fninf: return '-inf' + if s == fnan: return 'nan' + raise ValueError + + if min_fixed is None: min_fixed = min(-(dps//3), -5) + if max_fixed is None: max_fixed = dps + + # to_digits_exp rounds to floor. + # This sometimes kills some instances of "...00001" + sign, digits, exponent = to_digits_exp(s, dps+3) + + # No digits: show only .0; round exponent to nearest + if not dps: + if digits[0] in '56789': + exponent += 1 + digits = ".0" + + else: + # Rounding up kills some instances of "...99999" + if len(digits) > dps and digits[dps] in '56789': + digits = digits[:dps] + i = dps - 1 + while i >= 0 and digits[i] == '9': + i -= 1 + if i >= 0: + digits = digits[:i] + str(int(digits[i]) + 1) + '0' * (dps - i - 1) + else: + digits = '1' + '0' * (dps - 1) + exponent += 1 + else: + digits = digits[:dps] + + # Prettify numbers close to unit magnitude + if min_fixed < exponent < max_fixed: + if exponent < 0: + digits = ("0"*int(-exponent)) + digits + split = 1 + else: + split = exponent + 1 + if split > dps: + digits += "0"*(split-dps) + exponent = 0 + else: + split = 1 + + digits = (digits[:split] + "." + digits[split:]) + + if strip_zeros: + # Clean up trailing zeros + digits = digits.rstrip('0') + if digits[-1] == ".": + digits += "0" + + if exponent == 0 and dps and not show_zero_exponent: return sign + digits + if exponent >= 0: return sign + digits + "e+" + str(exponent) + if exponent < 0: return sign + digits + "e" + str(exponent) + +def str_to_man_exp(x, base=10): + """Helper function for from_str.""" + x = x.lower().rstrip('l') + # Verify that the input is a valid float literal + float(x) + # Split into mantissa, exponent + parts = x.split('e') + if len(parts) == 1: + exp = 0 + else: # == 2 + x = parts[0] + exp = int(parts[1]) + # Look for radix point in mantissa + parts = x.split('.') + if len(parts) == 2: + a, b = parts[0], parts[1].rstrip('0') + exp -= len(b) + x = a + b + x = MPZ(int(x, base)) + return x, exp + +special_str = {'inf':finf, '+inf':finf, '-inf':fninf, 'nan':fnan} + +def from_str(x, prec, rnd=round_fast): + """Create a raw mpf from a decimal literal, rounding in the + specified direction if the input number cannot be represented + exactly as a binary floating-point number with the given number of + bits. The literal syntax accepted is the same as for Python + floats. + + TODO: the rounding does not work properly for large exponents. + """ + x = x.lower().strip() + if x in special_str: + return special_str[x] + + if '/' in x: + p, q = x.split('/') + p, q = p.rstrip('l'), q.rstrip('l') + return from_rational(int(p), int(q), prec, rnd) + + man, exp = str_to_man_exp(x, base=10) + + # XXX: appropriate cutoffs & track direction + # note no factors of 5 + if abs(exp) > 400: + s = from_int(man, prec+10) + s = mpf_mul(s, mpf_pow_int(ften, exp, prec+10), prec, rnd) + else: + if exp >= 0: + s = from_int(man * 10**exp, prec, rnd) + else: + s = from_rational(man, 10**-exp, prec, rnd) + return s + +# Binary string conversion. These are currently mainly used for debugging +# and could use some improvement in the future + +def from_bstr(x): + man, exp = str_to_man_exp(x, base=2) + man = MPZ(man) + sign = 0 + if man < 0: + man = -man + sign = 1 + bc = bitcount(man) + return normalize(sign, man, exp, bc, bc, round_floor) + +def to_bstr(x): + sign, man, exp, bc = x + return ['','-'][sign] + numeral(man, size=bitcount(man), base=2) + ("e%i" % exp) + + +#----------------------------------------------------------------------------# +# Square roots # +#----------------------------------------------------------------------------# + + +def mpf_sqrt(s, prec, rnd=round_fast): + """ + Compute the square root of a nonnegative mpf value. The + result is correctly rounded. + """ + sign, man, exp, bc = s + if sign: + raise ComplexResult("square root of a negative number") + if not man: + return s + if exp & 1: + exp -= 1 + man <<= 1 + bc += 1 + elif man == 1: + return normalize1(sign, man, exp//2, bc, prec, rnd) + shift = max(4, 2*prec-bc+4) + shift += shift & 1 + if rnd in 'fd': + man = isqrt(man<= 0: + a = mpf_pos(sa, prec, round_floor) + b = mpf_pos(sb, prec, round_ceiling) + # Upper point nonnegative? + elif sbs >= 0: + a = fzero + negsa = mpf_neg(sa) + if mpf_lt(negsa, sb): + b = mpf_pos(sb, prec, round_ceiling) + else: + b = mpf_pos(negsa, prec, round_ceiling) + # Both negative? + else: + a = mpf_neg(sb, prec, round_floor) + b = mpf_neg(sa, prec, round_ceiling) + return a, b + +# TODO: optimize +def mpi_mul_mpf(s, t, prec): + return mpi_mul(s, (t, t), prec) + +def mpi_div_mpf(s, t, prec): + return mpi_div(s, (t, t), prec) + +def mpi_mul(s, t, prec=0): + sa, sb = s + ta, tb = t + sas = mpf_sign(sa) + sbs = mpf_sign(sb) + tas = mpf_sign(ta) + tbs = mpf_sign(tb) + if sas == sbs == 0: + # Should maybe be undefined + if ta == fninf or tb == finf: + return fninf, finf + return fzero, fzero + if tas == tbs == 0: + # Should maybe be undefined + if sa == fninf or sb == finf: + return fninf, finf + return fzero, fzero + if sas >= 0: + # positive * positive + if tas >= 0: + a = mpf_mul(sa, ta, prec, round_floor) + b = mpf_mul(sb, tb, prec, round_ceiling) + if a == fnan: a = fzero + if b == fnan: b = finf + # positive * negative + elif tbs <= 0: + a = mpf_mul(sb, ta, prec, round_floor) + b = mpf_mul(sa, tb, prec, round_ceiling) + if a == fnan: a = fninf + if b == fnan: b = fzero + # positive * both signs + else: + a = mpf_mul(sb, ta, prec, round_floor) + b = mpf_mul(sb, tb, prec, round_ceiling) + if a == fnan: a = fninf + if b == fnan: b = finf + elif sbs <= 0: + # negative * positive + if tas >= 0: + a = mpf_mul(sa, tb, prec, round_floor) + b = mpf_mul(sb, ta, prec, round_ceiling) + if a == fnan: a = fninf + if b == fnan: b = fzero + # negative * negative + elif tbs <= 0: + a = mpf_mul(sb, tb, prec, round_floor) + b = mpf_mul(sa, ta, prec, round_ceiling) + if a == fnan: a = fzero + if b == fnan: b = finf + # negative * both signs + else: + a = mpf_mul(sa, tb, prec, round_floor) + b = mpf_mul(sa, ta, prec, round_ceiling) + if a == fnan: a = fninf + if b == fnan: b = finf + else: + # General case: perform all cross-multiplications and compare + # Since the multiplications can be done exactly, we need only + # do 4 (instead of 8: two for each rounding mode) + cases = [mpf_mul(sa, ta), mpf_mul(sa, tb), mpf_mul(sb, ta), mpf_mul(sb, tb)] + if fnan in cases: + a, b = (fninf, finf) + else: + a, b = mpf_min_max(cases) + a = mpf_pos(a, prec, round_floor) + b = mpf_pos(b, prec, round_ceiling) + return a, b + +def mpi_square(s, prec=0): + sa, sb = s + if mpf_ge(sa, fzero): + a = mpf_mul(sa, sa, prec, round_floor) + b = mpf_mul(sb, sb, prec, round_ceiling) + elif mpf_le(sb, fzero): + a = mpf_mul(sb, sb, prec, round_floor) + b = mpf_mul(sa, sa, prec, round_ceiling) + else: + sa = mpf_neg(sa) + sa, sb = mpf_min_max([sa, sb]) + a = fzero + b = mpf_mul(sb, sb, prec, round_ceiling) + return a, b + +def mpi_div(s, t, prec): + sa, sb = s + ta, tb = t + sas = mpf_sign(sa) + sbs = mpf_sign(sb) + tas = mpf_sign(ta) + tbs = mpf_sign(tb) + # 0 / X + if sas == sbs == 0: + # 0 / + if (tas < 0 and tbs > 0) or (tas == 0 or tbs == 0): + return fninf, finf + return fzero, fzero + # Denominator contains both negative and positive numbers; + # this should properly be a multi-interval, but the closest + # match is the entire (extended) real line + if tas < 0 and tbs > 0: + return fninf, finf + # Assume denominator to be nonnegative + if tas < 0: + return mpi_div(mpi_neg(s), mpi_neg(t), prec) + # Division by zero + # XXX: make sure all results make sense + if tas == 0: + # Numerator contains both signs? + if sas < 0 and sbs > 0: + return fninf, finf + if tas == tbs: + return fninf, finf + # Numerator positive? + if sas >= 0: + a = mpf_div(sa, tb, prec, round_floor) + b = finf + if sbs <= 0: + a = fninf + b = mpf_div(sb, tb, prec, round_ceiling) + # Division with positive denominator + # We still have to handle nans resulting from inf/0 or inf/inf + else: + # Nonnegative numerator + if sas >= 0: + a = mpf_div(sa, tb, prec, round_floor) + b = mpf_div(sb, ta, prec, round_ceiling) + if a == fnan: a = fzero + if b == fnan: b = finf + # Nonpositive numerator + elif sbs <= 0: + a = mpf_div(sa, ta, prec, round_floor) + b = mpf_div(sb, tb, prec, round_ceiling) + if a == fnan: a = fninf + if b == fnan: b = fzero + # Numerator contains both signs? + else: + a = mpf_div(sa, ta, prec, round_floor) + b = mpf_div(sb, ta, prec, round_ceiling) + if a == fnan: a = fninf + if b == fnan: b = finf + return a, b + +def mpi_pi(prec): + a = mpf_pi(prec, round_floor) + b = mpf_pi(prec, round_ceiling) + return a, b + +def mpi_exp(s, prec): + sa, sb = s + # exp is monotonic + a = mpf_exp(sa, prec, round_floor) + b = mpf_exp(sb, prec, round_ceiling) + return a, b + +def mpi_log(s, prec): + sa, sb = s + # log is monotonic + a = mpf_log(sa, prec, round_floor) + b = mpf_log(sb, prec, round_ceiling) + return a, b + +def mpi_sqrt(s, prec): + sa, sb = s + # sqrt is monotonic + a = mpf_sqrt(sa, prec, round_floor) + b = mpf_sqrt(sb, prec, round_ceiling) + return a, b + +def mpi_atan(s, prec): + sa, sb = s + a = mpf_atan(sa, prec, round_floor) + b = mpf_atan(sb, prec, round_ceiling) + return a, b + +def mpi_pow_int(s, n, prec): + sa, sb = s + if n < 0: + return mpi_div((fone, fone), mpi_pow_int(s, -n, prec+20), prec) + if n == 0: + return (fone, fone) + if n == 1: + return s + if n == 2: + return mpi_square(s, prec) + # Odd -- signs are preserved + if n & 1: + a = mpf_pow_int(sa, n, prec, round_floor) + b = mpf_pow_int(sb, n, prec, round_ceiling) + # Even -- important to ensure positivity + else: + sas = mpf_sign(sa) + sbs = mpf_sign(sb) + # Nonnegative? + if sas >= 0: + a = mpf_pow_int(sa, n, prec, round_floor) + b = mpf_pow_int(sb, n, prec, round_ceiling) + # Nonpositive? + elif sbs <= 0: + a = mpf_pow_int(sb, n, prec, round_floor) + b = mpf_pow_int(sa, n, prec, round_ceiling) + # Mixed signs? + else: + a = fzero + # max(-a,b)**n + sa = mpf_neg(sa) + if mpf_ge(sa, sb): + b = mpf_pow_int(sa, n, prec, round_ceiling) + else: + b = mpf_pow_int(sb, n, prec, round_ceiling) + return a, b + +def mpi_pow(s, t, prec): + ta, tb = t + if ta == tb and ta not in (finf, fninf): + if ta == from_int(to_int(ta)): + return mpi_pow_int(s, to_int(ta), prec) + if ta == fhalf: + return mpi_sqrt(s, prec) + u = mpi_log(s, prec + 20) + v = mpi_mul(u, t, prec + 20) + return mpi_exp(v, prec) + +def MIN(x, y): + if mpf_le(x, y): + return x + return y + +def MAX(x, y): + if mpf_ge(x, y): + return x + return y + +def cos_sin_quadrant(x, wp): + sign, man, exp, bc = x + if x == fzero: + return fone, fzero, 0 + # TODO: combine evaluation code to avoid duplicate modulo + c, s = mpf_cos_sin(x, wp) + t, n, wp_ = mod_pi2(man, exp, exp+bc, 15) + if sign: + n = -1-n + return c, s, n + +def mpi_cos_sin(x, prec): + a, b = x + if a == b == fzero: + return (fone, fone), (fzero, fzero) + # Guaranteed to contain both -1 and 1 + if (finf in x) or (fninf in x): + return (fnone, fone), (fnone, fone) + wp = prec + 20 + ca, sa, na = cos_sin_quadrant(a, wp) + cb, sb, nb = cos_sin_quadrant(b, wp) + ca, cb = mpf_min_max([ca, cb]) + sa, sb = mpf_min_max([sa, sb]) + # Both functions are monotonic within one quadrant + if na == nb: + pass + # Guaranteed to contain both -1 and 1 + elif nb - na >= 4: + return (fnone, fone), (fnone, fone) + else: + # cos has maximum between a and b + if na//4 != nb//4: + cb = fone + # cos has minimum + if (na-2)//4 != (nb-2)//4: + ca = fnone + # sin has maximum + if (na-1)//4 != (nb-1)//4: + sb = fone + # sin has minimum + if (na-3)//4 != (nb-3)//4: + sa = fnone + # Perturb to force interval rounding + more = from_man_exp((MPZ_ONE<= 1: + if sign: + return fnone + return fone + return v + ca = finalize(ca, round_floor) + cb = finalize(cb, round_ceiling) + sa = finalize(sa, round_floor) + sb = finalize(sb, round_ceiling) + return (ca,cb), (sa,sb) + +def mpi_cos(x, prec): + return mpi_cos_sin(x, prec)[0] + +def mpi_sin(x, prec): + return mpi_cos_sin(x, prec)[1] + +def mpi_tan(x, prec): + cos, sin = mpi_cos_sin(x, prec+20) + return mpi_div(sin, cos, prec) + +def mpi_cot(x, prec): + cos, sin = mpi_cos_sin(x, prec+20) + return mpi_div(cos, sin, prec) + +def mpi_from_str_a_b(x, y, percent, prec): + wp = prec + 20 + xa = from_str(x, wp, round_floor) + xb = from_str(x, wp, round_ceiling) + #ya = from_str(y, wp, round_floor) + y = from_str(y, wp, round_ceiling) + assert mpf_ge(y, fzero) + if percent: + y = mpf_mul(MAX(mpf_abs(xa), mpf_abs(xb)), y, wp, round_ceiling) + y = mpf_div(y, from_int(100), wp, round_ceiling) + a = mpf_sub(xa, y, prec, round_floor) + b = mpf_add(xb, y, prec, round_ceiling) + return a, b + +def mpi_from_str(s, prec): + """ + Parse an interval number given as a string. + + Allowed forms are + + "-1.23e-27" + Any single decimal floating-point literal. + "a +- b" or "a (b)" + a is the midpoint of the interval and b is the half-width + "a +- b%" or "a (b%)" + a is the midpoint of the interval and the half-width + is b percent of a (`a \times b / 100`). + "[a, b]" + The interval indicated directly. + "x[y,z]e" + x are shared digits, y and z are unequal digits, e is the exponent. + + """ + e = ValueError("Improperly formed interval number '%s'" % s) + s = s.replace(" ", "") + wp = prec + 20 + if "+-" in s: + x, y = s.split("+-") + return mpi_from_str_a_b(x, y, False, prec) + # case 2 + elif "(" in s: + # Don't confuse with a complex number (x,y) + if s[0] == "(" or ")" not in s: + raise e + s = s.replace(")", "") + percent = False + if "%" in s: + if s[-1] != "%": + raise e + percent = True + s = s.replace("%", "") + x, y = s.split("(") + return mpi_from_str_a_b(x, y, percent, prec) + elif "," in s: + if ('[' not in s) or (']' not in s): + raise e + if s[0] == '[': + # case 3 + s = s.replace("[", "") + s = s.replace("]", "") + a, b = s.split(",") + a = from_str(a, prec, round_floor) + b = from_str(b, prec, round_ceiling) + return a, b + else: + # case 4 + x, y = s.split('[') + y, z = y.split(',') + if 'e' in s: + z, e = z.split(']') + else: + z, e = z.rstrip(']'), '' + a = from_str(x+y+e, prec, round_floor) + b = from_str(x+z+e, prec, round_ceiling) + return a, b + else: + a = from_str(s, prec, round_floor) + b = from_str(s, prec, round_ceiling) + return a, b + +def mpi_to_str(x, dps, use_spaces=True, brackets='[]', mode='brackets', error_dps=4, **kwargs): + """ + Convert a mpi interval to a string. + + **Arguments** + + *dps* + decimal places to use for printing + *use_spaces* + use spaces for more readable output, defaults to true + *brackets* + pair of strings (or two-character string) giving left and right brackets + *mode* + mode of display: 'plusminus', 'percent', 'brackets' (default) or 'diff' + *error_dps* + limit the error to *error_dps* digits (mode 'plusminus and 'percent') + + Additional keyword arguments are forwarded to the mpf-to-string conversion + for the components of the output. + + **Examples** + + >>> from mpmath import mpi, mp + >>> mp.dps = 30 + >>> x = mpi(1, 2)._mpi_ + >>> mpi_to_str(x, 2, mode='plusminus') + '1.5 +- 0.5' + >>> mpi_to_str(x, 2, mode='percent') + '1.5 (33.33%)' + >>> mpi_to_str(x, 2, mode='brackets') + '[1.0, 2.0]' + >>> mpi_to_str(x, 2, mode='brackets' , brackets=('<', '>')) + '<1.0, 2.0>' + >>> x = mpi('5.2582327113062393041', '5.2582327113062749951')._mpi_ + >>> mpi_to_str(x, 15, mode='diff') + '5.2582327113062[4, 7]' + >>> mpi_to_str(mpi(0)._mpi_, 2, mode='percent') + '0.0 (0.0%)' + + """ + prec = dps_to_prec(dps) + wp = prec + 20 + a, b = x + mid = mpi_mid(x, prec) + delta = mpi_delta(x, prec) + a_str = to_str(a, dps, **kwargs) + b_str = to_str(b, dps, **kwargs) + mid_str = to_str(mid, dps, **kwargs) + sp = "" + if use_spaces: + sp = " " + br1, br2 = brackets + if mode == 'plusminus': + delta_str = to_str(mpf_shift(delta,-1), dps, **kwargs) + s = mid_str + sp + "+-" + sp + delta_str + elif mode == 'percent': + if mid == fzero: + p = fzero + else: + # p = 100 * delta(x) / (2*mid(x)) + p = mpf_mul(delta, from_int(100)) + p = mpf_div(p, mpf_mul(mid, from_int(2)), wp) + s = mid_str + sp + "(" + to_str(p, error_dps) + "%)" + elif mode == 'brackets': + s = br1 + a_str + "," + sp + b_str + br2 + elif mode == 'diff': + # use more digits if str(x.a) and str(x.b) are equal + if a_str == b_str: + a_str = to_str(a, dps+3, **kwargs) + b_str = to_str(b, dps+3, **kwargs) + # separate mantissa and exponent + a = a_str.split('e') + if len(a) == 1: + a.append('') + b = b_str.split('e') + if len(b) == 1: + b.append('') + if a[1] == b[1]: + if a[0] != b[0]: + for i in xrange(len(a[0]) + 1): + if a[0][i] != b[0][i]: + break + s = (a[0][:i] + br1 + a[0][i:] + ',' + sp + b[0][i:] + br2 + + 'e'*min(len(a[1]), 1) + a[1]) + else: # no difference + s = a[0] + br1 + br2 + 'e'*min(len(a[1]), 1) + a[1] + else: + s = br1 + 'e'.join(a) + ',' + sp + 'e'.join(b) + br2 + else: + raise ValueError("'%s' is unknown mode for printing mpi" % mode) + return s + +def mpci_add(x, y, prec): + a, b = x + c, d = y + return mpi_add(a, c, prec), mpi_add(b, d, prec) + +def mpci_sub(x, y, prec): + a, b = x + c, d = y + return mpi_sub(a, c, prec), mpi_sub(b, d, prec) + +def mpci_neg(x, prec=0): + a, b = x + return mpi_neg(a, prec), mpi_neg(b, prec) + +def mpci_pos(x, prec): + a, b = x + return mpi_pos(a, prec), mpi_pos(b, prec) + +def mpci_mul(x, y, prec): + # TODO: optimize for real/imag cases + a, b = x + c, d = y + r1 = mpi_mul(a,c) + r2 = mpi_mul(b,d) + re = mpi_sub(r1,r2,prec) + i1 = mpi_mul(a,d) + i2 = mpi_mul(b,c) + im = mpi_add(i1,i2,prec) + return re, im + +def mpci_div(x, y, prec): + # TODO: optimize for real/imag cases + a, b = x + c, d = y + wp = prec+20 + m1 = mpi_square(c) + m2 = mpi_square(d) + m = mpi_add(m1,m2,wp) + re = mpi_add(mpi_mul(a,c), mpi_mul(b,d), wp) + im = mpi_sub(mpi_mul(b,c), mpi_mul(a,d), wp) + re = mpi_div(re, m, prec) + im = mpi_div(im, m, prec) + return re, im + +def mpci_exp(x, prec): + a, b = x + wp = prec+20 + r = mpi_exp(a, wp) + c, s = mpi_cos_sin(b, wp) + a = mpi_mul(r, c, prec) + b = mpi_mul(r, s, prec) + return a, b + +def mpi_shift(x, n): + a, b = x + return mpf_shift(a,n), mpf_shift(b,n) + +def mpi_cosh_sinh(x, prec): + # TODO: accuracy for small x + wp = prec+20 + e1 = mpi_exp(x, wp) + e2 = mpi_div(mpi_one, e1, wp) + c = mpi_add(e1, e2, prec) + s = mpi_sub(e1, e2, prec) + c = mpi_shift(c, -1) + s = mpi_shift(s, -1) + return c, s + +def mpci_cos(x, prec): + a, b = x + wp = prec+10 + c, s = mpi_cos_sin(a, wp) + ch, sh = mpi_cosh_sinh(b, wp) + re = mpi_mul(c, ch, prec) + im = mpi_mul(s, sh, prec) + return re, mpi_neg(im) + +def mpci_sin(x, prec): + a, b = x + wp = prec+10 + c, s = mpi_cos_sin(a, wp) + ch, sh = mpi_cosh_sinh(b, wp) + re = mpi_mul(s, ch, prec) + im = mpi_mul(c, sh, prec) + return re, im + +def mpci_abs(x, prec): + a, b = x + if a == mpi_zero: + return mpi_abs(b) + if b == mpi_zero: + return mpi_abs(a) + # Important: nonnegative + a = mpi_square(a) + b = mpi_square(b) + t = mpi_add(a, b, prec+20) + return mpi_sqrt(t, prec) + +def mpi_atan2(y, x, prec): + ya, yb = y + xa, xb = x + # Constrained to the real line + if ya == yb == fzero: + if mpf_ge(xa, fzero): + return mpi_zero + return mpi_pi(prec) + # Right half-plane + if mpf_ge(xa, fzero): + if mpf_ge(ya, fzero): + a = mpf_atan2(ya, xb, prec, round_floor) + else: + a = mpf_atan2(ya, xa, prec, round_floor) + if mpf_ge(yb, fzero): + b = mpf_atan2(yb, xa, prec, round_ceiling) + else: + b = mpf_atan2(yb, xb, prec, round_ceiling) + # Upper half-plane + elif mpf_ge(ya, fzero): + b = mpf_atan2(ya, xa, prec, round_ceiling) + if mpf_le(xb, fzero): + a = mpf_atan2(yb, xb, prec, round_floor) + else: + a = mpf_atan2(ya, xb, prec, round_floor) + # Lower half-plane + elif mpf_le(yb, fzero): + a = mpf_atan2(yb, xa, prec, round_floor) + if mpf_le(xb, fzero): + b = mpf_atan2(ya, xb, prec, round_ceiling) + else: + b = mpf_atan2(yb, xb, prec, round_ceiling) + # Covering the origin + else: + b = mpf_pi(prec, round_ceiling) + a = mpf_neg(b) + return a, b + +def mpci_arg(z, prec): + x, y = z + return mpi_atan2(y, x, prec) + +def mpci_log(z, prec): + x, y = z + re = mpi_log(mpci_abs(z, prec+20), prec) + im = mpci_arg(z, prec) + return re, im + +def mpci_pow(x, y, prec): + # TODO: recognize/speed up real cases, integer y + yre, yim = y + if yim == mpi_zero: + ya, yb = yre + if ya == yb: + sign, man, exp, bc = yb + if man and exp >= 0: + return mpci_pow_int(x, (-1)**sign * int(man<>= 1 + return mpci_pos(result, prec) + +gamma_min_a = from_float(1.46163214496) +gamma_min_b = from_float(1.46163214497) +gamma_min = (gamma_min_a, gamma_min_b) +gamma_mono_imag_a = from_float(-1.1) +gamma_mono_imag_b = from_float(1.1) + +def mpi_overlap(x, y): + a, b = x + c, d = y + if mpf_lt(d, a): return False + if mpf_gt(c, b): return False + return True + +# type = 0 -- gamma +# type = 1 -- factorial +# type = 2 -- 1/gamma +# type = 3 -- log-gamma + +def mpi_gamma(z, prec, type=0): + a, b = z + wp = prec+20 + + if type == 1: + return mpi_gamma(mpi_add(z, mpi_one, wp), prec, 0) + + # increasing + if mpf_gt(a, gamma_min_b): + if type == 0: + c = mpf_gamma(a, prec, round_floor) + d = mpf_gamma(b, prec, round_ceiling) + elif type == 2: + c = mpf_rgamma(b, prec, round_floor) + d = mpf_rgamma(a, prec, round_ceiling) + elif type == 3: + c = mpf_loggamma(a, prec, round_floor) + d = mpf_loggamma(b, prec, round_ceiling) + # decreasing + elif mpf_gt(a, fzero) and mpf_lt(b, gamma_min_a): + if type == 0: + c = mpf_gamma(b, prec, round_floor) + d = mpf_gamma(a, prec, round_ceiling) + elif type == 2: + c = mpf_rgamma(a, prec, round_floor) + d = mpf_rgamma(b, prec, round_ceiling) + elif type == 3: + c = mpf_loggamma(b, prec, round_floor) + d = mpf_loggamma(a, prec, round_ceiling) + else: + # TODO: reflection formula + znew = mpi_add(z, mpi_one, wp) + if type == 0: return mpi_div(mpi_gamma(znew, prec+2, 0), z, prec) + if type == 2: return mpi_mul(mpi_gamma(znew, prec+2, 2), z, prec) + if type == 3: return mpi_sub(mpi_gamma(znew, prec+2, 3), mpi_log(z, prec+2), prec) + return c, d + +def mpci_gamma(z, prec, type=0): + (a1,a2), (b1,b2) = z + + # Real case + if b1 == b2 == fzero and (type != 3 or mpf_gt(a1,fzero)): + return mpi_gamma(z, prec, type), mpi_zero + + # Estimate precision + wp = prec+20 + if type != 3: + amag = a2[2]+a2[3] + bmag = b2[2]+b2[3] + if a2 != fzero: + mag = max(amag, bmag) + else: + mag = bmag + an = abs(to_int(a2)) + bn = abs(to_int(b2)) + absn = max(an, bn) + gamma_size = max(0,absn*mag) + wp += bitcount(gamma_size) + + # Assume type != 1 + if type == 1: + (a1,a2) = mpi_add((a1,a2), mpi_one, wp); z = (a1,a2), (b1,b2) + type = 0 + + # Avoid non-monotonic region near the negative real axis + if mpf_lt(a1, gamma_min_b): + if mpi_overlap((b1,b2), (gamma_mono_imag_a, gamma_mono_imag_b)): + # TODO: reflection formula + #if mpf_lt(a2, mpf_shift(fone,-1)): + # znew = mpci_sub((mpi_one,mpi_zero),z,wp) + # ... + # Recurrence: + # gamma(z) = gamma(z+1)/z + znew = mpi_add((a1,a2), mpi_one, wp), (b1,b2) + if type == 0: return mpci_div(mpci_gamma(znew, prec+2, 0), z, prec) + if type == 2: return mpci_mul(mpci_gamma(znew, prec+2, 2), z, prec) + if type == 3: return mpci_sub(mpci_gamma(znew, prec+2, 3), mpci_log(z,prec+2), prec) + + # Use monotonicity (except for a small region close to the + # origin and near poles) + # upper half-plane + if mpf_ge(b1, fzero): + minre = mpc_loggamma((a1,b2), wp, round_floor) + maxre = mpc_loggamma((a2,b1), wp, round_ceiling) + minim = mpc_loggamma((a1,b1), wp, round_floor) + maxim = mpc_loggamma((a2,b2), wp, round_ceiling) + # lower half-plane + elif mpf_le(b2, fzero): + minre = mpc_loggamma((a1,b1), wp, round_floor) + maxre = mpc_loggamma((a2,b2), wp, round_ceiling) + minim = mpc_loggamma((a2,b1), wp, round_floor) + maxim = mpc_loggamma((a1,b2), wp, round_ceiling) + # crosses real axis + else: + maxre = mpc_loggamma((a2,fzero), wp, round_ceiling) + # stretches more into the lower half-plane + if mpf_gt(mpf_neg(b1), b2): + minre = mpc_loggamma((a1,b1), wp, round_ceiling) + else: + minre = mpc_loggamma((a1,b2), wp, round_ceiling) + minim = mpc_loggamma((a2,b1), wp, round_floor) + maxim = mpc_loggamma((a2,b2), wp, round_floor) + + w = (minre[0], maxre[0]), (minim[1], maxim[1]) + if type == 3: + return mpi_pos(w[0], prec), mpi_pos(w[1], prec) + if type == 2: + w = mpci_neg(w) + return mpci_exp(w, prec) + +def mpi_loggamma(z, prec): return mpi_gamma(z, prec, type=3) +def mpci_loggamma(z, prec): return mpci_gamma(z, prec, type=3) + +def mpi_rgamma(z, prec): return mpi_gamma(z, prec, type=2) +def mpci_rgamma(z, prec): return mpci_gamma(z, prec, type=2) + +def mpi_factorial(z, prec): return mpi_gamma(z, prec, type=1) +def mpci_factorial(z, prec): return mpci_gamma(z, prec, type=1) diff --git a/lib/python3.12/site-packages/mpmath/math2.py b/lib/python3.12/site-packages/mpmath/math2.py new file mode 100644 index 0000000000000000000000000000000000000000..302e25f509c18b2c76a2b62611f2765db84ab13e --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/math2.py @@ -0,0 +1,672 @@ +""" +This module complements the math and cmath builtin modules by providing +fast machine precision versions of some additional functions (gamma, ...) +and wrapping math/cmath functions so that they can be called with either +real or complex arguments. +""" + +import operator +import math +import cmath + +# Irrational (?) constants +pi = 3.1415926535897932385 +e = 2.7182818284590452354 +sqrt2 = 1.4142135623730950488 +sqrt5 = 2.2360679774997896964 +phi = 1.6180339887498948482 +ln2 = 0.69314718055994530942 +ln10 = 2.302585092994045684 +euler = 0.57721566490153286061 +catalan = 0.91596559417721901505 +khinchin = 2.6854520010653064453 +apery = 1.2020569031595942854 + +logpi = 1.1447298858494001741 + +def _mathfun_real(f_real, f_complex): + def f(x, **kwargs): + if type(x) is float: + return f_real(x) + if type(x) is complex: + return f_complex(x) + try: + x = float(x) + return f_real(x) + except (TypeError, ValueError): + x = complex(x) + return f_complex(x) + f.__name__ = f_real.__name__ + return f + +def _mathfun(f_real, f_complex): + def f(x, **kwargs): + if type(x) is complex: + return f_complex(x) + try: + return f_real(float(x)) + except (TypeError, ValueError): + return f_complex(complex(x)) + f.__name__ = f_real.__name__ + return f + +def _mathfun_n(f_real, f_complex): + def f(*args, **kwargs): + try: + return f_real(*(float(x) for x in args)) + except (TypeError, ValueError): + return f_complex(*(complex(x) for x in args)) + f.__name__ = f_real.__name__ + return f + +# Workaround for non-raising log and sqrt in Python 2.5 and 2.4 +# on Unix system +try: + math.log(-2.0) + def math_log(x): + if x <= 0.0: + raise ValueError("math domain error") + return math.log(x) + def math_sqrt(x): + if x < 0.0: + raise ValueError("math domain error") + return math.sqrt(x) +except (ValueError, TypeError): + math_log = math.log + math_sqrt = math.sqrt + +pow = _mathfun_n(operator.pow, lambda x, y: complex(x)**y) +log = _mathfun_n(math_log, cmath.log) +sqrt = _mathfun(math_sqrt, cmath.sqrt) +exp = _mathfun_real(math.exp, cmath.exp) + +cos = _mathfun_real(math.cos, cmath.cos) +sin = _mathfun_real(math.sin, cmath.sin) +tan = _mathfun_real(math.tan, cmath.tan) + +acos = _mathfun(math.acos, cmath.acos) +asin = _mathfun(math.asin, cmath.asin) +atan = _mathfun_real(math.atan, cmath.atan) + +cosh = _mathfun_real(math.cosh, cmath.cosh) +sinh = _mathfun_real(math.sinh, cmath.sinh) +tanh = _mathfun_real(math.tanh, cmath.tanh) + +floor = _mathfun_real(math.floor, + lambda z: complex(math.floor(z.real), math.floor(z.imag))) +ceil = _mathfun_real(math.ceil, + lambda z: complex(math.ceil(z.real), math.ceil(z.imag))) + + +cos_sin = _mathfun_real(lambda x: (math.cos(x), math.sin(x)), + lambda z: (cmath.cos(z), cmath.sin(z))) + +cbrt = _mathfun(lambda x: x**(1./3), lambda z: z**(1./3)) + +def nthroot(x, n): + r = 1./n + try: + return float(x) ** r + except (ValueError, TypeError): + return complex(x) ** r + +def _sinpi_real(x): + if x < 0: + return -_sinpi_real(-x) + n, r = divmod(x, 0.5) + r *= pi + n %= 4 + if n == 0: return math.sin(r) + if n == 1: return math.cos(r) + if n == 2: return -math.sin(r) + if n == 3: return -math.cos(r) + +def _cospi_real(x): + if x < 0: + x = -x + n, r = divmod(x, 0.5) + r *= pi + n %= 4 + if n == 0: return math.cos(r) + if n == 1: return -math.sin(r) + if n == 2: return -math.cos(r) + if n == 3: return math.sin(r) + +def _sinpi_complex(z): + if z.real < 0: + return -_sinpi_complex(-z) + n, r = divmod(z.real, 0.5) + z = pi*complex(r, z.imag) + n %= 4 + if n == 0: return cmath.sin(z) + if n == 1: return cmath.cos(z) + if n == 2: return -cmath.sin(z) + if n == 3: return -cmath.cos(z) + +def _cospi_complex(z): + if z.real < 0: + z = -z + n, r = divmod(z.real, 0.5) + z = pi*complex(r, z.imag) + n %= 4 + if n == 0: return cmath.cos(z) + if n == 1: return -cmath.sin(z) + if n == 2: return -cmath.cos(z) + if n == 3: return cmath.sin(z) + +cospi = _mathfun_real(_cospi_real, _cospi_complex) +sinpi = _mathfun_real(_sinpi_real, _sinpi_complex) + +def tanpi(x): + try: + return sinpi(x) / cospi(x) + except OverflowError: + if complex(x).imag > 10: + return 1j + if complex(x).imag < 10: + return -1j + raise + +def cotpi(x): + try: + return cospi(x) / sinpi(x) + except OverflowError: + if complex(x).imag > 10: + return -1j + if complex(x).imag < 10: + return 1j + raise + +INF = 1e300*1e300 +NINF = -INF +NAN = INF-INF +EPS = 2.2204460492503131e-16 + +_exact_gamma = (INF, 1.0, 1.0, 2.0, 6.0, 24.0, 120.0, 720.0, 5040.0, 40320.0, + 362880.0, 3628800.0, 39916800.0, 479001600.0, 6227020800.0, 87178291200.0, + 1307674368000.0, 20922789888000.0, 355687428096000.0, 6402373705728000.0, + 121645100408832000.0, 2432902008176640000.0) + +_max_exact_gamma = len(_exact_gamma)-1 + +# Lanczos coefficients used by the GNU Scientific Library +_lanczos_g = 7 +_lanczos_p = (0.99999999999980993, 676.5203681218851, -1259.1392167224028, + 771.32342877765313, -176.61502916214059, 12.507343278686905, + -0.13857109526572012, 9.9843695780195716e-6, 1.5056327351493116e-7) + +def _gamma_real(x): + _intx = int(x) + if _intx == x: + if _intx <= 0: + #return (-1)**_intx * INF + raise ZeroDivisionError("gamma function pole") + if _intx <= _max_exact_gamma: + return _exact_gamma[_intx] + if x < 0.5: + # TODO: sinpi + return pi / (_sinpi_real(x)*_gamma_real(1-x)) + else: + x -= 1.0 + r = _lanczos_p[0] + for i in range(1, _lanczos_g+2): + r += _lanczos_p[i]/(x+i) + t = x + _lanczos_g + 0.5 + return 2.506628274631000502417 * t**(x+0.5) * math.exp(-t) * r + +def _gamma_complex(x): + if not x.imag: + return complex(_gamma_real(x.real)) + if x.real < 0.5: + # TODO: sinpi + return pi / (_sinpi_complex(x)*_gamma_complex(1-x)) + else: + x -= 1.0 + r = _lanczos_p[0] + for i in range(1, _lanczos_g+2): + r += _lanczos_p[i]/(x+i) + t = x + _lanczos_g + 0.5 + return 2.506628274631000502417 * t**(x+0.5) * cmath.exp(-t) * r + +gamma = _mathfun_real(_gamma_real, _gamma_complex) + +def rgamma(x): + try: + return 1./gamma(x) + except ZeroDivisionError: + return x*0.0 + +def factorial(x): + return gamma(x+1.0) + +def arg(x): + if type(x) is float: + return math.atan2(0.0,x) + return math.atan2(x.imag,x.real) + +# XXX: broken for negatives +def loggamma(x): + if type(x) not in (float, complex): + try: + x = float(x) + except (ValueError, TypeError): + x = complex(x) + try: + xreal = x.real + ximag = x.imag + except AttributeError: # py2.5 + xreal = x + ximag = 0.0 + # Reflection formula + # http://functions.wolfram.com/GammaBetaErf/LogGamma/16/01/01/0003/ + if xreal < 0.0: + if abs(x) < 0.5: + v = log(gamma(x)) + if ximag == 0: + v = v.conjugate() + return v + z = 1-x + try: + re = z.real + im = z.imag + except AttributeError: # py2.5 + re = z + im = 0.0 + refloor = floor(re) + if im == 0.0: + imsign = 0 + elif im < 0.0: + imsign = -1 + else: + imsign = 1 + return (-pi*1j)*abs(refloor)*(1-abs(imsign)) + logpi - \ + log(sinpi(z-refloor)) - loggamma(z) + 1j*pi*refloor*imsign + if x == 1.0 or x == 2.0: + return x*0 + p = 0. + while abs(x) < 11: + p -= log(x) + x += 1.0 + s = 0.918938533204672742 + (x-0.5)*log(x) - x + r = 1./x + r2 = r*r + s += 0.083333333333333333333*r; r *= r2 + s += -0.0027777777777777777778*r; r *= r2 + s += 0.00079365079365079365079*r; r *= r2 + s += -0.0005952380952380952381*r; r *= r2 + s += 0.00084175084175084175084*r; r *= r2 + s += -0.0019175269175269175269*r; r *= r2 + s += 0.0064102564102564102564*r; r *= r2 + s += -0.02955065359477124183*r + return s + p + +_psi_coeff = [ +0.083333333333333333333, +-0.0083333333333333333333, +0.003968253968253968254, +-0.0041666666666666666667, +0.0075757575757575757576, +-0.021092796092796092796, +0.083333333333333333333, +-0.44325980392156862745, +3.0539543302701197438, +-26.456212121212121212] + +def _digamma_real(x): + _intx = int(x) + if _intx == x: + if _intx <= 0: + raise ZeroDivisionError("polygamma pole") + if x < 0.5: + x = 1.0-x + s = pi*cotpi(x) + else: + s = 0.0 + while x < 10.0: + s -= 1.0/x + x += 1.0 + x2 = x**-2 + t = x2 + for c in _psi_coeff: + s -= c*t + if t < 1e-20: + break + t *= x2 + return s + math_log(x) - 0.5/x + +def _digamma_complex(x): + if not x.imag: + return complex(_digamma_real(x.real)) + if x.real < 0.5: + x = 1.0-x + s = pi*cotpi(x) + else: + s = 0.0 + while abs(x) < 10.0: + s -= 1.0/x + x += 1.0 + x2 = x**-2 + t = x2 + for c in _psi_coeff: + s -= c*t + if abs(t) < 1e-20: + break + t *= x2 + return s + cmath.log(x) - 0.5/x + +digamma = _mathfun_real(_digamma_real, _digamma_complex) + +# TODO: could implement complex erf and erfc here. Need +# to find an accurate method (avoiding cancellation) +# for approx. 1 < abs(x) < 9. + +_erfc_coeff_P = [ + 1.0000000161203922312, + 2.1275306946297962644, + 2.2280433377390253297, + 1.4695509105618423961, + 0.66275911699770787537, + 0.20924776504163751585, + 0.045459713768411264339, + 0.0063065951710717791934, + 0.00044560259661560421715][::-1] + +_erfc_coeff_Q = [ + 1.0000000000000000000, + 3.2559100272784894318, + 4.9019435608903239131, + 4.4971472894498014205, + 2.7845640601891186528, + 1.2146026030046904138, + 0.37647108453729465912, + 0.080970149639040548613, + 0.011178148899483545902, + 0.00078981003831980423513][::-1] + +def _polyval(coeffs, x): + p = coeffs[0] + for c in coeffs[1:]: + p = c + x*p + return p + +def _erf_taylor(x): + # Taylor series assuming 0 <= x <= 1 + x2 = x*x + s = t = x + n = 1 + while abs(t) > 1e-17: + t *= x2/n + s -= t/(n+n+1) + n += 1 + t *= x2/n + s += t/(n+n+1) + n += 1 + return 1.1283791670955125739*s + +def _erfc_mid(x): + # Rational approximation assuming 0 <= x <= 9 + return exp(-x*x)*_polyval(_erfc_coeff_P,x)/_polyval(_erfc_coeff_Q,x) + +def _erfc_asymp(x): + # Asymptotic expansion assuming x >= 9 + x2 = x*x + v = exp(-x2)/x*0.56418958354775628695 + r = t = 0.5 / x2 + s = 1.0 + for n in range(1,22,4): + s -= t + t *= r * (n+2) + s += t + t *= r * (n+4) + if abs(t) < 1e-17: + break + return s * v + +def erf(x): + """ + erf of a real number. + """ + x = float(x) + if x != x: + return x + if x < 0.0: + return -erf(-x) + if x >= 1.0: + if x >= 6.0: + return 1.0 + return 1.0 - _erfc_mid(x) + return _erf_taylor(x) + +def erfc(x): + """ + erfc of a real number. + """ + x = float(x) + if x != x: + return x + if x < 0.0: + if x < -6.0: + return 2.0 + return 2.0-erfc(-x) + if x > 9.0: + return _erfc_asymp(x) + if x >= 1.0: + return _erfc_mid(x) + return 1.0 - _erf_taylor(x) + +gauss42 = [\ +(0.99839961899006235, 0.0041059986046490839), +(-0.99839961899006235, 0.0041059986046490839), +(0.9915772883408609, 0.009536220301748501), +(-0.9915772883408609,0.009536220301748501), +(0.97934250806374812, 0.014922443697357493), +(-0.97934250806374812, 0.014922443697357493), +(0.96175936533820439,0.020227869569052644), +(-0.96175936533820439, 0.020227869569052644), +(0.93892355735498811, 0.025422959526113047), +(-0.93892355735498811,0.025422959526113047), +(0.91095972490412735, 0.030479240699603467), +(-0.91095972490412735, 0.030479240699603467), +(0.87802056981217269,0.03536907109759211), +(-0.87802056981217269, 0.03536907109759211), +(0.8402859832618168, 0.040065735180692258), +(-0.8402859832618168,0.040065735180692258), +(0.7979620532554873, 0.044543577771965874), +(-0.7979620532554873, 0.044543577771965874), +(0.75127993568948048,0.048778140792803244), +(-0.75127993568948048, 0.048778140792803244), +(0.70049459055617114, 0.052746295699174064), +(-0.70049459055617114,0.052746295699174064), +(0.64588338886924779, 0.056426369358018376), +(-0.64588338886924779, 0.056426369358018376), +(0.58774459748510932, 0.059798262227586649), +(-0.58774459748510932, 0.059798262227586649), +(0.5263957499311922, 0.062843558045002565), +(-0.5263957499311922, 0.062843558045002565), +(0.46217191207042191, 0.065545624364908975), +(-0.46217191207042191, 0.065545624364908975), +(0.39542385204297503, 0.067889703376521934), +(-0.39542385204297503, 0.067889703376521934), +(0.32651612446541151, 0.069862992492594159), +(-0.32651612446541151, 0.069862992492594159), +(0.25582507934287907, 0.071454714265170971), +(-0.25582507934287907, 0.071454714265170971), +(0.18373680656485453, 0.072656175243804091), +(-0.18373680656485453, 0.072656175243804091), +(0.11064502720851986, 0.073460813453467527), +(-0.11064502720851986, 0.073460813453467527), +(0.036948943165351772, 0.073864234232172879), +(-0.036948943165351772, 0.073864234232172879)] + +EI_ASYMP_CONVERGENCE_RADIUS = 40.0 + +def ei_asymp(z, _e1=False): + r = 1./z + s = t = 1.0 + k = 1 + while 1: + t *= k*r + s += t + if abs(t) < 1e-16: + break + k += 1 + v = s*exp(z)/z + if _e1: + if type(z) is complex: + zreal = z.real + zimag = z.imag + else: + zreal = z + zimag = 0.0 + if zimag == 0.0 and zreal > 0.0: + v += pi*1j + else: + if type(z) is complex: + if z.imag > 0: + v += pi*1j + if z.imag < 0: + v -= pi*1j + return v + +def ei_taylor(z, _e1=False): + s = t = z + k = 2 + while 1: + t = t*z/k + term = t/k + if abs(term) < 1e-17: + break + s += term + k += 1 + s += euler + if _e1: + s += log(-z) + else: + if type(z) is float or z.imag == 0.0: + s += math_log(abs(z)) + else: + s += cmath.log(z) + return s + +def ei(z, _e1=False): + typez = type(z) + if typez not in (float, complex): + try: + z = float(z) + typez = float + except (TypeError, ValueError): + z = complex(z) + typez = complex + if not z: + return -INF + absz = abs(z) + if absz > EI_ASYMP_CONVERGENCE_RADIUS: + return ei_asymp(z, _e1) + elif absz <= 2.0 or (typez is float and z > 0.0): + return ei_taylor(z, _e1) + # Integrate, starting from whichever is smaller of a Taylor + # series value or an asymptotic series value + if typez is complex and z.real > 0.0: + zref = z / absz + ref = ei_taylor(zref, _e1) + else: + zref = EI_ASYMP_CONVERGENCE_RADIUS * z / absz + ref = ei_asymp(zref, _e1) + C = (zref-z)*0.5 + D = (zref+z)*0.5 + s = 0.0 + if type(z) is complex: + _exp = cmath.exp + else: + _exp = math.exp + for x,w in gauss42: + t = C*x+D + s += w*_exp(t)/t + ref -= C*s + return ref + +def e1(z): + # hack to get consistent signs if the imaginary part if 0 + # and signed + typez = type(z) + if type(z) not in (float, complex): + try: + z = float(z) + typez = float + except (TypeError, ValueError): + z = complex(z) + typez = complex + if typez is complex and not z.imag: + z = complex(z.real, 0.0) + # end hack + return -ei(-z, _e1=True) + +_zeta_int = [\ +-0.5, +0.0, +1.6449340668482264365,1.2020569031595942854,1.0823232337111381915, +1.0369277551433699263,1.0173430619844491397,1.0083492773819228268, +1.0040773561979443394,1.0020083928260822144,1.0009945751278180853, +1.0004941886041194646,1.0002460865533080483,1.0001227133475784891, +1.0000612481350587048,1.0000305882363070205,1.0000152822594086519, +1.0000076371976378998,1.0000038172932649998,1.0000019082127165539, +1.0000009539620338728,1.0000004769329867878,1.0000002384505027277, +1.0000001192199259653,1.0000000596081890513,1.0000000298035035147, +1.0000000149015548284] + +_zeta_P = [-3.50000000087575873, -0.701274355654678147, +-0.0672313458590012612, -0.00398731457954257841, +-0.000160948723019303141, -4.67633010038383371e-6, +-1.02078104417700585e-7, -1.68030037095896287e-9, +-1.85231868742346722e-11][::-1] + +_zeta_Q = [1.00000000000000000, -0.936552848762465319, +-0.0588835413263763741, -0.00441498861482948666, +-0.000143416758067432622, -5.10691659585090782e-6, +-9.58813053268913799e-8, -1.72963791443181972e-9, +-1.83527919681474132e-11][::-1] + +_zeta_1 = [3.03768838606128127e-10, -1.21924525236601262e-8, +2.01201845887608893e-7, -1.53917240683468381e-6, +-5.09890411005967954e-7, 0.000122464707271619326, +-0.000905721539353130232, -0.00239315326074843037, +0.084239750013159168, 0.418938517907442414, 0.500000001921884009] + +_zeta_0 = [-3.46092485016748794e-10, -6.42610089468292485e-9, +1.76409071536679773e-7, -1.47141263991560698e-6, -6.38880222546167613e-7, +0.000122641099800668209, -0.000905894913516772796, -0.00239303348507992713, +0.0842396947501199816, 0.418938533204660256, 0.500000000000000052] + +def zeta(s): + """ + Riemann zeta function, real argument + """ + if not isinstance(s, (float, int)): + try: + s = float(s) + except (ValueError, TypeError): + try: + s = complex(s) + if not s.imag: + return complex(zeta(s.real)) + except (ValueError, TypeError): + pass + raise NotImplementedError + if s == 1: + raise ValueError("zeta(1) pole") + if s >= 27: + return 1.0 + 2.0**(-s) + 3.0**(-s) + n = int(s) + if n == s: + if n >= 0: + return _zeta_int[n] + if not (n % 2): + return 0.0 + if s <= 0.0: + return 2.**s*pi**(s-1)*_sinpi_real(0.5*s)*_gamma_real(1-s)*zeta(1-s) + if s <= 2.0: + if s <= 1.0: + return _polyval(_zeta_0,s)/(s-1) + return _polyval(_zeta_1,s)/(s-1) + z = _polyval(_zeta_P,s) / _polyval(_zeta_Q,s) + return 1.0 + 2.0**(-s) + 3.0**(-s) + 4.0**(-s)*z diff --git a/lib/python3.12/site-packages/mpmath/matrices/__init__.py b/lib/python3.12/site-packages/mpmath/matrices/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..293697b9fcf8bd82d58ac4ff45acd73fadac82f9 --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/matrices/__init__.py @@ -0,0 +1,2 @@ +from . import eigen # to set methods +from . import eigen_symmetric # to set methods diff --git a/lib/python3.12/site-packages/mpmath/matrices/__pycache__/__init__.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/matrices/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..13e4dec1c57169a86c6014b2c2664aabef3e717a Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/matrices/__pycache__/__init__.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/matrices/__pycache__/calculus.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/matrices/__pycache__/calculus.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3005badc9d7057b3537b13e811783d391b21df2d Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/matrices/__pycache__/calculus.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/matrices/__pycache__/eigen.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/matrices/__pycache__/eigen.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0f8065830d7a1a8c4c8cbc951731aad70ddd1e33 Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/matrices/__pycache__/eigen.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/matrices/__pycache__/eigen_symmetric.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/matrices/__pycache__/eigen_symmetric.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6cb4176846da3d291e2a47a958a8ef300d8137f1 Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/matrices/__pycache__/eigen_symmetric.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/matrices/__pycache__/linalg.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/matrices/__pycache__/linalg.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cfcf6bb018c03b787da4a2762d98539a8a82ddb4 Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/matrices/__pycache__/linalg.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/matrices/__pycache__/matrices.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/matrices/__pycache__/matrices.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..40f290d10de324175b531e84228cb4d61f675b08 Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/matrices/__pycache__/matrices.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/matrices/calculus.py b/lib/python3.12/site-packages/mpmath/matrices/calculus.py new file mode 100644 index 0000000000000000000000000000000000000000..7fae2a7a9a29898241ed41810331b480ff70798f --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/matrices/calculus.py @@ -0,0 +1,531 @@ +from ..libmp.backend import xrange + +# TODO: should use diagonalization-based algorithms + +class MatrixCalculusMethods(object): + + def _exp_pade(ctx, a): + """ + Exponential of a matrix using Pade approximants. + + See G. H. Golub, C. F. van Loan 'Matrix Computations', + third Ed., page 572 + + TODO: + - find a good estimate for q + - reduce the number of matrix multiplications to improve + performance + """ + def eps_pade(p): + return ctx.mpf(2)**(3-2*p) * \ + ctx.factorial(p)**2/(ctx.factorial(2*p)**2 * (2*p + 1)) + q = 4 + extraq = 8 + while 1: + if eps_pade(q) < ctx.eps: + break + q += 1 + q += extraq + j = int(max(1, ctx.mag(ctx.mnorm(a,'inf')))) + extra = q + prec = ctx.prec + ctx.dps += extra + 3 + try: + a = a/2**j + na = a.rows + den = ctx.eye(na) + num = ctx.eye(na) + x = ctx.eye(na) + c = ctx.mpf(1) + for k in range(1, q+1): + c *= ctx.mpf(q - k + 1)/((2*q - k + 1) * k) + x = a*x + cx = c*x + num += cx + den += (-1)**k * cx + f = ctx.lu_solve_mat(den, num) + for k in range(j): + f = f*f + finally: + ctx.prec = prec + return f*1 + + def expm(ctx, A, method='taylor'): + r""" + Computes the matrix exponential of a square matrix `A`, which is defined + by the power series + + .. math :: + + \exp(A) = I + A + \frac{A^2}{2!} + \frac{A^3}{3!} + \ldots + + With method='taylor', the matrix exponential is computed + using the Taylor series. With method='pade', Pade approximants + are used instead. + + **Examples** + + Basic examples:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> expm(zeros(3)) + [1.0 0.0 0.0] + [0.0 1.0 0.0] + [0.0 0.0 1.0] + >>> expm(eye(3)) + [2.71828182845905 0.0 0.0] + [ 0.0 2.71828182845905 0.0] + [ 0.0 0.0 2.71828182845905] + >>> expm([[1,1,0],[1,0,1],[0,1,0]]) + [ 3.86814500615414 2.26812870852145 0.841130841230196] + [ 2.26812870852145 2.44114713886289 1.42699786729125] + [0.841130841230196 1.42699786729125 1.6000162976327] + >>> expm([[1,1,0],[1,0,1],[0,1,0]], method='pade') + [ 3.86814500615414 2.26812870852145 0.841130841230196] + [ 2.26812870852145 2.44114713886289 1.42699786729125] + [0.841130841230196 1.42699786729125 1.6000162976327] + >>> expm([[1+j, 0], [1+j,1]]) + [(1.46869393991589 + 2.28735528717884j) 0.0] + [ (1.03776739863568 + 3.536943175722j) (2.71828182845905 + 0.0j)] + + Matrices with large entries are allowed:: + + >>> expm(matrix([[1,2],[2,3]])**25) + [5.65024064048415e+2050488462815550 9.14228140091932e+2050488462815550] + [9.14228140091932e+2050488462815550 1.47925220414035e+2050488462815551] + + The identity `\exp(A+B) = \exp(A) \exp(B)` does not hold for + noncommuting matrices:: + + >>> A = hilbert(3) + >>> B = A + eye(3) + >>> chop(mnorm(A*B - B*A)) + 0.0 + >>> chop(mnorm(expm(A+B) - expm(A)*expm(B))) + 0.0 + >>> B = A + ones(3) + >>> mnorm(A*B - B*A) + 1.8 + >>> mnorm(expm(A+B) - expm(A)*expm(B)) + 42.0927851137247 + + """ + if method == 'pade': + prec = ctx.prec + try: + A = ctx.matrix(A) + ctx.prec += 2*A.rows + res = ctx._exp_pade(A) + finally: + ctx.prec = prec + return res + A = ctx.matrix(A) + prec = ctx.prec + j = int(max(1, ctx.mag(ctx.mnorm(A,'inf')))) + j += int(0.5*prec**0.5) + try: + ctx.prec += 10 + 2*j + tol = +ctx.eps + A = A/2**j + T = A + Y = A**0 + A + k = 2 + while 1: + T *= A * (1/ctx.mpf(k)) + if ctx.mnorm(T, 'inf') < tol: + break + Y += T + k += 1 + for k in xrange(j): + Y = Y*Y + finally: + ctx.prec = prec + Y *= 1 + return Y + + def cosm(ctx, A): + r""" + Gives the cosine of a square matrix `A`, defined in analogy + with the matrix exponential. + + Examples:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> X = eye(3) + >>> cosm(X) + [0.54030230586814 0.0 0.0] + [ 0.0 0.54030230586814 0.0] + [ 0.0 0.0 0.54030230586814] + >>> X = hilbert(3) + >>> cosm(X) + [ 0.424403834569555 -0.316643413047167 -0.221474945949293] + [-0.316643413047167 0.820646708837824 -0.127183694770039] + [-0.221474945949293 -0.127183694770039 0.909236687217541] + >>> X = matrix([[1+j,-2],[0,-j]]) + >>> cosm(X) + [(0.833730025131149 - 0.988897705762865j) (1.07485840848393 - 0.17192140544213j)] + [ 0.0 (1.54308063481524 + 0.0j)] + """ + B = 0.5 * (ctx.expm(A*ctx.j) + ctx.expm(A*(-ctx.j))) + if not sum(A.apply(ctx.im).apply(abs)): + B = B.apply(ctx.re) + return B + + def sinm(ctx, A): + r""" + Gives the sine of a square matrix `A`, defined in analogy + with the matrix exponential. + + Examples:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> X = eye(3) + >>> sinm(X) + [0.841470984807897 0.0 0.0] + [ 0.0 0.841470984807897 0.0] + [ 0.0 0.0 0.841470984807897] + >>> X = hilbert(3) + >>> sinm(X) + [0.711608512150994 0.339783913247439 0.220742837314741] + [0.339783913247439 0.244113865695532 0.187231271174372] + [0.220742837314741 0.187231271174372 0.155816730769635] + >>> X = matrix([[1+j,-2],[0,-j]]) + >>> sinm(X) + [(1.29845758141598 + 0.634963914784736j) (-1.96751511930922 + 0.314700021761367j)] + [ 0.0 (0.0 - 1.1752011936438j)] + """ + B = (-0.5j) * (ctx.expm(A*ctx.j) - ctx.expm(A*(-ctx.j))) + if not sum(A.apply(ctx.im).apply(abs)): + B = B.apply(ctx.re) + return B + + def _sqrtm_rot(ctx, A, _may_rotate): + # If the iteration fails to converge, cheat by performing + # a rotation by a complex number + u = ctx.j**0.3 + return ctx.sqrtm(u*A, _may_rotate) / ctx.sqrt(u) + + def sqrtm(ctx, A, _may_rotate=2): + r""" + Computes a square root of the square matrix `A`, i.e. returns + a matrix `B = A^{1/2}` such that `B^2 = A`. The square root + of a matrix, if it exists, is not unique. + + **Examples** + + Square roots of some simple matrices:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> sqrtm([[1,0], [0,1]]) + [1.0 0.0] + [0.0 1.0] + >>> sqrtm([[0,0], [0,0]]) + [0.0 0.0] + [0.0 0.0] + >>> sqrtm([[2,0],[0,1]]) + [1.4142135623731 0.0] + [ 0.0 1.0] + >>> sqrtm([[1,1],[1,0]]) + [ (0.920442065259926 - 0.21728689675164j) (0.568864481005783 + 0.351577584254143j)] + [(0.568864481005783 + 0.351577584254143j) (0.351577584254143 - 0.568864481005783j)] + >>> sqrtm([[1,0],[0,1]]) + [1.0 0.0] + [0.0 1.0] + >>> sqrtm([[-1,0],[0,1]]) + [(0.0 - 1.0j) 0.0] + [ 0.0 (1.0 + 0.0j)] + >>> sqrtm([[j,0],[0,j]]) + [(0.707106781186547 + 0.707106781186547j) 0.0] + [ 0.0 (0.707106781186547 + 0.707106781186547j)] + + A square root of a rotation matrix, giving the corresponding + half-angle rotation matrix:: + + >>> t1 = 0.75 + >>> t2 = t1 * 0.5 + >>> A1 = matrix([[cos(t1), -sin(t1)], [sin(t1), cos(t1)]]) + >>> A2 = matrix([[cos(t2), -sin(t2)], [sin(t2), cos(t2)]]) + >>> sqrtm(A1) + [0.930507621912314 -0.366272529086048] + [0.366272529086048 0.930507621912314] + >>> A2 + [0.930507621912314 -0.366272529086048] + [0.366272529086048 0.930507621912314] + + The identity `(A^2)^{1/2} = A` does not necessarily hold:: + + >>> A = matrix([[4,1,4],[7,8,9],[10,2,11]]) + >>> sqrtm(A**2) + [ 4.0 1.0 4.0] + [ 7.0 8.0 9.0] + [10.0 2.0 11.0] + >>> sqrtm(A)**2 + [ 4.0 1.0 4.0] + [ 7.0 8.0 9.0] + [10.0 2.0 11.0] + >>> A = matrix([[-4,1,4],[7,-8,9],[10,2,11]]) + >>> sqrtm(A**2) + [ 7.43715112194995 -0.324127569985474 1.8481718827526] + [-0.251549715716942 9.32699765900402 2.48221180985147] + [ 4.11609388833616 0.775751877098258 13.017955697342] + >>> chop(sqrtm(A)**2) + [-4.0 1.0 4.0] + [ 7.0 -8.0 9.0] + [10.0 2.0 11.0] + + For some matrices, a square root does not exist:: + + >>> sqrtm([[0,1], [0,0]]) + Traceback (most recent call last): + ... + ZeroDivisionError: matrix is numerically singular + + Two examples from the documentation for Matlab's ``sqrtm``:: + + >>> mp.dps = 15; mp.pretty = True + >>> sqrtm([[7,10],[15,22]]) + [1.56669890360128 1.74077655955698] + [2.61116483933547 4.17786374293675] + >>> + >>> X = matrix(\ + ... [[5,-4,1,0,0], + ... [-4,6,-4,1,0], + ... [1,-4,6,-4,1], + ... [0,1,-4,6,-4], + ... [0,0,1,-4,5]]) + >>> Y = matrix(\ + ... [[2,-1,-0,-0,-0], + ... [-1,2,-1,0,-0], + ... [0,-1,2,-1,0], + ... [-0,0,-1,2,-1], + ... [-0,-0,-0,-1,2]]) + >>> mnorm(sqrtm(X) - Y) + 4.53155328326114e-19 + + """ + A = ctx.matrix(A) + # Trivial + if A*0 == A: + return A + prec = ctx.prec + if _may_rotate: + d = ctx.det(A) + if abs(ctx.im(d)) < 16*ctx.eps and ctx.re(d) < 0: + return ctx._sqrtm_rot(A, _may_rotate-1) + try: + ctx.prec += 10 + tol = ctx.eps * 128 + Y = A + Z = I = A**0 + k = 0 + # Denman-Beavers iteration + while 1: + Yprev = Y + try: + Y, Z = 0.5*(Y+ctx.inverse(Z)), 0.5*(Z+ctx.inverse(Y)) + except ZeroDivisionError: + if _may_rotate: + Y = ctx._sqrtm_rot(A, _may_rotate-1) + break + else: + raise + mag1 = ctx.mnorm(Y-Yprev, 'inf') + mag2 = ctx.mnorm(Y, 'inf') + if mag1 <= mag2*tol: + break + if _may_rotate and k > 6 and not mag1 < mag2 * 0.001: + return ctx._sqrtm_rot(A, _may_rotate-1) + k += 1 + if k > ctx.prec: + raise ctx.NoConvergence + finally: + ctx.prec = prec + Y *= 1 + return Y + + def logm(ctx, A): + r""" + Computes a logarithm of the square matrix `A`, i.e. returns + a matrix `B = \log(A)` such that `\exp(B) = A`. The logarithm + of a matrix, if it exists, is not unique. + + **Examples** + + Logarithms of some simple matrices:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> X = eye(3) + >>> logm(X) + [0.0 0.0 0.0] + [0.0 0.0 0.0] + [0.0 0.0 0.0] + >>> logm(2*X) + [0.693147180559945 0.0 0.0] + [ 0.0 0.693147180559945 0.0] + [ 0.0 0.0 0.693147180559945] + >>> logm(expm(X)) + [1.0 0.0 0.0] + [0.0 1.0 0.0] + [0.0 0.0 1.0] + + A logarithm of a complex matrix:: + + >>> X = matrix([[2+j, 1, 3], [1-j, 1-2*j, 1], [-4, -5, j]]) + >>> B = logm(X) + >>> nprint(B) + [ (0.808757 + 0.107759j) (2.20752 + 0.202762j) (1.07376 - 0.773874j)] + [ (0.905709 - 0.107795j) (0.0287395 - 0.824993j) (0.111619 + 0.514272j)] + [(-0.930151 + 0.399512j) (-2.06266 - 0.674397j) (0.791552 + 0.519839j)] + >>> chop(expm(B)) + [(2.0 + 1.0j) 1.0 3.0] + [(1.0 - 1.0j) (1.0 - 2.0j) 1.0] + [ -4.0 -5.0 (0.0 + 1.0j)] + + A matrix `X` close to the identity matrix, for which + `\log(\exp(X)) = \exp(\log(X)) = X` holds:: + + >>> X = eye(3) + hilbert(3)/4 + >>> X + [ 1.25 0.125 0.0833333333333333] + [ 0.125 1.08333333333333 0.0625] + [0.0833333333333333 0.0625 1.05] + >>> logm(expm(X)) + [ 1.25 0.125 0.0833333333333333] + [ 0.125 1.08333333333333 0.0625] + [0.0833333333333333 0.0625 1.05] + >>> expm(logm(X)) + [ 1.25 0.125 0.0833333333333333] + [ 0.125 1.08333333333333 0.0625] + [0.0833333333333333 0.0625 1.05] + + A logarithm of a rotation matrix, giving back the angle of + the rotation:: + + >>> t = 3.7 + >>> A = matrix([[cos(t),sin(t)],[-sin(t),cos(t)]]) + >>> chop(logm(A)) + [ 0.0 -2.58318530717959] + [2.58318530717959 0.0] + >>> (2*pi-t) + 2.58318530717959 + + For some matrices, a logarithm does not exist:: + + >>> logm([[1,0], [0,0]]) + Traceback (most recent call last): + ... + ZeroDivisionError: matrix is numerically singular + + Logarithm of a matrix with large entries:: + + >>> logm(hilbert(3) * 10**20).apply(re) + [ 45.5597513593433 1.27721006042799 0.317662687717978] + [ 1.27721006042799 42.5222778973542 2.24003708791604] + [0.317662687717978 2.24003708791604 42.395212822267] + + """ + A = ctx.matrix(A) + prec = ctx.prec + try: + ctx.prec += 10 + tol = ctx.eps * 128 + I = A**0 + B = A + n = 0 + while 1: + B = ctx.sqrtm(B) + n += 1 + if ctx.mnorm(B-I, 'inf') < 0.125: + break + T = X = B-I + L = X*0 + k = 1 + while 1: + if k & 1: + L += T / k + else: + L -= T / k + T *= X + if ctx.mnorm(T, 'inf') < tol: + break + k += 1 + if k > ctx.prec: + raise ctx.NoConvergence + finally: + ctx.prec = prec + L *= 2**n + return L + + def powm(ctx, A, r): + r""" + Computes `A^r = \exp(A \log r)` for a matrix `A` and complex + number `r`. + + **Examples** + + Powers and inverse powers of a matrix:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> A = matrix([[4,1,4],[7,8,9],[10,2,11]]) + >>> powm(A, 2) + [ 63.0 20.0 69.0] + [174.0 89.0 199.0] + [164.0 48.0 179.0] + >>> chop(powm(powm(A, 4), 1/4.)) + [ 4.0 1.0 4.0] + [ 7.0 8.0 9.0] + [10.0 2.0 11.0] + >>> powm(extraprec(20)(powm)(A, -4), -1/4.) + [ 4.0 1.0 4.0] + [ 7.0 8.0 9.0] + [10.0 2.0 11.0] + >>> chop(powm(powm(A, 1+0.5j), 1/(1+0.5j))) + [ 4.0 1.0 4.0] + [ 7.0 8.0 9.0] + [10.0 2.0 11.0] + >>> powm(extraprec(5)(powm)(A, -1.5), -1/(1.5)) + [ 4.0 1.0 4.0] + [ 7.0 8.0 9.0] + [10.0 2.0 11.0] + + A Fibonacci-generating matrix:: + + >>> powm([[1,1],[1,0]], 10) + [89.0 55.0] + [55.0 34.0] + >>> fib(10) + 55.0 + >>> powm([[1,1],[1,0]], 6.5) + [(16.5166626964253 - 0.0121089837381789j) (10.2078589271083 + 0.0195927472575932j)] + [(10.2078589271083 + 0.0195927472575932j) (6.30880376931698 - 0.0317017309957721j)] + >>> (phi**6.5 - (1-phi)**6.5)/sqrt(5) + (10.2078589271083 - 0.0195927472575932j) + >>> powm([[1,1],[1,0]], 6.2) + [ (14.3076953002666 - 0.008222855781077j) (8.81733464837593 + 0.0133048601383712j)] + [(8.81733464837593 + 0.0133048601383712j) (5.49036065189071 - 0.0215277159194482j)] + >>> (phi**6.2 - (1-phi)**6.2)/sqrt(5) + (8.81733464837593 - 0.0133048601383712j) + + """ + A = ctx.matrix(A) + r = ctx.convert(r) + prec = ctx.prec + try: + ctx.prec += 10 + if ctx.isint(r): + v = A ** int(r) + elif ctx.isint(r*2): + y = int(r*2) + v = ctx.sqrtm(A) ** y + else: + v = ctx.expm(r*ctx.logm(A)) + finally: + ctx.prec = prec + v *= 1 + return v diff --git a/lib/python3.12/site-packages/mpmath/matrices/eigen.py b/lib/python3.12/site-packages/mpmath/matrices/eigen.py new file mode 100644 index 0000000000000000000000000000000000000000..885d604203195b695183329acc637de91aeaf5ea --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/matrices/eigen.py @@ -0,0 +1,877 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +################################################################################################## +# module for the eigenvalue problem +# Copyright 2013 Timo Hartmann (thartmann15 at gmail.com) +# +# todo: +# - implement balancing +# - agressive early deflation +# +################################################################################################## + +""" +The eigenvalue problem +---------------------- + +This file contains routines for the eigenvalue problem. + +high level routines: + + hessenberg : reduction of a real or complex square matrix to upper Hessenberg form + schur : reduction of a real or complex square matrix to upper Schur form + eig : eigenvalues and eigenvectors of a real or complex square matrix + +low level routines: + + hessenberg_reduce_0 : reduction of a real or complex square matrix to upper Hessenberg form + hessenberg_reduce_1 : auxiliary routine to hessenberg_reduce_0 + qr_step : a single implicitly shifted QR step for an upper Hessenberg matrix + hessenberg_qr : Schur decomposition of an upper Hessenberg matrix + eig_tr_r : right eigenvectors of an upper triangular matrix + eig_tr_l : left eigenvectors of an upper triangular matrix +""" + +from ..libmp.backend import xrange + +class Eigen(object): + pass + +def defun(f): + setattr(Eigen, f.__name__, f) + return f + +def hessenberg_reduce_0(ctx, A, T): + """ + This routine computes the (upper) Hessenberg decomposition of a square matrix A. + Given A, an unitary matrix Q is calculated such that + + Q' A Q = H and Q' Q = Q Q' = 1 + + where H is an upper Hessenberg matrix, meaning that it only contains zeros + below the first subdiagonal. Here ' denotes the hermitian transpose (i.e. + transposition and conjugation). + + parameters: + A (input/output) On input, A contains the square matrix A of + dimension (n,n). On output, A contains a compressed representation + of Q and H. + T (output) An array of length n containing the first elements of + the Householder reflectors. + """ + + # internally we work with householder reflections from the right. + # let u be a row vector (i.e. u[i]=A[i,:i]). then + # Q is build up by reflectors of the type (1-v'v) where v is a suitable + # modification of u. these reflectors are applyed to A from the right. + # because we work with reflectors from the right we have to start with + # the bottom row of A and work then upwards (this corresponds to + # some kind of RQ decomposition). + # the first part of the vectors v (i.e. A[i,:(i-1)]) are stored as row vectors + # in the lower left part of A (excluding the diagonal and subdiagonal). + # the last entry of v is stored in T. + # the upper right part of A (including diagonal and subdiagonal) becomes H. + + + n = A.rows + if n <= 2: return + + for i in xrange(n-1, 1, -1): + + # scale the vector + + scale = 0 + for k in xrange(0, i): + scale += abs(ctx.re(A[i,k])) + abs(ctx.im(A[i,k])) + + scale_inv = 0 + if scale != 0: + scale_inv = 1 / scale + + if scale == 0 or ctx.isinf(scale_inv): + # sadly there are floating point numbers not equal to zero whose reciprocal is infinity + T[i] = 0 + A[i,i-1] = 0 + continue + + # calculate parameters for housholder transformation + + H = 0 + for k in xrange(0, i): + A[i,k] *= scale_inv + rr = ctx.re(A[i,k]) + ii = ctx.im(A[i,k]) + H += rr * rr + ii * ii + + F = A[i,i-1] + f = abs(F) + G = ctx.sqrt(H) + A[i,i-1] = - G * scale + + if f == 0: + T[i] = G + else: + ff = F / f + T[i] = F + G * ff + A[i,i-1] *= ff + + H += G * f + H = 1 / ctx.sqrt(H) + + T[i] *= H + for k in xrange(0, i - 1): + A[i,k] *= H + + for j in xrange(0, i): + # apply housholder transformation (from right) + + G = ctx.conj(T[i]) * A[j,i-1] + for k in xrange(0, i-1): + G += ctx.conj(A[i,k]) * A[j,k] + + A[j,i-1] -= G * T[i] + for k in xrange(0, i-1): + A[j,k] -= G * A[i,k] + + for j in xrange(0, n): + # apply housholder transformation (from left) + + G = T[i] * A[i-1,j] + for k in xrange(0, i-1): + G += A[i,k] * A[k,j] + + A[i-1,j] -= G * ctx.conj(T[i]) + for k in xrange(0, i-1): + A[k,j] -= G * ctx.conj(A[i,k]) + + + +def hessenberg_reduce_1(ctx, A, T): + """ + This routine forms the unitary matrix Q described in hessenberg_reduce_0. + + parameters: + A (input/output) On input, A is the same matrix as delivered by + hessenberg_reduce_0. On output, A is set to Q. + + T (input) On input, T is the same array as delivered by hessenberg_reduce_0. + """ + + n = A.rows + + if n == 1: + A[0,0] = 1 + return + + A[0,0] = A[1,1] = 1 + A[0,1] = A[1,0] = 0 + + for i in xrange(2, n): + if T[i] != 0: + + for j in xrange(0, i): + G = T[i] * A[i-1,j] + for k in xrange(0, i-1): + G += A[i,k] * A[k,j] + + A[i-1,j] -= G * ctx.conj(T[i]) + for k in xrange(0, i-1): + A[k,j] -= G * ctx.conj(A[i,k]) + + A[i,i] = 1 + for j in xrange(0, i): + A[j,i] = A[i,j] = 0 + + + +@defun +def hessenberg(ctx, A, overwrite_a = False): + """ + This routine computes the Hessenberg decomposition of a square matrix A. + Given A, an unitary matrix Q is determined such that + + Q' A Q = H and Q' Q = Q Q' = 1 + + where H is an upper right Hessenberg matrix. Here ' denotes the hermitian + transpose (i.e. transposition and conjugation). + + input: + A : a real or complex square matrix + overwrite_a : if true, allows modification of A which may improve + performance. if false, A is not modified. + + output: + Q : an unitary matrix + H : an upper right Hessenberg matrix + + example: + >>> from mpmath import mp + >>> A = mp.matrix([[3, -1, 2], [2, 5, -5], [-2, -3, 7]]) + >>> Q, H = mp.hessenberg(A) + >>> mp.nprint(H, 3) # doctest:+SKIP + [ 3.15 2.23 4.44] + [-0.769 4.85 3.05] + [ 0.0 3.61 7.0] + >>> print(mp.chop(A - Q * H * Q.transpose_conj())) + [0.0 0.0 0.0] + [0.0 0.0 0.0] + [0.0 0.0 0.0] + + return value: (Q, H) + """ + + n = A.rows + + if n == 1: + return (ctx.matrix([[1]]), A) + + if not overwrite_a: + A = A.copy() + + T = ctx.matrix(n, 1) + + hessenberg_reduce_0(ctx, A, T) + Q = A.copy() + hessenberg_reduce_1(ctx, Q, T) + + for x in xrange(n): + for y in xrange(x+2, n): + A[y,x] = 0 + + return Q, A + + +########################################################################### + + +def qr_step(ctx, n0, n1, A, Q, shift): + """ + This subroutine executes a single implicitly shifted QR step applied to an + upper Hessenberg matrix A. Given A and shift as input, first an QR + decomposition is calculated: + + Q R = A - shift * 1 . + + The output is then following matrix: + + R Q + shift * 1 + + parameters: + n0, n1 (input) Two integers which specify the submatrix A[n0:n1,n0:n1] + on which this subroutine operators. The subdiagonal elements + to the left and below this submatrix must be deflated (i.e. zero). + following restriction is imposed: n1>=n0+2 + A (input/output) On input, A is an upper Hessenberg matrix. + On output, A is replaced by "R Q + shift * 1" + Q (input/output) The parameter Q is multiplied by the unitary matrix + Q arising from the QR decomposition. Q can also be false, in which + case the unitary matrix Q is not computated. + shift (input) a complex number specifying the shift. idealy close to an + eigenvalue of the bottemmost part of the submatrix A[n0:n1,n0:n1]. + + references: + Stoer, Bulirsch - Introduction to Numerical Analysis. + Kresser : Numerical Methods for General and Structured Eigenvalue Problems + """ + + # implicitly shifted and bulge chasing is explained at p.398/399 in "Stoer, Bulirsch - Introduction to Numerical Analysis" + # for bulge chasing see also "Watkins - The Matrix Eigenvalue Problem" sec.4.5,p.173 + + # the Givens rotation we used is determined as follows: let c,s be two complex + # numbers. then we have following relation: + # + # v = sqrt(|c|^2 + |s|^2) + # + # 1/v [ c~ s~] [c] = [v] + # [-s c ] [s] [0] + # + # the matrix on the left is our Givens rotation. + + n = A.rows + + # first step + + # calculate givens rotation + c = A[n0 ,n0] - shift + s = A[n0+1,n0] + + v = ctx.hypot(ctx.hypot(ctx.re(c), ctx.im(c)), ctx.hypot(ctx.re(s), ctx.im(s))) + + if v == 0: + v = 1 + c = 1 + s = 0 + else: + c /= v + s /= v + + cc = ctx.conj(c) + cs = ctx.conj(s) + + for k in xrange(n0, n): + # apply givens rotation from the left + x = A[n0 ,k] + y = A[n0+1,k] + A[n0 ,k] = cc * x + cs * y + A[n0+1,k] = c * y - s * x + + for k in xrange(min(n1, n0+3)): + # apply givens rotation from the right + x = A[k,n0 ] + y = A[k,n0+1] + A[k,n0 ] = c * x + s * y + A[k,n0+1] = cc * y - cs * x + + if not isinstance(Q, bool): + for k in xrange(n): + # eigenvectors + x = Q[k,n0 ] + y = Q[k,n0+1] + Q[k,n0 ] = c * x + s * y + Q[k,n0+1] = cc * y - cs * x + + # chase the bulge + + for j in xrange(n0, n1 - 2): + # calculate givens rotation + + c = A[j+1,j] + s = A[j+2,j] + + v = ctx.hypot(ctx.hypot(ctx.re(c), ctx.im(c)), ctx.hypot(ctx.re(s), ctx.im(s))) + + if v == 0: + A[j+1,j] = 0 + v = 1 + c = 1 + s = 0 + else: + A[j+1,j] = v + c /= v + s /= v + + A[j+2,j] = 0 + + cc = ctx.conj(c) + cs = ctx.conj(s) + + for k in xrange(j+1, n): + # apply givens rotation from the left + x = A[j+1,k] + y = A[j+2,k] + A[j+1,k] = cc * x + cs * y + A[j+2,k] = c * y - s * x + + for k in xrange(0, min(n1, j+4)): + # apply givens rotation from the right + x = A[k,j+1] + y = A[k,j+2] + A[k,j+1] = c * x + s * y + A[k,j+2] = cc * y - cs * x + + if not isinstance(Q, bool): + for k in xrange(0, n): + # eigenvectors + x = Q[k,j+1] + y = Q[k,j+2] + Q[k,j+1] = c * x + s * y + Q[k,j+2] = cc * y - cs * x + + + +def hessenberg_qr(ctx, A, Q): + """ + This routine computes the Schur decomposition of an upper Hessenberg matrix A. + Given A, an unitary matrix Q is determined such that + + Q' A Q = R and Q' Q = Q Q' = 1 + + where R is an upper right triangular matrix. Here ' denotes the hermitian + transpose (i.e. transposition and conjugation). + + parameters: + A (input/output) On input, A contains an upper Hessenberg matrix. + On output, A is replace by the upper right triangluar matrix R. + + Q (input/output) The parameter Q is multiplied by the unitary + matrix Q arising from the Schur decomposition. Q can also be + false, in which case the unitary matrix Q is not computated. + """ + + n = A.rows + + norm = 0 + for x in xrange(n): + for y in xrange(min(x+2, n)): + norm += ctx.re(A[y,x]) ** 2 + ctx.im(A[y,x]) ** 2 + norm = ctx.sqrt(norm) / n + + if norm == 0: + return + + n0 = 0 + n1 = n + + eps = ctx.eps / (100 * n) + maxits = ctx.dps * 4 + + its = totalits = 0 + + while 1: + # kressner p.32 algo 3 + # the active submatrix is A[n0:n1,n0:n1] + + k = n0 + + while k + 1 < n1: + s = abs(ctx.re(A[k,k])) + abs(ctx.im(A[k,k])) + abs(ctx.re(A[k+1,k+1])) + abs(ctx.im(A[k+1,k+1])) + if s < eps * norm: + s = norm + if abs(A[k+1,k]) < eps * s: + break + k += 1 + + if k + 1 < n1: + # deflation found at position (k+1, k) + + A[k+1,k] = 0 + n0 = k + 1 + + its = 0 + + if n0 + 1 >= n1: + # block of size at most two has converged + n0 = 0 + n1 = k + 1 + if n1 < 2: + # QR algorithm has converged + return + else: + if (its % 30) == 10: + # exceptional shift + shift = A[n1-1,n1-2] + elif (its % 30) == 20: + # exceptional shift + shift = abs(A[n1-1,n1-2]) + elif (its % 30) == 29: + # exceptional shift + shift = norm + else: + # A = [ a b ] det(x-A)=x*x-x*tr(A)+det(A) + # [ c d ] + # + # eigenvalues bad: (tr(A)+sqrt((tr(A))**2-4*det(A)))/2 + # bad because of cancellation if |c| is small and |a-d| is small, too. + # + # eigenvalues good: (a+d+sqrt((a-d)**2+4*b*c))/2 + + t = A[n1-2,n1-2] + A[n1-1,n1-1] + s = (A[n1-1,n1-1] - A[n1-2,n1-2]) ** 2 + 4 * A[n1-1,n1-2] * A[n1-2,n1-1] + if ctx.re(s) > 0: + s = ctx.sqrt(s) + else: + s = ctx.sqrt(-s) * 1j + a = (t + s) / 2 + b = (t - s) / 2 + if abs(A[n1-1,n1-1] - a) > abs(A[n1-1,n1-1] - b): + shift = b + else: + shift = a + + its += 1 + totalits += 1 + + qr_step(ctx, n0, n1, A, Q, shift) + + if its > maxits: + raise RuntimeError("qr: failed to converge after %d steps" % its) + + +@defun +def schur(ctx, A, overwrite_a = False): + """ + This routine computes the Schur decomposition of a square matrix A. + Given A, an unitary matrix Q is determined such that + + Q' A Q = R and Q' Q = Q Q' = 1 + + where R is an upper right triangular matrix. Here ' denotes the + hermitian transpose (i.e. transposition and conjugation). + + input: + A : a real or complex square matrix + overwrite_a : if true, allows modification of A which may improve + performance. if false, A is not modified. + + output: + Q : an unitary matrix + R : an upper right triangular matrix + + return value: (Q, R) + + example: + >>> from mpmath import mp + >>> A = mp.matrix([[3, -1, 2], [2, 5, -5], [-2, -3, 7]]) + >>> Q, R = mp.schur(A) + >>> mp.nprint(R, 3) # doctest:+SKIP + [2.0 0.417 -2.53] + [0.0 4.0 -4.74] + [0.0 0.0 9.0] + >>> print(mp.chop(A - Q * R * Q.transpose_conj())) + [0.0 0.0 0.0] + [0.0 0.0 0.0] + [0.0 0.0 0.0] + + warning: The Schur decomposition is not unique. + """ + + n = A.rows + + if n == 1: + return (ctx.matrix([[1]]), A) + + if not overwrite_a: + A = A.copy() + + T = ctx.matrix(n, 1) + + hessenberg_reduce_0(ctx, A, T) + Q = A.copy() + hessenberg_reduce_1(ctx, Q, T) + + for x in xrange(n): + for y in xrange(x + 2, n): + A[y,x] = 0 + + hessenberg_qr(ctx, A, Q) + + return Q, A + + +def eig_tr_r(ctx, A): + """ + This routine calculates the right eigenvectors of an upper right triangular matrix. + + input: + A an upper right triangular matrix + + output: + ER a matrix whose columns form the right eigenvectors of A + + return value: ER + """ + + # this subroutine is inspired by the lapack routines ctrevc.f,clatrs.f + + n = A.rows + + ER = ctx.eye(n) + + eps = ctx.eps + + unfl = ctx.ldexp(ctx.one, -ctx.prec * 30) + # since mpmath effectively has no limits on the exponent, we simply scale doubles up + # original double has prec*20 + + smlnum = unfl * (n / eps) + simin = 1 / ctx.sqrt(eps) + + rmax = 1 + + for i in xrange(1, n): + s = A[i,i] + + smin = max(eps * abs(s), smlnum) + + for j in xrange(i - 1, -1, -1): + + r = 0 + for k in xrange(j + 1, i + 1): + r += A[j,k] * ER[k,i] + + t = A[j,j] - s + if abs(t) < smin: + t = smin + + r = -r / t + ER[j,i] = r + + rmax = max(rmax, abs(r)) + if rmax > simin: + for k in xrange(j, i+1): + ER[k,i] /= rmax + rmax = 1 + + if rmax != 1: + for k in xrange(0, i + 1): + ER[k,i] /= rmax + + return ER + +def eig_tr_l(ctx, A): + """ + This routine calculates the left eigenvectors of an upper right triangular matrix. + + input: + A an upper right triangular matrix + + output: + EL a matrix whose rows form the left eigenvectors of A + + return value: EL + """ + + n = A.rows + + EL = ctx.eye(n) + + eps = ctx.eps + + unfl = ctx.ldexp(ctx.one, -ctx.prec * 30) + # since mpmath effectively has no limits on the exponent, we simply scale doubles up + # original double has prec*20 + + smlnum = unfl * (n / eps) + simin = 1 / ctx.sqrt(eps) + + rmax = 1 + + for i in xrange(0, n - 1): + s = A[i,i] + + smin = max(eps * abs(s), smlnum) + + for j in xrange(i + 1, n): + + r = 0 + for k in xrange(i, j): + r += EL[i,k] * A[k,j] + + t = A[j,j] - s + if abs(t) < smin: + t = smin + + r = -r / t + EL[i,j] = r + + rmax = max(rmax, abs(r)) + if rmax > simin: + for k in xrange(i, j + 1): + EL[i,k] /= rmax + rmax = 1 + + if rmax != 1: + for k in xrange(i, n): + EL[i,k] /= rmax + + return EL + +@defun +def eig(ctx, A, left = False, right = True, overwrite_a = False): + """ + This routine computes the eigenvalues and optionally the left and right + eigenvectors of a square matrix A. Given A, a vector E and matrices ER + and EL are calculated such that + + A ER[:,i] = E[i] ER[:,i] + EL[i,:] A = EL[i,:] E[i] + + E contains the eigenvalues of A. The columns of ER contain the right eigenvectors + of A whereas the rows of EL contain the left eigenvectors. + + + input: + A : a real or complex square matrix of shape (n, n) + left : if true, the left eigenvectors are calculated. + right : if true, the right eigenvectors are calculated. + overwrite_a : if true, allows modification of A which may improve + performance. if false, A is not modified. + + output: + E : a list of length n containing the eigenvalues of A. + ER : a matrix whose columns contain the right eigenvectors of A. + EL : a matrix whose rows contain the left eigenvectors of A. + + return values: + E if left and right are both false. + (E, ER) if right is true and left is false. + (E, EL) if left is true and right is false. + (E, EL, ER) if left and right are true. + + + examples: + >>> from mpmath import mp + >>> A = mp.matrix([[3, -1, 2], [2, 5, -5], [-2, -3, 7]]) + >>> E, ER = mp.eig(A) + >>> print(mp.chop(A * ER[:,0] - E[0] * ER[:,0])) + [0.0] + [0.0] + [0.0] + + >>> E, EL, ER = mp.eig(A,left = True, right = True) + >>> E, EL, ER = mp.eig_sort(E, EL, ER) + >>> mp.nprint(E) + [2.0, 4.0, 9.0] + >>> print(mp.chop(A * ER[:,0] - E[0] * ER[:,0])) + [0.0] + [0.0] + [0.0] + >>> print(mp.chop( EL[0,:] * A - EL[0,:] * E[0])) + [0.0 0.0 0.0] + + warning: + - If there are multiple eigenvalues, the eigenvectors do not necessarily + span the whole vectorspace, i.e. ER and EL may have not full rank. + Furthermore in that case the eigenvectors are numerical ill-conditioned. + - In the general case the eigenvalues have no natural order. + + see also: + - eigh (or eigsy, eighe) for the symmetric eigenvalue problem. + - eig_sort for sorting of eigenvalues and eigenvectors + """ + + n = A.rows + + if n == 1: + if left and (not right): + return ([A[0]], ctx.matrix([[1]])) + + if right and (not left): + return ([A[0]], ctx.matrix([[1]])) + + return ([A[0]], ctx.matrix([[1]]), ctx.matrix([[1]])) + + if not overwrite_a: + A = A.copy() + + T = ctx.zeros(n, 1) + + hessenberg_reduce_0(ctx, A, T) + + if left or right: + Q = A.copy() + hessenberg_reduce_1(ctx, Q, T) + else: + Q = False + + for x in xrange(n): + for y in xrange(x + 2, n): + A[y,x] = 0 + + hessenberg_qr(ctx, A, Q) + + E = [0 for i in xrange(n)] + for i in xrange(n): + E[i] = A[i,i] + + if not (left or right): + return E + + if left: + EL = eig_tr_l(ctx, A) + EL = EL * Q.transpose_conj() + + if right: + ER = eig_tr_r(ctx, A) + ER = Q * ER + + if left and (not right): + return (E, EL) + + if right and (not left): + return (E, ER) + + return (E, EL, ER) + +@defun +def eig_sort(ctx, E, EL = False, ER = False, f = "real"): + """ + This routine sorts the eigenvalues and eigenvectors delivered by ``eig``. + + parameters: + E : the eigenvalues as delivered by eig + EL : the left eigenvectors as delivered by eig, or false + ER : the right eigenvectors as delivered by eig, or false + f : either a string ("real" sort by increasing real part, "imag" sort by + increasing imag part, "abs" sort by absolute value) or a function + mapping complexs to the reals, i.e. ``f = lambda x: -mp.re(x) `` + would sort the eigenvalues by decreasing real part. + + return values: + E if EL and ER are both false. + (E, ER) if ER is not false and left is false. + (E, EL) if EL is not false and right is false. + (E, EL, ER) if EL and ER are not false. + + example: + >>> from mpmath import mp + >>> A = mp.matrix([[3, -1, 2], [2, 5, -5], [-2, -3, 7]]) + >>> E, EL, ER = mp.eig(A,left = True, right = True) + >>> E, EL, ER = mp.eig_sort(E, EL, ER) + >>> mp.nprint(E) + [2.0, 4.0, 9.0] + >>> E, EL, ER = mp.eig_sort(E, EL, ER,f = lambda x: -mp.re(x)) + >>> mp.nprint(E) + [9.0, 4.0, 2.0] + >>> print(mp.chop(A * ER[:,0] - E[0] * ER[:,0])) + [0.0] + [0.0] + [0.0] + >>> print(mp.chop( EL[0,:] * A - EL[0,:] * E[0])) + [0.0 0.0 0.0] + """ + + if isinstance(f, str): + if f == "real": + f = ctx.re + elif f == "imag": + f = ctx.im + elif f == "abs": + f = abs + else: + raise RuntimeError("unknown function %s" % f) + + n = len(E) + + # Sort eigenvalues (bubble-sort) + + for i in xrange(n): + imax = i + s = f(E[i]) # s is the current maximal element + + for j in xrange(i + 1, n): + c = f(E[j]) + if c < s: + s = c + imax = j + + if imax != i: + # swap eigenvalues + + z = E[i] + E[i] = E[imax] + E[imax] = z + + if not isinstance(EL, bool): + for j in xrange(n): + z = EL[i,j] + EL[i,j] = EL[imax,j] + EL[imax,j] = z + + if not isinstance(ER, bool): + for j in xrange(n): + z = ER[j,i] + ER[j,i] = ER[j,imax] + ER[j,imax] = z + + if isinstance(EL, bool) and isinstance(ER, bool): + return E + + if isinstance(EL, bool) and not(isinstance(ER, bool)): + return (E, ER) + + if isinstance(ER, bool) and not(isinstance(EL, bool)): + return (E, EL) + + return (E, EL, ER) diff --git a/lib/python3.12/site-packages/mpmath/matrices/eigen_symmetric.py b/lib/python3.12/site-packages/mpmath/matrices/eigen_symmetric.py new file mode 100644 index 0000000000000000000000000000000000000000..c82c0bb061d22c37a89f82a0b9bdab3e9ba7ddde --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/matrices/eigen_symmetric.py @@ -0,0 +1,1807 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +################################################################################################## +# module for the symmetric eigenvalue problem +# Copyright 2013 Timo Hartmann (thartmann15 at gmail.com) +# +# todo: +# - implement balancing +# +################################################################################################## + +""" +The symmetric eigenvalue problem. +--------------------------------- + +This file contains routines for the symmetric eigenvalue problem. + +high level routines: + + eigsy : real symmetric (ordinary) eigenvalue problem + eighe : complex hermitian (ordinary) eigenvalue problem + eigh : unified interface for eigsy and eighe + svd_r : singular value decomposition for real matrices + svd_c : singular value decomposition for complex matrices + svd : unified interface for svd_r and svd_c + + +low level routines: + + r_sy_tridiag : reduction of real symmetric matrix to real symmetric tridiagonal matrix + c_he_tridiag_0 : reduction of complex hermitian matrix to real symmetric tridiagonal matrix + c_he_tridiag_1 : auxiliary routine to c_he_tridiag_0 + c_he_tridiag_2 : auxiliary routine to c_he_tridiag_0 + tridiag_eigen : solves the real symmetric tridiagonal matrix eigenvalue problem + svd_r_raw : raw singular value decomposition for real matrices + svd_c_raw : raw singular value decomposition for complex matrices +""" + +from ..libmp.backend import xrange +from .eigen import defun + + +def r_sy_tridiag(ctx, A, D, E, calc_ev = True): + """ + This routine transforms a real symmetric matrix A to a real symmetric + tridiagonal matrix T using an orthogonal similarity transformation: + Q' * A * Q = T (here ' denotes the matrix transpose). + The orthogonal matrix Q is build up from Householder reflectors. + + parameters: + A (input/output) On input, A contains the real symmetric matrix of + dimension (n,n). On output, if calc_ev is true, A contains the + orthogonal matrix Q, otherwise A is destroyed. + + D (output) real array of length n, contains the diagonal elements + of the tridiagonal matrix + + E (output) real array of length n, contains the offdiagonal elements + of the tridiagonal matrix in E[0:(n-1)] where is the dimension of + the matrix A. E[n-1] is undefined. + + calc_ev (input) If calc_ev is true, this routine explicitly calculates the + orthogonal matrix Q which is then returned in A. If calc_ev is + false, Q is not explicitly calculated resulting in a shorter run time. + + This routine is a python translation of the fortran routine tred2.f in the + software library EISPACK (see netlib.org) which itself is based on the algol + procedure tred2 described in: + - Num. Math. 11, p.181-195 (1968) by Martin, Reinsch and Wilkonson + - Handbook for auto. comp., Vol II, Linear Algebra, p.212-226 (1971) + + For a good introduction to Householder reflections, see also + Stoer, Bulirsch - Introduction to Numerical Analysis. + """ + + # note : the vector v of the i-th houshoulder reflector is stored in a[(i+1):,i] + # whereas v/ is stored in a[i,(i+1):] + + n = A.rows + for i in xrange(n - 1, 0, -1): + # scale the vector + + scale = 0 + for k in xrange(0, i): + scale += abs(A[k,i]) + + scale_inv = 0 + if scale != 0: + scale_inv = 1/scale + + # sadly there are floating point numbers not equal to zero whose reciprocal is infinity + + if i == 1 or scale == 0 or ctx.isinf(scale_inv): + E[i] = A[i-1,i] # nothing to do + D[i] = 0 + continue + + # calculate parameters for housholder transformation + + H = 0 + for k in xrange(0, i): + A[k,i] *= scale_inv + H += A[k,i] * A[k,i] + + F = A[i-1,i] + G = ctx.sqrt(H) + if F > 0: + G = -G + E[i] = scale * G + H -= F * G + A[i-1,i] = F - G + F = 0 + + # apply housholder transformation + + for j in xrange(0, i): + if calc_ev: + A[i,j] = A[j,i] / H + + G = 0 # calculate A*U + for k in xrange(0, j + 1): + G += A[k,j] * A[k,i] + for k in xrange(j + 1, i): + G += A[j,k] * A[k,i] + + E[j] = G / H # calculate P + F += E[j] * A[j,i] + + HH = F / (2 * H) + + for j in xrange(0, i): # calculate reduced A + F = A[j,i] + G = E[j] - HH * F # calculate Q + E[j] = G + + for k in xrange(0, j + 1): + A[k,j] -= F * E[k] + G * A[k,i] + + D[i] = H + + for i in xrange(1, n): # better for compatibility + E[i-1] = E[i] + E[n-1] = 0 + + if calc_ev: + D[0] = 0 + for i in xrange(0, n): + if D[i] != 0: + for j in xrange(0, i): # accumulate transformation matrices + G = 0 + for k in xrange(0, i): + G += A[i,k] * A[k,j] + for k in xrange(0, i): + A[k,j] -= G * A[k,i] + + D[i] = A[i,i] + A[i,i] = 1 + + for j in xrange(0, i): + A[j,i] = A[i,j] = 0 + else: + for i in xrange(0, n): + D[i] = A[i,i] + + + + + +def c_he_tridiag_0(ctx, A, D, E, T): + """ + This routine transforms a complex hermitian matrix A to a real symmetric + tridiagonal matrix T using an unitary similarity transformation: + Q' * A * Q = T (here ' denotes the hermitian matrix transpose, + i.e. transposition und conjugation). + The unitary matrix Q is build up from Householder reflectors and + an unitary diagonal matrix. + + parameters: + A (input/output) On input, A contains the complex hermitian matrix + of dimension (n,n). On output, A contains the unitary matrix Q + in compressed form. + + D (output) real array of length n, contains the diagonal elements + of the tridiagonal matrix. + + E (output) real array of length n, contains the offdiagonal elements + of the tridiagonal matrix in E[0:(n-1)] where is the dimension of + the matrix A. E[n-1] is undefined. + + T (output) complex array of length n, contains a unitary diagonal + matrix. + + This routine is a python translation (in slightly modified form) of the fortran + routine htridi.f in the software library EISPACK (see netlib.org) which itself + is a complex version of the algol procedure tred1 described in: + - Num. Math. 11, p.181-195 (1968) by Martin, Reinsch and Wilkonson + - Handbook for auto. comp., Vol II, Linear Algebra, p.212-226 (1971) + + For a good introduction to Householder reflections, see also + Stoer, Bulirsch - Introduction to Numerical Analysis. + """ + + n = A.rows + T[n-1] = 1 + for i in xrange(n - 1, 0, -1): + + # scale the vector + + scale = 0 + for k in xrange(0, i): + scale += abs(ctx.re(A[k,i])) + abs(ctx.im(A[k,i])) + + scale_inv = 0 + if scale != 0: + scale_inv = 1 / scale + + # sadly there are floating point numbers not equal to zero whose reciprocal is infinity + + if scale == 0 or ctx.isinf(scale_inv): + E[i] = 0 + D[i] = 0 + T[i-1] = 1 + continue + + if i == 1: + F = A[i-1,i] + f = abs(F) + E[i] = f + D[i] = 0 + if f != 0: + T[i-1] = T[i] * F / f + else: + T[i-1] = T[i] + continue + + # calculate parameters for housholder transformation + + H = 0 + for k in xrange(0, i): + A[k,i] *= scale_inv + rr = ctx.re(A[k,i]) + ii = ctx.im(A[k,i]) + H += rr * rr + ii * ii + + F = A[i-1,i] + f = abs(F) + G = ctx.sqrt(H) + H += G * f + E[i] = scale * G + if f != 0: + F = F / f + TZ = - T[i] * F # T[i-1]=-T[i]*F, but we need T[i-1] as temporary storage + G *= F + else: + TZ = -T[i] # T[i-1]=-T[i] + A[i-1,i] += G + F = 0 + + # apply housholder transformation + + for j in xrange(0, i): + A[i,j] = A[j,i] / H + + G = 0 # calculate A*U + for k in xrange(0, j + 1): + G += ctx.conj(A[k,j]) * A[k,i] + for k in xrange(j + 1, i): + G += A[j,k] * A[k,i] + + T[j] = G / H # calculate P + F += ctx.conj(T[j]) * A[j,i] + + HH = F / (2 * H) + + for j in xrange(0, i): # calculate reduced A + F = A[j,i] + G = T[j] - HH * F # calculate Q + T[j] = G + + for k in xrange(0, j + 1): + A[k,j] -= ctx.conj(F) * T[k] + ctx.conj(G) * A[k,i] + # as we use the lower left part for storage + # we have to use the transpose of the normal formula + + T[i-1] = TZ + D[i] = H + + for i in xrange(1, n): # better for compatibility + E[i-1] = E[i] + E[n-1] = 0 + + D[0] = 0 + for i in xrange(0, n): + zw = D[i] + D[i] = ctx.re(A[i,i]) + A[i,i] = zw + + + + + + + +def c_he_tridiag_1(ctx, A, T): + """ + This routine forms the unitary matrix Q described in c_he_tridiag_0. + + parameters: + A (input/output) On input, A is the same matrix as delivered by + c_he_tridiag_0. On output, A is set to Q. + + T (input) On input, T is the same array as delivered by c_he_tridiag_0. + + """ + + n = A.rows + + for i in xrange(0, n): + if A[i,i] != 0: + for j in xrange(0, i): + G = 0 + for k in xrange(0, i): + G += ctx.conj(A[i,k]) * A[k,j] + for k in xrange(0, i): + A[k,j] -= G * A[k,i] + + A[i,i] = 1 + + for j in xrange(0, i): + A[j,i] = A[i,j] = 0 + + for i in xrange(0, n): + for k in xrange(0, n): + A[i,k] *= T[k] + + + + +def c_he_tridiag_2(ctx, A, T, B): + """ + This routine applied the unitary matrix Q described in c_he_tridiag_0 + onto the the matrix B, i.e. it forms Q*B. + + parameters: + A (input) On input, A is the same matrix as delivered by c_he_tridiag_0. + + T (input) On input, T is the same array as delivered by c_he_tridiag_0. + + B (input/output) On input, B is a complex matrix. On output B is replaced + by Q*B. + + This routine is a python translation of the fortran routine htribk.f in the + software library EISPACK (see netlib.org). See c_he_tridiag_0 for more + references. + """ + + n = A.rows + + for i in xrange(0, n): + for k in xrange(0, n): + B[k,i] *= T[k] + + for i in xrange(0, n): + if A[i,i] != 0: + for j in xrange(0, n): + G = 0 + for k in xrange(0, i): + G += ctx.conj(A[i,k]) * B[k,j] + for k in xrange(0, i): + B[k,j] -= G * A[k,i] + + + + + +def tridiag_eigen(ctx, d, e, z = False): + """ + This subroutine find the eigenvalues and the first components of the + eigenvectors of a real symmetric tridiagonal matrix using the implicit + QL method. + + parameters: + + d (input/output) real array of length n. on input, d contains the diagonal + elements of the input matrix. on output, d contains the eigenvalues in + ascending order. + + e (input) real array of length n. on input, e contains the offdiagonal + elements of the input matrix in e[0:(n-1)]. On output, e has been + destroyed. + + z (input/output) If z is equal to False, no eigenvectors will be computed. + Otherwise on input z should have the format z[0:m,0:n] (i.e. a real or + complex matrix of dimension (m,n) ). On output this matrix will be + multiplied by the matrix of the eigenvectors (i.e. the columns of this + matrix are the eigenvectors): z --> z*EV + That means if z[i,j]={1 if j==j; 0 otherwise} on input, then on output + z will contain the first m components of the eigenvectors. That means + if m is equal to n, the i-th eigenvector will be z[:,i]. + + This routine is a python translation (in slightly modified form) of the + fortran routine imtql2.f in the software library EISPACK (see netlib.org) + which itself is based on the algol procudure imtql2 desribed in: + - num. math. 12, p. 377-383(1968) by matrin and wilkinson + - modified in num. math. 15, p. 450(1970) by dubrulle + - handbook for auto. comp., vol. II-linear algebra, p. 241-248 (1971) + See also the routine gaussq.f in netlog.org or acm algorithm 726. + """ + + n = len(d) + e[n-1] = 0 + iterlim = 2 * ctx.dps + + for l in xrange(n): + j = 0 + while 1: + m = l + while 1: + # look for a small subdiagonal element + if m + 1 == n: + break + if abs(e[m]) <= ctx.eps * (abs(d[m]) + abs(d[m + 1])): + break + m = m + 1 + if m == l: + break + + if j >= iterlim: + raise RuntimeError("tridiag_eigen: no convergence to an eigenvalue after %d iterations" % iterlim) + + j += 1 + + # form shift + + p = d[l] + g = (d[l + 1] - p) / (2 * e[l]) + r = ctx.hypot(g, 1) + + if g < 0: + s = g - r + else: + s = g + r + + g = d[m] - p + e[l] / s + + s, c, p = 1, 1, 0 + + for i in xrange(m - 1, l - 1, -1): + f = s * e[i] + b = c * e[i] + if abs(f) > abs(g): # this here is a slight improvement also used in gaussq.f or acm algorithm 726. + c = g / f + r = ctx.hypot(c, 1) + e[i + 1] = f * r + s = 1 / r + c = c * s + else: + s = f / g + r = ctx.hypot(s, 1) + e[i + 1] = g * r + c = 1 / r + s = s * c + g = d[i + 1] - p + r = (d[i] - g) * s + 2 * c * b + p = s * r + d[i + 1] = g + p + g = c * r - b + + if not isinstance(z, bool): + # calculate eigenvectors + for w in xrange(z.rows): + f = z[w,i+1] + z[w,i+1] = s * z[w,i] + c * f + z[w,i ] = c * z[w,i] - s * f + + d[l] = d[l] - p + e[l] = g + e[m] = 0 + + for ii in xrange(1, n): + # sort eigenvalues and eigenvectors (bubble-sort) + i = ii - 1 + k = i + p = d[i] + for j in xrange(ii, n): + if d[j] >= p: + continue + k = j + p = d[k] + if k == i: + continue + d[k] = d[i] + d[i] = p + + if not isinstance(z, bool): + for w in xrange(z.rows): + p = z[w,i] + z[w,i] = z[w,k] + z[w,k] = p + +######################################################################################## + +@defun +def eigsy(ctx, A, eigvals_only = False, overwrite_a = False): + """ + This routine solves the (ordinary) eigenvalue problem for a real symmetric + square matrix A. Given A, an orthogonal matrix Q is calculated which + diagonalizes A: + + Q' A Q = diag(E) and Q Q' = Q' Q = 1 + + Here diag(E) is a diagonal matrix whose diagonal is E. + ' denotes the transpose. + + The columns of Q are the eigenvectors of A and E contains the eigenvalues: + + A Q[:,i] = E[i] Q[:,i] + + + input: + + A: real matrix of format (n,n) which is symmetric + (i.e. A=A' or A[i,j]=A[j,i]) + + eigvals_only: if true, calculates only the eigenvalues E. + if false, calculates both eigenvectors and eigenvalues. + + overwrite_a: if true, allows modification of A which may improve + performance. if false, A is not modified. + + output: + + E: vector of format (n). contains the eigenvalues of A in ascending order. + + Q: orthogonal matrix of format (n,n). contains the eigenvectors + of A as columns. + + return value: + + E if eigvals_only is true + (E, Q) if eigvals_only is false + + example: + >>> from mpmath import mp + >>> A = mp.matrix([[3, 2], [2, 0]]) + >>> E = mp.eigsy(A, eigvals_only = True) + >>> print(E) + [-1.0] + [ 4.0] + + >>> A = mp.matrix([[1, 2], [2, 3]]) + >>> E, Q = mp.eigsy(A) + >>> print(mp.chop(A * Q[:,0] - E[0] * Q[:,0])) + [0.0] + [0.0] + + see also: eighe, eigh, eig + """ + + if not overwrite_a: + A = A.copy() + + d = ctx.zeros(A.rows, 1) + e = ctx.zeros(A.rows, 1) + + if eigvals_only: + r_sy_tridiag(ctx, A, d, e, calc_ev = False) + tridiag_eigen(ctx, d, e, False) + return d + else: + r_sy_tridiag(ctx, A, d, e, calc_ev = True) + tridiag_eigen(ctx, d, e, A) + return (d, A) + + +@defun +def eighe(ctx, A, eigvals_only = False, overwrite_a = False): + """ + This routine solves the (ordinary) eigenvalue problem for a complex + hermitian square matrix A. Given A, an unitary matrix Q is calculated which + diagonalizes A: + + Q' A Q = diag(E) and Q Q' = Q' Q = 1 + + Here diag(E) a is diagonal matrix whose diagonal is E. + ' denotes the hermitian transpose (i.e. ordinary transposition and + complex conjugation). + + The columns of Q are the eigenvectors of A and E contains the eigenvalues: + + A Q[:,i] = E[i] Q[:,i] + + + input: + + A: complex matrix of format (n,n) which is hermitian + (i.e. A=A' or A[i,j]=conj(A[j,i])) + + eigvals_only: if true, calculates only the eigenvalues E. + if false, calculates both eigenvectors and eigenvalues. + + overwrite_a: if true, allows modification of A which may improve + performance. if false, A is not modified. + + output: + + E: vector of format (n). contains the eigenvalues of A in ascending order. + + Q: unitary matrix of format (n,n). contains the eigenvectors + of A as columns. + + return value: + + E if eigvals_only is true + (E, Q) if eigvals_only is false + + example: + >>> from mpmath import mp + >>> A = mp.matrix([[1, -3 - 1j], [-3 + 1j, -2]]) + >>> E = mp.eighe(A, eigvals_only = True) + >>> print(E) + [-4.0] + [ 3.0] + + >>> A = mp.matrix([[1, 2 + 5j], [2 - 5j, 3]]) + >>> E, Q = mp.eighe(A) + >>> print(mp.chop(A * Q[:,0] - E[0] * Q[:,0])) + [0.0] + [0.0] + + see also: eigsy, eigh, eig + """ + + if not overwrite_a: + A = A.copy() + + d = ctx.zeros(A.rows, 1) + e = ctx.zeros(A.rows, 1) + t = ctx.zeros(A.rows, 1) + + if eigvals_only: + c_he_tridiag_0(ctx, A, d, e, t) + tridiag_eigen(ctx, d, e, False) + return d + else: + c_he_tridiag_0(ctx, A, d, e, t) + B = ctx.eye(A.rows) + tridiag_eigen(ctx, d, e, B) + c_he_tridiag_2(ctx, A, t, B) + return (d, B) + +@defun +def eigh(ctx, A, eigvals_only = False, overwrite_a = False): + """ + "eigh" is a unified interface for "eigsy" and "eighe". Depending on + whether A is real or complex the appropriate function is called. + + This routine solves the (ordinary) eigenvalue problem for a real symmetric + or complex hermitian square matrix A. Given A, an orthogonal (A real) or + unitary (A complex) matrix Q is calculated which diagonalizes A: + + Q' A Q = diag(E) and Q Q' = Q' Q = 1 + + Here diag(E) a is diagonal matrix whose diagonal is E. + ' denotes the hermitian transpose (i.e. ordinary transposition and + complex conjugation). + + The columns of Q are the eigenvectors of A and E contains the eigenvalues: + + A Q[:,i] = E[i] Q[:,i] + + input: + + A: a real or complex square matrix of format (n,n) which is symmetric + (i.e. A[i,j]=A[j,i]) or hermitian (i.e. A[i,j]=conj(A[j,i])). + + eigvals_only: if true, calculates only the eigenvalues E. + if false, calculates both eigenvectors and eigenvalues. + + overwrite_a: if true, allows modification of A which may improve + performance. if false, A is not modified. + + output: + + E: vector of format (n). contains the eigenvalues of A in ascending order. + + Q: an orthogonal or unitary matrix of format (n,n). contains the + eigenvectors of A as columns. + + return value: + + E if eigvals_only is true + (E, Q) if eigvals_only is false + + example: + >>> from mpmath import mp + >>> A = mp.matrix([[3, 2], [2, 0]]) + >>> E = mp.eigh(A, eigvals_only = True) + >>> print(E) + [-1.0] + [ 4.0] + + >>> A = mp.matrix([[1, 2], [2, 3]]) + >>> E, Q = mp.eigh(A) + >>> print(mp.chop(A * Q[:,0] - E[0] * Q[:,0])) + [0.0] + [0.0] + + >>> A = mp.matrix([[1, 2 + 5j], [2 - 5j, 3]]) + >>> E, Q = mp.eigh(A) + >>> print(mp.chop(A * Q[:,0] - E[0] * Q[:,0])) + [0.0] + [0.0] + + see also: eigsy, eighe, eig + """ + + iscomplex = any(type(x) is ctx.mpc for x in A) + + if iscomplex: + return ctx.eighe(A, eigvals_only = eigvals_only, overwrite_a = overwrite_a) + else: + return ctx.eigsy(A, eigvals_only = eigvals_only, overwrite_a = overwrite_a) + + +@defun +def gauss_quadrature(ctx, n, qtype = "legendre", alpha = 0, beta = 0): + """ + This routine calulates gaussian quadrature rules for different + families of orthogonal polynomials. Let (a, b) be an interval, + W(x) a positive weight function and n a positive integer. + Then the purpose of this routine is to calculate pairs (x_k, w_k) + for k=0, 1, 2, ... (n-1) which give + + int(W(x) * F(x), x = a..b) = sum(w_k * F(x_k),k = 0..(n-1)) + + exact for all polynomials F(x) of degree (strictly) less than 2*n. For all + integrable functions F(x) the sum is a (more or less) good approximation to + the integral. The x_k are called nodes (which are the zeros of the + related orthogonal polynomials) and the w_k are called the weights. + + parameters + n (input) The degree of the quadrature rule, i.e. its number of + nodes. + + qtype (input) The family of orthogonal polynmomials for which to + compute the quadrature rule. See the list below. + + alpha (input) real number, used as parameter for some orthogonal + polynomials + + beta (input) real number, used as parameter for some orthogonal + polynomials. + + return value + + (X, W) a pair of two real arrays where x_k = X[k] and w_k = W[k]. + + + orthogonal polynomials: + + qtype polynomial + ----- ---------- + + "legendre" Legendre polynomials, W(x)=1 on the interval (-1, +1) + "legendre01" shifted Legendre polynomials, W(x)=1 on the interval (0, +1) + "hermite" Hermite polynomials, W(x)=exp(-x*x) on (-infinity,+infinity) + "laguerre" Laguerre polynomials, W(x)=exp(-x) on (0,+infinity) + "glaguerre" generalized Laguerre polynomials, W(x)=exp(-x)*x**alpha + on (0, +infinity) + "chebyshev1" Chebyshev polynomials of the first kind, W(x)=1/sqrt(1-x*x) + on (-1, +1) + "chebyshev2" Chebyshev polynomials of the second kind, W(x)=sqrt(1-x*x) + on (-1, +1) + "jacobi" Jacobi polynomials, W(x)=(1-x)**alpha * (1+x)**beta on (-1, +1) + with alpha>-1 and beta>-1 + + examples: + >>> from mpmath import mp + >>> f = lambda x: x**8 + 2 * x**6 - 3 * x**4 + 5 * x**2 - 7 + >>> X, W = mp.gauss_quadrature(5, "hermite") + >>> A = mp.fdot([(f(x), w) for x, w in zip(X, W)]) + >>> B = mp.sqrt(mp.pi) * 57 / 16 + >>> C = mp.quad(lambda x: mp.exp(- x * x) * f(x), [-mp.inf, +mp.inf]) + >>> mp.nprint((mp.chop(A-B, tol = 1e-10), mp.chop(A-C, tol = 1e-10))) + (0.0, 0.0) + + >>> f = lambda x: x**5 - 2 * x**4 + 3 * x**3 - 5 * x**2 + 7 * x - 11 + >>> X, W = mp.gauss_quadrature(3, "laguerre") + >>> A = mp.fdot([(f(x), w) for x, w in zip(X, W)]) + >>> B = 76 + >>> C = mp.quad(lambda x: mp.exp(-x) * f(x), [0, +mp.inf]) + >>> mp.nprint(mp.chop(A-B, tol = 1e-10), mp.chop(A-C, tol = 1e-10)) + .0 + + # orthogonality of the chebyshev polynomials: + >>> f = lambda x: mp.chebyt(3, x) * mp.chebyt(2, x) + >>> X, W = mp.gauss_quadrature(3, "chebyshev1") + >>> A = mp.fdot([(f(x), w) for x, w in zip(X, W)]) + >>> print(mp.chop(A, tol = 1e-10)) + 0.0 + + references: + - golub and welsch, "calculations of gaussian quadrature rules", mathematics of + computation 23, p. 221-230 (1969) + - golub, "some modified matrix eigenvalue problems", siam review 15, p. 318-334 (1973) + - stroud and secrest, "gaussian quadrature formulas", prentice-hall (1966) + + See also the routine gaussq.f in netlog.org or ACM Transactions on + Mathematical Software algorithm 726. + """ + + d = ctx.zeros(n, 1) + e = ctx.zeros(n, 1) + z = ctx.zeros(1, n) + + z[0,0] = 1 + + if qtype == "legendre": + # legendre on the range -1 +1 , abramowitz, table 25.4, p.916 + w = 2 + for i in xrange(n): + j = i + 1 + e[i] = ctx.sqrt(j * j / (4 * j * j - ctx.mpf(1))) + elif qtype == "legendre01": + # legendre shifted to 0 1 , abramowitz, table 25.8, p.921 + w = 1 + for i in xrange(n): + d[i] = 1 / ctx.mpf(2) + j = i + 1 + e[i] = ctx.sqrt(j * j / (16 * j * j - ctx.mpf(4))) + elif qtype == "hermite": + # hermite on the range -inf +inf , abramowitz, table 25.10,p.924 + w = ctx.sqrt(ctx.pi) + for i in xrange(n): + j = i + 1 + e[i] = ctx.sqrt(j / ctx.mpf(2)) + elif qtype == "laguerre": + # laguerre on the range 0 +inf , abramowitz, table 25.9, p. 923 + w = 1 + for i in xrange(n): + j = i + 1 + d[i] = 2 * j - 1 + e[i] = j + elif qtype=="chebyshev1": + # chebyshev polynimials of the first kind + w = ctx.pi + for i in xrange(n): + e[i] = 1 / ctx.mpf(2) + e[0] = ctx.sqrt(1 / ctx.mpf(2)) + elif qtype == "chebyshev2": + # chebyshev polynimials of the second kind + w = ctx.pi / 2 + for i in xrange(n): + e[i] = 1 / ctx.mpf(2) + elif qtype == "glaguerre": + # generalized laguerre on the range 0 +inf + w = ctx.gamma(1 + alpha) + for i in xrange(n): + j = i + 1 + d[i] = 2 * j - 1 + alpha + e[i] = ctx.sqrt(j * (j + alpha)) + elif qtype == "jacobi": + # jacobi polynomials + alpha = ctx.mpf(alpha) + beta = ctx.mpf(beta) + ab = alpha + beta + abi = ab + 2 + w = (2**(ab+1)) * ctx.gamma(alpha + 1) * ctx.gamma(beta + 1) / ctx.gamma(abi) + d[0] = (beta - alpha) / abi + e[0] = ctx.sqrt(4 * (1 + alpha) * (1 + beta) / ((abi + 1) * (abi * abi))) + a2b2 = beta * beta - alpha * alpha + for i in xrange(1, n): + j = i + 1 + abi = 2 * j + ab + d[i] = a2b2 / ((abi - 2) * abi) + e[i] = ctx.sqrt(4 * j * (j + alpha) * (j + beta) * (j + ab) / ((abi * abi - 1) * abi * abi)) + elif isinstance(qtype, str): + raise ValueError("unknown quadrature rule \"%s\"" % qtype) + elif not isinstance(qtype, str): + w = qtype(d, e) + else: + assert 0 + + tridiag_eigen(ctx, d, e, z) + + for i in xrange(len(z)): + z[i] *= z[i] + + z = z.transpose() + return (d, w * z) + +################################################################################################## +################################################################################################## +################################################################################################## + +def svd_r_raw(ctx, A, V = False, calc_u = False): + """ + This routine computes the singular value decomposition of a matrix A. + Given A, two orthogonal matrices U and V are calculated such that + + A = U S V + + where S is a suitable shaped matrix whose off-diagonal elements are zero. + The diagonal elements of S are the singular values of A, i.e. the + squareroots of the eigenvalues of A' A or A A'. Here ' denotes the transpose. + Householder bidiagonalization and a variant of the QR algorithm is used. + + overview of the matrices : + + A : m*n A gets replaced by U + U : m*n U replaces A. If n>m then only the first m*m block of U is + non-zero. column-orthogonal: U' U = B + here B is a n*n matrix whose first min(m,n) diagonal + elements are 1 and all other elements are zero. + S : n*n diagonal matrix, only the diagonal elements are stored in + the array S. only the first min(m,n) diagonal elements are non-zero. + V : n*n orthogonal: V V' = V' V = 1 + + parameters: + A (input/output) On input, A contains a real matrix of shape m*n. + On output, if calc_u is true A contains the column-orthogonal + matrix U; otherwise A is simply used as workspace and thus destroyed. + + V (input/output) if false, the matrix V is not calculated. otherwise + V must be a matrix of shape n*n. + + calc_u (input) If true, the matrix U is calculated and replaces A. + if false, U is not calculated and A is simply destroyed + + return value: + S an array of length n containing the singular values of A sorted by + decreasing magnitude. only the first min(m,n) elements are non-zero. + + This routine is a python translation of the fortran routine svd.f in the + software library EISPACK (see netlib.org) which itself is based on the + algol procedure svd described in: + - num. math. 14, 403-420(1970) by golub and reinsch. + - wilkinson/reinsch: handbook for auto. comp., vol ii-linear algebra, 134-151(1971). + + """ + + m, n = A.rows, A.cols + + S = ctx.zeros(n, 1) + + # work is a temporary array of size n + work = ctx.zeros(n, 1) + + g = scale = anorm = 0 + maxits = 3 * ctx.dps + + for i in xrange(n): # householder reduction to bidiagonal form + work[i] = scale*g + g = s = scale = 0 + if i < m: + for k in xrange(i, m): + scale += ctx.fabs(A[k,i]) + if scale != 0: + for k in xrange(i, m): + A[k,i] /= scale + s += A[k,i] * A[k,i] + f = A[i,i] + g = -ctx.sqrt(s) + if f < 0: + g = -g + h = f * g - s + A[i,i] = f - g + for j in xrange(i+1, n): + s = 0 + for k in xrange(i, m): + s += A[k,i] * A[k,j] + f = s / h + for k in xrange(i, m): + A[k,j] += f * A[k,i] + for k in xrange(i,m): + A[k,i] *= scale + + S[i] = scale * g + g = s = scale = 0 + + if i < m and i != n - 1: + for k in xrange(i+1, n): + scale += ctx.fabs(A[i,k]) + if scale: + for k in xrange(i+1, n): + A[i,k] /= scale + s += A[i,k] * A[i,k] + f = A[i,i+1] + g = -ctx.sqrt(s) + if f < 0: + g = -g + h = f * g - s + A[i,i+1] = f - g + + for k in xrange(i+1, n): + work[k] = A[i,k] / h + + for j in xrange(i+1, m): + s = 0 + for k in xrange(i+1, n): + s += A[j,k] * A[i,k] + for k in xrange(i+1, n): + A[j,k] += s * work[k] + + for k in xrange(i+1, n): + A[i,k] *= scale + + anorm = max(anorm, ctx.fabs(S[i]) + ctx.fabs(work[i])) + + if not isinstance(V, bool): + for i in xrange(n-2, -1, -1): # accumulation of right hand transformations + V[i+1,i+1] = 1 + + if work[i+1] != 0: + for j in xrange(i+1, n): + V[i,j] = (A[i,j] / A[i,i+1]) / work[i+1] + for j in xrange(i+1, n): + s = 0 + for k in xrange(i+1, n): + s += A[i,k] * V[j,k] + for k in xrange(i+1, n): + V[j,k] += s * V[i,k] + + for j in xrange(i+1, n): + V[j,i] = V[i,j] = 0 + + V[0,0] = 1 + + if m= maxits: + raise RuntimeError("svd: no convergence to an eigenvalue after %d iterations" % its) + + x = S[l] # shift from bottom 2 by 2 minor + nm = k-1 + y = S[nm] + g = work[nm] + h = work[k] + f = ((y - z) * (y + z) + (g - h) * (g + h))/(2 * h * y) + g = ctx.hypot(f, 1) + if f >= 0: f = ((x - z) * (x + z) + h * ((y / (f + g)) - h)) / x + else: f = ((x - z) * (x + z) + h * ((y / (f - g)) - h)) / x + + c = s = 1 # next qt transformation + + for j in xrange(l, nm + 1): + g = work[j+1] + y = S[j+1] + h = s * g + g = c * g + z = ctx.hypot(f, h) + work[j] = z + c = f / z + s = h / z + f = x * c + g * s + g = g * c - x * s + h = y * s + y *= c + if not isinstance(V, bool): + for jj in xrange(n): + x = V[j ,jj] + z = V[j+1,jj] + V[j ,jj]= x * c + z * s + V[j+1 ,jj]= z * c - x * s + z = ctx.hypot(f, h) + S[j] = z + if z != 0: # rotation can be arbitray if z=0 + z = 1 / z + c = f * z + s = h * z + f = c * g + s * y + x = c * y - s * g + + if calc_u: + for jj in xrange(m): + y = A[jj,j ] + z = A[jj,j+1] + A[jj,j ] = y * c + z * s + A[jj,j+1 ] = z * c - y * s + + work[l] = 0 + work[k] = f + S[k] = x + + ########################## + + # Sort singular values into decreasing order (bubble-sort) + + for i in xrange(n): + imax = i + s = ctx.fabs(S[i]) # s is the current maximal element + + for j in xrange(i + 1, n): + c = ctx.fabs(S[j]) + if c > s: + s = c + imax = j + + if imax != i: + # swap singular values + + z = S[i] + S[i] = S[imax] + S[imax] = z + + if calc_u: + for j in xrange(m): + z = A[j,i] + A[j,i] = A[j,imax] + A[j,imax] = z + + if not isinstance(V, bool): + for j in xrange(n): + z = V[i,j] + V[i,j] = V[imax,j] + V[imax,j] = z + + return S + +####################### + +def svd_c_raw(ctx, A, V = False, calc_u = False): + """ + This routine computes the singular value decomposition of a matrix A. + Given A, two unitary matrices U and V are calculated such that + + A = U S V + + where S is a suitable shaped matrix whose off-diagonal elements are zero. + The diagonal elements of S are the singular values of A, i.e. the + squareroots of the eigenvalues of A' A or A A'. Here ' denotes the hermitian + transpose (i.e. transposition and conjugation). Householder bidiagonalization + and a variant of the QR algorithm is used. + + overview of the matrices : + + A : m*n A gets replaced by U + U : m*n U replaces A. If n>m then only the first m*m block of U is + non-zero. column-unitary: U' U = B + here B is a n*n matrix whose first min(m,n) diagonal + elements are 1 and all other elements are zero. + S : n*n diagonal matrix, only the diagonal elements are stored in + the array S. only the first min(m,n) diagonal elements are non-zero. + V : n*n unitary: V V' = V' V = 1 + + parameters: + A (input/output) On input, A contains a complex matrix of shape m*n. + On output, if calc_u is true A contains the column-unitary + matrix U; otherwise A is simply used as workspace and thus destroyed. + + V (input/output) if false, the matrix V is not calculated. otherwise + V must be a matrix of shape n*n. + + calc_u (input) If true, the matrix U is calculated and replaces A. + if false, U is not calculated and A is simply destroyed + + return value: + S an array of length n containing the singular values of A sorted by + decreasing magnitude. only the first min(m,n) elements are non-zero. + + This routine is a python translation of the fortran routine svd.f in the + software library EISPACK (see netlib.org) which itself is based on the + algol procedure svd described in: + - num. math. 14, 403-420(1970) by golub and reinsch. + - wilkinson/reinsch: handbook for auto. comp., vol ii-linear algebra, 134-151(1971). + + """ + + m, n = A.rows, A.cols + + S = ctx.zeros(n, 1) + + # work is a temporary array of size n + work = ctx.zeros(n, 1) + lbeta = ctx.zeros(n, 1) + rbeta = ctx.zeros(n, 1) + dwork = ctx.zeros(n, 1) + + g = scale = anorm = 0 + maxits = 3 * ctx.dps + + for i in xrange(n): # householder reduction to bidiagonal form + dwork[i] = scale * g # dwork are the side-diagonal elements + g = s = scale = 0 + if i < m: + for k in xrange(i, m): + scale += ctx.fabs(ctx.re(A[k,i])) + ctx.fabs(ctx.im(A[k,i])) + if scale != 0: + for k in xrange(i, m): + A[k,i] /= scale + ar = ctx.re(A[k,i]) + ai = ctx.im(A[k,i]) + s += ar * ar + ai * ai + f = A[i,i] + g = -ctx.sqrt(s) + if ctx.re(f) < 0: + beta = -g - ctx.conj(f) + g = -g + else: + beta = -g + ctx.conj(f) + beta /= ctx.conj(beta) + beta += 1 + h = 2 * (ctx.re(f) * g - s) + A[i,i] = f - g + beta /= h + lbeta[i] = (beta / scale) / scale + for j in xrange(i+1, n): + s = 0 + for k in xrange(i, m): + s += ctx.conj(A[k,i]) * A[k,j] + f = beta * s + for k in xrange(i, m): + A[k,j] += f * A[k,i] + for k in xrange(i, m): + A[k,i] *= scale + + S[i] = scale * g # S are the diagonal elements + g = s = scale = 0 + + if i < m and i != n - 1: + for k in xrange(i+1, n): + scale += ctx.fabs(ctx.re(A[i,k])) + ctx.fabs(ctx.im(A[i,k])) + if scale: + for k in xrange(i+1, n): + A[i,k] /= scale + ar = ctx.re(A[i,k]) + ai = ctx.im(A[i,k]) + s += ar * ar + ai * ai + f = A[i,i+1] + g = -ctx.sqrt(s) + if ctx.re(f) < 0: + beta = -g - ctx.conj(f) + g = -g + else: + beta = -g + ctx.conj(f) + + beta /= ctx.conj(beta) + beta += 1 + + h = 2 * (ctx.re(f) * g - s) + A[i,i+1] = f - g + + beta /= h + rbeta[i] = (beta / scale) / scale + + for k in xrange(i+1, n): + work[k] = A[i, k] + + for j in xrange(i+1, m): + s = 0 + for k in xrange(i+1, n): + s += ctx.conj(A[i,k]) * A[j,k] + f = s * beta + for k in xrange(i+1,n): + A[j,k] += f * work[k] + + for k in xrange(i+1, n): + A[i,k] *= scale + + anorm = max(anorm,ctx.fabs(S[i]) + ctx.fabs(dwork[i])) + + if not isinstance(V, bool): + for i in xrange(n-2, -1, -1): # accumulation of right hand transformations + V[i+1,i+1] = 1 + + if dwork[i+1] != 0: + f = ctx.conj(rbeta[i]) + for j in xrange(i+1, n): + V[i,j] = A[i,j] * f + for j in xrange(i+1, n): + s = 0 + for k in xrange(i+1, n): + s += ctx.conj(A[i,k]) * V[j,k] + for k in xrange(i+1, n): + V[j,k] += s * V[i,k] + + for j in xrange(i+1,n): + V[j,i] = V[i,j] = 0 + + V[0,0] = 1 + + if m < n : minnm = m + else : minnm = n + + if calc_u: + for i in xrange(minnm-1, -1, -1): # accumulation of left hand transformations + g = S[i] + for j in xrange(i+1, n): + A[i,j] = 0 + if g != 0: + g = 1 / g + for j in xrange(i+1, n): + s = 0 + for k in xrange(i+1, m): + s += ctx.conj(A[k,i]) * A[k,j] + f = s * ctx.conj(lbeta[i]) + for k in xrange(i, m): + A[k,j] += f * A[k,i] + for j in xrange(i, m): + A[j,i] *= g + else: + for j in xrange(i, m): + A[j,i] = 0 + A[i,i] += 1 + + for k in xrange(n-1, -1, -1): + # diagonalization of the bidiagonal form: + # loop over singular values, and over allowed itations + + its = 0 + while 1: + its += 1 + flag = True + + for l in xrange(k, -1, -1): + nm = l - 1 + + if ctx.fabs(dwork[l]) + anorm == anorm: + flag = False + break + + if ctx.fabs(S[nm]) + anorm == anorm: + break + + if flag: + c = 0 + s = 1 + for i in xrange(l, k+1): + f = s * dwork[i] + dwork[i] *= c + if ctx.fabs(f) + anorm == anorm: + break + g = S[i] + h = ctx.hypot(f, g) + S[i] = h + h = 1 / h + c = g * h + s = -f * h + + if calc_u: + for j in xrange(m): + y = A[j,nm] + z = A[j,i] + A[j,nm]= y * c + z * s + A[j,i] = z * c - y * s + + z = S[k] + + if l == k: # convergence + if z < 0: # singular value is made nonnegative + S[k] = -z + if not isinstance(V, bool): + for j in xrange(n): + V[k,j] = -V[k,j] + break + + if its >= maxits: + raise RuntimeError("svd: no convergence to an eigenvalue after %d iterations" % its) + + x = S[l] # shift from bottom 2 by 2 minor + nm = k-1 + y = S[nm] + g = dwork[nm] + h = dwork[k] + f = ((y - z) * (y + z) + (g - h) * (g + h)) / (2 * h * y) + g = ctx.hypot(f, 1) + if f >=0: f = (( x - z) *( x + z) + h *((y / (f + g)) - h)) / x + else: f = (( x - z) *( x + z) + h *((y / (f - g)) - h)) / x + + c = s = 1 # next qt transformation + + for j in xrange(l, nm + 1): + g = dwork[j+1] + y = S[j+1] + h = s * g + g = c * g + z = ctx.hypot(f, h) + dwork[j] = z + c = f / z + s = h / z + f = x * c + g * s + g = g * c - x * s + h = y * s + y *= c + if not isinstance(V, bool): + for jj in xrange(n): + x = V[j ,jj] + z = V[j+1,jj] + V[j ,jj]= x * c + z * s + V[j+1,jj ]= z * c - x * s + z = ctx.hypot(f, h) + S[j] = z + if z != 0: # rotation can be arbitray if z=0 + z = 1 / z + c = f * z + s = h * z + f = c * g + s * y + x = c * y - s * g + if calc_u: + for jj in xrange(m): + y = A[jj,j ] + z = A[jj,j+1] + A[jj,j ]= y * c + z * s + A[jj,j+1 ]= z * c - y * s + + dwork[l] = 0 + dwork[k] = f + S[k] = x + + ########################## + + # Sort singular values into decreasing order (bubble-sort) + + for i in xrange(n): + imax = i + s = ctx.fabs(S[i]) # s is the current maximal element + + for j in xrange(i + 1, n): + c = ctx.fabs(S[j]) + if c > s: + s = c + imax = j + + if imax != i: + # swap singular values + + z = S[i] + S[i] = S[imax] + S[imax] = z + + if calc_u: + for j in xrange(m): + z = A[j,i] + A[j,i] = A[j,imax] + A[j,imax] = z + + if not isinstance(V, bool): + for j in xrange(n): + z = V[i,j] + V[i,j] = V[imax,j] + V[imax,j] = z + + return S + +################################################################################################## + +@defun +def svd_r(ctx, A, full_matrices = False, compute_uv = True, overwrite_a = False): + """ + This routine computes the singular value decomposition of a matrix A. + Given A, two orthogonal matrices U and V are calculated such that + + A = U S V and U' U = 1 and V V' = 1 + + where S is a suitable shaped matrix whose off-diagonal elements are zero. + Here ' denotes the transpose. The diagonal elements of S are the singular + values of A, i.e. the squareroots of the eigenvalues of A' A or A A'. + + input: + A : a real matrix of shape (m, n) + full_matrices : if true, U and V are of shape (m, m) and (n, n). + if false, U and V are of shape (m, min(m, n)) and (min(m, n), n). + compute_uv : if true, U and V are calculated. if false, only S is calculated. + overwrite_a : if true, allows modification of A which may improve + performance. if false, A is not modified. + + output: + U : an orthogonal matrix: U' U = 1. if full_matrices is true, U is of + shape (m, m). ortherwise it is of shape (m, min(m, n)). + + S : an array of length min(m, n) containing the singular values of A sorted by + decreasing magnitude. + + V : an orthogonal matrix: V V' = 1. if full_matrices is true, V is of + shape (n, n). ortherwise it is of shape (min(m, n), n). + + return value: + + S if compute_uv is false + (U, S, V) if compute_uv is true + + overview of the matrices: + + full_matrices true: + A : m*n + U : m*m U' U = 1 + S as matrix : m*n + V : n*n V V' = 1 + + full_matrices false: + A : m*n + U : m*min(n,m) U' U = 1 + S as matrix : min(m,n)*min(m,n) + V : min(m,n)*n V V' = 1 + + examples: + + >>> from mpmath import mp + >>> A = mp.matrix([[2, -2, -1], [3, 4, -2], [-2, -2, 0]]) + >>> S = mp.svd_r(A, compute_uv = False) + >>> print(S) + [6.0] + [3.0] + [1.0] + + >>> U, S, V = mp.svd_r(A) + >>> print(mp.chop(A - U * mp.diag(S) * V)) + [0.0 0.0 0.0] + [0.0 0.0 0.0] + [0.0 0.0 0.0] + + + see also: svd, svd_c + """ + + m, n = A.rows, A.cols + + if not compute_uv: + if not overwrite_a: + A = A.copy() + S = svd_r_raw(ctx, A, V = False, calc_u = False) + S = S[:min(m,n)] + return S + + if full_matrices and n < m: + V = ctx.zeros(m, m) + A0 = ctx.zeros(m, m) + A0[:,:n] = A + S = svd_r_raw(ctx, A0, V, calc_u = True) + + S = S[:n] + V = V[:n,:n] + + return (A0, S, V) + else: + if not overwrite_a: + A = A.copy() + V = ctx.zeros(n, n) + S = svd_r_raw(ctx, A, V, calc_u = True) + + if n > m: + if full_matrices == False: + V = V[:m,:] + + S = S[:m] + A = A[:,:m] + + return (A, S, V) + +############################## + +@defun +def svd_c(ctx, A, full_matrices = False, compute_uv = True, overwrite_a = False): + """ + This routine computes the singular value decomposition of a matrix A. + Given A, two unitary matrices U and V are calculated such that + + A = U S V and U' U = 1 and V V' = 1 + + where S is a suitable shaped matrix whose off-diagonal elements are zero. + Here ' denotes the hermitian transpose (i.e. transposition and complex + conjugation). The diagonal elements of S are the singular values of A, + i.e. the squareroots of the eigenvalues of A' A or A A'. + + input: + A : a complex matrix of shape (m, n) + full_matrices : if true, U and V are of shape (m, m) and (n, n). + if false, U and V are of shape (m, min(m, n)) and (min(m, n), n). + compute_uv : if true, U and V are calculated. if false, only S is calculated. + overwrite_a : if true, allows modification of A which may improve + performance. if false, A is not modified. + + output: + U : an unitary matrix: U' U = 1. if full_matrices is true, U is of + shape (m, m). ortherwise it is of shape (m, min(m, n)). + + S : an array of length min(m, n) containing the singular values of A sorted by + decreasing magnitude. + + V : an unitary matrix: V V' = 1. if full_matrices is true, V is of + shape (n, n). ortherwise it is of shape (min(m, n), n). + + return value: + + S if compute_uv is false + (U, S, V) if compute_uv is true + + overview of the matrices: + + full_matrices true: + A : m*n + U : m*m U' U = 1 + S as matrix : m*n + V : n*n V V' = 1 + + full_matrices false: + A : m*n + U : m*min(n,m) U' U = 1 + S as matrix : min(m,n)*min(m,n) + V : min(m,n)*n V V' = 1 + + example: + >>> from mpmath import mp + >>> A = mp.matrix([[-2j, -1-3j, -2+2j], [2-2j, -1-3j, 1], [-3+1j,-2j,0]]) + >>> S = mp.svd_c(A, compute_uv = False) + >>> print(mp.chop(S - mp.matrix([mp.sqrt(34), mp.sqrt(15), mp.sqrt(6)]))) + [0.0] + [0.0] + [0.0] + + >>> U, S, V = mp.svd_c(A) + >>> print(mp.chop(A - U * mp.diag(S) * V)) + [0.0 0.0 0.0] + [0.0 0.0 0.0] + [0.0 0.0 0.0] + + see also: svd, svd_r + """ + + m, n = A.rows, A.cols + + if not compute_uv: + if not overwrite_a: + A = A.copy() + S = svd_c_raw(ctx, A, V = False, calc_u = False) + S = S[:min(m,n)] + return S + + if full_matrices and n < m: + V = ctx.zeros(m, m) + A0 = ctx.zeros(m, m) + A0[:,:n] = A + S = svd_c_raw(ctx, A0, V, calc_u = True) + + S = S[:n] + V = V[:n,:n] + + return (A0, S, V) + else: + if not overwrite_a: + A = A.copy() + V = ctx.zeros(n, n) + S = svd_c_raw(ctx, A, V, calc_u = True) + + if n > m: + if full_matrices == False: + V = V[:m,:] + + S = S[:m] + A = A[:,:m] + + return (A, S, V) + +@defun +def svd(ctx, A, full_matrices = False, compute_uv = True, overwrite_a = False): + """ + "svd" is a unified interface for "svd_r" and "svd_c". Depending on + whether A is real or complex the appropriate function is called. + + This routine computes the singular value decomposition of a matrix A. + Given A, two orthogonal (A real) or unitary (A complex) matrices U and V + are calculated such that + + A = U S V and U' U = 1 and V V' = 1 + + where S is a suitable shaped matrix whose off-diagonal elements are zero. + Here ' denotes the hermitian transpose (i.e. transposition and complex + conjugation). The diagonal elements of S are the singular values of A, + i.e. the squareroots of the eigenvalues of A' A or A A'. + + input: + A : a real or complex matrix of shape (m, n) + full_matrices : if true, U and V are of shape (m, m) and (n, n). + if false, U and V are of shape (m, min(m, n)) and (min(m, n), n). + compute_uv : if true, U and V are calculated. if false, only S is calculated. + overwrite_a : if true, allows modification of A which may improve + performance. if false, A is not modified. + + output: + U : an orthogonal or unitary matrix: U' U = 1. if full_matrices is true, U is of + shape (m, m). ortherwise it is of shape (m, min(m, n)). + + S : an array of length min(m, n) containing the singular values of A sorted by + decreasing magnitude. + + V : an orthogonal or unitary matrix: V V' = 1. if full_matrices is true, V is of + shape (n, n). ortherwise it is of shape (min(m, n), n). + + return value: + + S if compute_uv is false + (U, S, V) if compute_uv is true + + overview of the matrices: + + full_matrices true: + A : m*n + U : m*m U' U = 1 + S as matrix : m*n + V : n*n V V' = 1 + + full_matrices false: + A : m*n + U : m*min(n,m) U' U = 1 + S as matrix : min(m,n)*min(m,n) + V : min(m,n)*n V V' = 1 + + examples: + + >>> from mpmath import mp + >>> A = mp.matrix([[2, -2, -1], [3, 4, -2], [-2, -2, 0]]) + >>> S = mp.svd(A, compute_uv = False) + >>> print(S) + [6.0] + [3.0] + [1.0] + + >>> U, S, V = mp.svd(A) + >>> print(mp.chop(A - U * mp.diag(S) * V)) + [0.0 0.0 0.0] + [0.0 0.0 0.0] + [0.0 0.0 0.0] + + see also: svd_r, svd_c + """ + + iscomplex = any(type(x) is ctx.mpc for x in A) + + if iscomplex: + return ctx.svd_c(A, full_matrices = full_matrices, compute_uv = compute_uv, overwrite_a = overwrite_a) + else: + return ctx.svd_r(A, full_matrices = full_matrices, compute_uv = compute_uv, overwrite_a = overwrite_a) diff --git a/lib/python3.12/site-packages/mpmath/matrices/linalg.py b/lib/python3.12/site-packages/mpmath/matrices/linalg.py new file mode 100644 index 0000000000000000000000000000000000000000..e2fe643e809822e3d05a52b73c965edb622f9af9 --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/matrices/linalg.py @@ -0,0 +1,790 @@ +""" +Linear algebra +-------------- + +Linear equations +................ + +Basic linear algebra is implemented; you can for example solve the linear +equation system:: + + x + 2*y = -10 + 3*x + 4*y = 10 + +using ``lu_solve``:: + + >>> from mpmath import * + >>> mp.pretty = False + >>> A = matrix([[1, 2], [3, 4]]) + >>> b = matrix([-10, 10]) + >>> x = lu_solve(A, b) + >>> x + matrix( + [['30.0'], + ['-20.0']]) + +If you don't trust the result, use ``residual`` to calculate the residual ||A*x-b||:: + + >>> residual(A, x, b) + matrix( + [['3.46944695195361e-18'], + ['3.46944695195361e-18']]) + >>> str(eps) + '2.22044604925031e-16' + +As you can see, the solution is quite accurate. The error is caused by the +inaccuracy of the internal floating point arithmetic. Though, it's even smaller +than the current machine epsilon, which basically means you can trust the +result. + +If you need more speed, use NumPy, or ``fp.lu_solve`` for a floating-point computation. + + >>> fp.lu_solve(A, b) # doctest: +ELLIPSIS + matrix(...) + +``lu_solve`` accepts overdetermined systems. It is usually not possible to solve +such systems, so the residual is minimized instead. Internally this is done +using Cholesky decomposition to compute a least squares approximation. This means +that that ``lu_solve`` will square the errors. If you can't afford this, use +``qr_solve`` instead. It is twice as slow but more accurate, and it calculates +the residual automatically. + + +Matrix factorization +.................... + +The function ``lu`` computes an explicit LU factorization of a matrix:: + + >>> P, L, U = lu(matrix([[0,2,3],[4,5,6],[7,8,9]])) + >>> print(P) + [0.0 0.0 1.0] + [1.0 0.0 0.0] + [0.0 1.0 0.0] + >>> print(L) + [ 1.0 0.0 0.0] + [ 0.0 1.0 0.0] + [0.571428571428571 0.214285714285714 1.0] + >>> print(U) + [7.0 8.0 9.0] + [0.0 2.0 3.0] + [0.0 0.0 0.214285714285714] + >>> print(P.T*L*U) + [0.0 2.0 3.0] + [4.0 5.0 6.0] + [7.0 8.0 9.0] + +Interval matrices +----------------- + +Matrices may contain interval elements. This allows one to perform +basic linear algebra operations such as matrix multiplication +and equation solving with rigorous error bounds:: + + >>> a = iv.matrix([['0.1','0.3','1.0'], + ... ['7.1','5.5','4.8'], + ... ['3.2','4.4','5.6']]) + >>> + >>> b = iv.matrix(['4','0.6','0.5']) + >>> c = iv.lu_solve(a, b) + >>> print(c) + [ [5.2582327113062568605927528666, 5.25823271130625686059275702219]] + [[-13.1550493962678375411635581388, -13.1550493962678375411635540152]] + [ [7.42069154774972557628979076189, 7.42069154774972557628979190734]] + >>> print(a*c) + [ [3.99999999999999999999999844904, 4.00000000000000000000000155096]] + [[0.599999999999999999999968898009, 0.600000000000000000000031763736]] + [[0.499999999999999999999979320485, 0.500000000000000000000020679515]] +""" + +# TODO: +# *implement high-level qr() +# *test unitvector +# *iterative solving + +from copy import copy + +from ..libmp.backend import xrange + +class LinearAlgebraMethods(object): + + def LU_decomp(ctx, A, overwrite=False, use_cache=True): + """ + LU-factorization of a n*n matrix using the Gauss algorithm. + Returns L and U in one matrix and the pivot indices. + + Use overwrite to specify whether A will be overwritten with L and U. + """ + if not A.rows == A.cols: + raise ValueError('need n*n matrix') + # get from cache if possible + if use_cache and isinstance(A, ctx.matrix) and A._LU: + return A._LU + if not overwrite: + orig = A + A = A.copy() + tol = ctx.absmin(ctx.mnorm(A,1) * ctx.eps) # each pivot element has to be bigger + n = A.rows + p = [None]*(n - 1) + for j in xrange(n - 1): + # pivoting, choose max(abs(reciprocal row sum)*abs(pivot element)) + biggest = 0 + for k in xrange(j, n): + s = ctx.fsum([ctx.absmin(A[k,l]) for l in xrange(j, n)]) + if ctx.absmin(s) <= tol: + raise ZeroDivisionError('matrix is numerically singular') + current = 1/s * ctx.absmin(A[k,j]) + if current > biggest: # TODO: what if equal? + biggest = current + p[j] = k + # swap rows according to p + ctx.swap_row(A, j, p[j]) + if ctx.absmin(A[j,j]) <= tol: + raise ZeroDivisionError('matrix is numerically singular') + # calculate elimination factors and add rows + for i in xrange(j + 1, n): + A[i,j] /= A[j,j] + for k in xrange(j + 1, n): + A[i,k] -= A[i,j]*A[j,k] + if ctx.absmin(A[n - 1,n - 1]) <= tol: + raise ZeroDivisionError('matrix is numerically singular') + # cache decomposition + if not overwrite and isinstance(orig, ctx.matrix): + orig._LU = (A, p) + return A, p + + def L_solve(ctx, L, b, p=None): + """ + Solve the lower part of a LU factorized matrix for y. + """ + if L.rows != L.cols: + raise RuntimeError("need n*n matrix") + n = L.rows + if len(b) != n: + raise ValueError("Value should be equal to n") + b = copy(b) + if p: # swap b according to p + for k in xrange(0, len(p)): + ctx.swap_row(b, k, p[k]) + # solve + for i in xrange(1, n): + for j in xrange(i): + b[i] -= L[i,j] * b[j] + return b + + def U_solve(ctx, U, y): + """ + Solve the upper part of a LU factorized matrix for x. + """ + if U.rows != U.cols: + raise RuntimeError("need n*n matrix") + n = U.rows + if len(y) != n: + raise ValueError("Value should be equal to n") + x = copy(y) + for i in xrange(n - 1, -1, -1): + for j in xrange(i + 1, n): + x[i] -= U[i,j] * x[j] + x[i] /= U[i,i] + return x + + def lu_solve(ctx, A, b, **kwargs): + """ + Ax = b => x + + Solve a determined or overdetermined linear equations system. + Fast LU decomposition is used, which is less accurate than QR decomposition + (especially for overdetermined systems), but it's twice as efficient. + Use qr_solve if you want more precision or have to solve a very ill- + conditioned system. + + If you specify real=True, it does not check for overdeterminded complex + systems. + """ + prec = ctx.prec + try: + ctx.prec += 10 + # do not overwrite A nor b + A, b = ctx.matrix(A, **kwargs).copy(), ctx.matrix(b, **kwargs).copy() + if A.rows < A.cols: + raise ValueError('cannot solve underdetermined system') + if A.rows > A.cols: + # use least-squares method if overdetermined + # (this increases errors) + AH = A.H + A = AH * A + b = AH * b + if (kwargs.get('real', False) or + not sum(type(i) is ctx.mpc for i in A)): + # TODO: necessary to check also b? + x = ctx.cholesky_solve(A, b) + else: + x = ctx.lu_solve(A, b) + else: + # LU factorization + A, p = ctx.LU_decomp(A) + b = ctx.L_solve(A, b, p) + x = ctx.U_solve(A, b) + finally: + ctx.prec = prec + return x + + def improve_solution(ctx, A, x, b, maxsteps=1): + """ + Improve a solution to a linear equation system iteratively. + + This re-uses the LU decomposition and is thus cheap. + Usually 3 up to 4 iterations are giving the maximal improvement. + """ + if A.rows != A.cols: + raise RuntimeError("need n*n matrix") # TODO: really? + for _ in xrange(maxsteps): + r = ctx.residual(A, x, b) + if ctx.norm(r, 2) < 10*ctx.eps: + break + # this uses cached LU decomposition and is thus cheap + dx = ctx.lu_solve(A, -r) + x += dx + return x + + def lu(ctx, A): + """ + A -> P, L, U + + LU factorisation of a square matrix A. L is the lower, U the upper part. + P is the permutation matrix indicating the row swaps. + + P*A = L*U + + If you need efficiency, use the low-level method LU_decomp instead, it's + much more memory efficient. + """ + # get factorization + A, p = ctx.LU_decomp(A) + n = A.rows + L = ctx.matrix(n) + U = ctx.matrix(n) + for i in xrange(n): + for j in xrange(n): + if i > j: + L[i,j] = A[i,j] + elif i == j: + L[i,j] = 1 + U[i,j] = A[i,j] + else: + U[i,j] = A[i,j] + # calculate permutation matrix + P = ctx.eye(n) + for k in xrange(len(p)): + ctx.swap_row(P, k, p[k]) + return P, L, U + + def unitvector(ctx, n, i): + """ + Return the i-th n-dimensional unit vector. + """ + assert 0 < i <= n, 'this unit vector does not exist' + return [ctx.zero]*(i-1) + [ctx.one] + [ctx.zero]*(n-i) + + def inverse(ctx, A, **kwargs): + """ + Calculate the inverse of a matrix. + + If you want to solve an equation system Ax = b, it's recommended to use + solve(A, b) instead, it's about 3 times more efficient. + """ + prec = ctx.prec + try: + ctx.prec += 10 + # do not overwrite A + A = ctx.matrix(A, **kwargs).copy() + n = A.rows + # get LU factorisation + A, p = ctx.LU_decomp(A) + cols = [] + # calculate unit vectors and solve corresponding system to get columns + for i in xrange(1, n + 1): + e = ctx.unitvector(n, i) + y = ctx.L_solve(A, e, p) + cols.append(ctx.U_solve(A, y)) + # convert columns to matrix + inv = [] + for i in xrange(n): + row = [] + for j in xrange(n): + row.append(cols[j][i]) + inv.append(row) + result = ctx.matrix(inv, **kwargs) + finally: + ctx.prec = prec + return result + + def householder(ctx, A): + """ + (A|b) -> H, p, x, res + + (A|b) is the coefficient matrix with left hand side of an optionally + overdetermined linear equation system. + H and p contain all information about the transformation matrices. + x is the solution, res the residual. + """ + if not isinstance(A, ctx.matrix): + raise TypeError("A should be a type of ctx.matrix") + m = A.rows + n = A.cols + if m < n - 1: + raise RuntimeError("Columns should not be less than rows") + # calculate Householder matrix + p = [] + for j in xrange(0, n - 1): + s = ctx.fsum(abs(A[i,j])**2 for i in xrange(j, m)) + if not abs(s) > ctx.eps: + raise ValueError('matrix is numerically singular') + p.append(-ctx.sign(ctx.re(A[j,j])) * ctx.sqrt(s)) + kappa = ctx.one / (s - p[j] * A[j,j]) + A[j,j] -= p[j] + for k in xrange(j+1, n): + y = ctx.fsum(ctx.conj(A[i,j]) * A[i,k] for i in xrange(j, m)) * kappa + for i in xrange(j, m): + A[i,k] -= A[i,j] * y + # solve Rx = c1 + x = [A[i,n - 1] for i in xrange(n - 1)] + for i in xrange(n - 2, -1, -1): + x[i] -= ctx.fsum(A[i,j] * x[j] for j in xrange(i + 1, n - 1)) + x[i] /= p[i] + # calculate residual + if not m == n - 1: + r = [A[m-1-i, n-1] for i in xrange(m - n + 1)] + else: + # determined system, residual should be 0 + r = [0]*m # maybe a bad idea, changing r[i] will change all elements + return A, p, x, r + + #def qr(ctx, A): + # """ + # A -> Q, R + # + # QR factorisation of a square matrix A using Householder decomposition. + # Q is orthogonal, this leads to very few numerical errors. + # + # A = Q*R + # """ + # H, p, x, res = householder(A) + # TODO: implement this + + def residual(ctx, A, x, b, **kwargs): + """ + Calculate the residual of a solution to a linear equation system. + + r = A*x - b for A*x = b + """ + oldprec = ctx.prec + try: + ctx.prec *= 2 + A, x, b = ctx.matrix(A, **kwargs), ctx.matrix(x, **kwargs), ctx.matrix(b, **kwargs) + return A*x - b + finally: + ctx.prec = oldprec + + def qr_solve(ctx, A, b, norm=None, **kwargs): + """ + Ax = b => x, ||Ax - b|| + + Solve a determined or overdetermined linear equations system and + calculate the norm of the residual (error). + QR decomposition using Householder factorization is applied, which gives very + accurate results even for ill-conditioned matrices. qr_solve is twice as + efficient. + """ + if norm is None: + norm = ctx.norm + prec = ctx.prec + try: + ctx.prec += 10 + # do not overwrite A nor b + A, b = ctx.matrix(A, **kwargs).copy(), ctx.matrix(b, **kwargs).copy() + if A.rows < A.cols: + raise ValueError('cannot solve underdetermined system') + H, p, x, r = ctx.householder(ctx.extend(A, b)) + res = ctx.norm(r) + # calculate residual "manually" for determined systems + if res == 0: + res = ctx.norm(ctx.residual(A, x, b)) + return ctx.matrix(x, **kwargs), res + finally: + ctx.prec = prec + + def cholesky(ctx, A, tol=None): + r""" + Cholesky decomposition of a symmetric positive-definite matrix `A`. + Returns a lower triangular matrix `L` such that `A = L \times L^T`. + More generally, for a complex Hermitian positive-definite matrix, + a Cholesky decomposition satisfying `A = L \times L^H` is returned. + + The Cholesky decomposition can be used to solve linear equation + systems twice as efficiently as LU decomposition, or to + test whether `A` is positive-definite. + + The optional parameter ``tol`` determines the tolerance for + verifying positive-definiteness. + + **Examples** + + Cholesky decomposition of a positive-definite symmetric matrix:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> A = eye(3) + hilbert(3) + >>> nprint(A) + [ 2.0 0.5 0.333333] + [ 0.5 1.33333 0.25] + [0.333333 0.25 1.2] + >>> L = cholesky(A) + >>> nprint(L) + [ 1.41421 0.0 0.0] + [0.353553 1.09924 0.0] + [0.235702 0.15162 1.05899] + >>> chop(A - L*L.T) + [0.0 0.0 0.0] + [0.0 0.0 0.0] + [0.0 0.0 0.0] + + Cholesky decomposition of a Hermitian matrix:: + + >>> A = eye(3) + matrix([[0,0.25j,-0.5j],[-0.25j,0,0],[0.5j,0,0]]) + >>> L = cholesky(A) + >>> nprint(L) + [ 1.0 0.0 0.0] + [(0.0 - 0.25j) (0.968246 + 0.0j) 0.0] + [ (0.0 + 0.5j) (0.129099 + 0.0j) (0.856349 + 0.0j)] + >>> chop(A - L*L.H) + [0.0 0.0 0.0] + [0.0 0.0 0.0] + [0.0 0.0 0.0] + + Attempted Cholesky decomposition of a matrix that is not positive + definite:: + + >>> A = -eye(3) + hilbert(3) + >>> L = cholesky(A) + Traceback (most recent call last): + ... + ValueError: matrix is not positive-definite + + **References** + + 1. [Wikipedia]_ http://en.wikipedia.org/wiki/Cholesky_decomposition + + """ + if not isinstance(A, ctx.matrix): + raise RuntimeError("A should be a type of ctx.matrix") + if not A.rows == A.cols: + raise ValueError('need n*n matrix') + if tol is None: + tol = +ctx.eps + n = A.rows + L = ctx.matrix(n) + for j in xrange(n): + c = ctx.re(A[j,j]) + if abs(c-A[j,j]) > tol: + raise ValueError('matrix is not Hermitian') + s = c - ctx.fsum((L[j,k] for k in xrange(j)), + absolute=True, squared=True) + if s < tol: + raise ValueError('matrix is not positive-definite') + L[j,j] = ctx.sqrt(s) + for i in xrange(j, n): + it1 = (L[i,k] for k in xrange(j)) + it2 = (L[j,k] for k in xrange(j)) + t = ctx.fdot(it1, it2, conjugate=True) + L[i,j] = (A[i,j] - t) / L[j,j] + return L + + def cholesky_solve(ctx, A, b, **kwargs): + """ + Ax = b => x + + Solve a symmetric positive-definite linear equation system. + This is twice as efficient as lu_solve. + + Typical use cases: + * A.T*A + * Hessian matrix + * differential equations + """ + prec = ctx.prec + try: + ctx.prec += 10 + # do not overwrite A nor b + A, b = ctx.matrix(A, **kwargs).copy(), ctx.matrix(b, **kwargs).copy() + if A.rows != A.cols: + raise ValueError('can only solve determined system') + # Cholesky factorization + L = ctx.cholesky(A) + # solve + n = L.rows + if len(b) != n: + raise ValueError("Value should be equal to n") + for i in xrange(n): + b[i] -= ctx.fsum(L[i,j] * b[j] for j in xrange(i)) + b[i] /= L[i,i] + x = ctx.U_solve(L.T, b) + return x + finally: + ctx.prec = prec + + def det(ctx, A): + """ + Calculate the determinant of a matrix. + """ + prec = ctx.prec + try: + # do not overwrite A + A = ctx.matrix(A).copy() + # use LU factorization to calculate determinant + try: + R, p = ctx.LU_decomp(A) + except ZeroDivisionError: + return 0 + z = 1 + for i, e in enumerate(p): + if i != e: + z *= -1 + for i in xrange(A.rows): + z *= R[i,i] + return z + finally: + ctx.prec = prec + + def cond(ctx, A, norm=None): + """ + Calculate the condition number of a matrix using a specified matrix norm. + + The condition number estimates the sensitivity of a matrix to errors. + Example: small input errors for ill-conditioned coefficient matrices + alter the solution of the system dramatically. + + For ill-conditioned matrices it's recommended to use qr_solve() instead + of lu_solve(). This does not help with input errors however, it just avoids + to add additional errors. + + Definition: cond(A) = ||A|| * ||A**-1|| + """ + if norm is None: + norm = lambda x: ctx.mnorm(x,1) + return norm(A) * norm(ctx.inverse(A)) + + def lu_solve_mat(ctx, a, b): + """Solve a * x = b where a and b are matrices.""" + r = ctx.matrix(a.rows, b.cols) + for i in range(b.cols): + c = ctx.lu_solve(a, b.column(i)) + for j in range(len(c)): + r[j, i] = c[j] + return r + + def qr(ctx, A, mode = 'full', edps = 10): + """ + Compute a QR factorization $A = QR$ where + A is an m x n matrix of real or complex numbers where m >= n + + mode has following meanings: + (1) mode = 'raw' returns two matrixes (A, tau) in the + internal format used by LAPACK + (2) mode = 'skinny' returns the leading n columns of Q + and n rows of R + (3) Any other value returns the leading m columns of Q + and m rows of R + + edps is the increase in mp precision used for calculations + + **Examples** + + >>> from mpmath import * + >>> mp.dps = 15 + >>> mp.pretty = True + >>> A = matrix([[1, 2], [3, 4], [1, 1]]) + >>> Q, R = qr(A) + >>> Q + [-0.301511344577764 0.861640436855329 0.408248290463863] + [-0.904534033733291 -0.123091490979333 -0.408248290463863] + [-0.301511344577764 -0.492365963917331 0.816496580927726] + >>> R + [-3.3166247903554 -4.52267016866645] + [ 0.0 0.738548945875996] + [ 0.0 0.0] + >>> Q * R + [1.0 2.0] + [3.0 4.0] + [1.0 1.0] + >>> chop(Q.T * Q) + [1.0 0.0 0.0] + [0.0 1.0 0.0] + [0.0 0.0 1.0] + >>> B = matrix([[1+0j, 2-3j], [3+j, 4+5j]]) + >>> Q, R = qr(B) + >>> nprint(Q) + [ (-0.301511 + 0.0j) (0.0695795 - 0.95092j)] + [(-0.904534 - 0.301511j) (-0.115966 + 0.278318j)] + >>> nprint(R) + [(-3.31662 + 0.0j) (-5.72872 - 2.41209j)] + [ 0.0 (3.91965 + 0.0j)] + >>> Q * R + [(1.0 + 0.0j) (2.0 - 3.0j)] + [(3.0 + 1.0j) (4.0 + 5.0j)] + >>> chop(Q.T * Q.conjugate()) + [1.0 0.0] + [0.0 1.0] + + """ + + # check values before continuing + assert isinstance(A, ctx.matrix) + m = A.rows + n = A.cols + assert n >= 0 + assert m >= n + assert edps >= 0 + + # check for complex data type + cmplx = any(type(x) is ctx.mpc for x in A) + + # temporarily increase the precision and initialize + with ctx.extradps(edps): + tau = ctx.matrix(n,1) + A = A.copy() + + # --------------- + # FACTOR MATRIX A + # --------------- + if cmplx: + one = ctx.mpc('1.0', '0.0') + zero = ctx.mpc('0.0', '0.0') + rzero = ctx.mpf('0.0') + + # main loop to factor A (complex) + for j in xrange(0, n): + alpha = A[j,j] + alphr = ctx.re(alpha) + alphi = ctx.im(alpha) + + if (m-j) >= 2: + xnorm = ctx.fsum( A[i,j]*ctx.conj(A[i,j]) for i in xrange(j+1, m) ) + xnorm = ctx.re( ctx.sqrt(xnorm) ) + else: + xnorm = rzero + + if (xnorm == rzero) and (alphi == rzero): + tau[j] = zero + continue + + if alphr < rzero: + beta = ctx.sqrt(alphr**2 + alphi**2 + xnorm**2) + else: + beta = -ctx.sqrt(alphr**2 + alphi**2 + xnorm**2) + + tau[j] = ctx.mpc( (beta - alphr) / beta, -alphi / beta ) + t = -ctx.conj(tau[j]) + za = one / (alpha - beta) + + for i in xrange(j+1, m): + A[i,j] *= za + + A[j,j] = one + for k in xrange(j+1, n): + y = ctx.fsum(A[i,j] * ctx.conj(A[i,k]) for i in xrange(j, m)) + temp = t * ctx.conj(y) + for i in xrange(j, m): + A[i,k] += A[i,j] * temp + + A[j,j] = ctx.mpc(beta, '0.0') + else: + one = ctx.mpf('1.0') + zero = ctx.mpf('0.0') + + # main loop to factor A (real) + for j in xrange(0, n): + alpha = A[j,j] + + if (m-j) > 2: + xnorm = ctx.fsum( (A[i,j])**2 for i in xrange(j+1, m) ) + xnorm = ctx.sqrt(xnorm) + elif (m-j) == 2: + xnorm = abs( A[m-1,j] ) + else: + xnorm = zero + + if xnorm == zero: + tau[j] = zero + continue + + if alpha < zero: + beta = ctx.sqrt(alpha**2 + xnorm**2) + else: + beta = -ctx.sqrt(alpha**2 + xnorm**2) + + tau[j] = (beta - alpha) / beta + t = -tau[j] + da = one / (alpha - beta) + + for i in xrange(j+1, m): + A[i,j] *= da + + A[j,j] = one + for k in xrange(j+1, n): + y = ctx.fsum( A[i,j] * A[i,k] for i in xrange(j, m) ) + temp = t * y + for i in xrange(j,m): + A[i,k] += A[i,j] * temp + + A[j,j] = beta + + # return factorization in same internal format as LAPACK + if (mode == 'raw') or (mode == 'RAW'): + return A, tau + + # ---------------------------------- + # FORM Q USING BACKWARD ACCUMULATION + # ---------------------------------- + + # form R before the values are overwritten + R = A.copy() + for j in xrange(0, n): + for i in xrange(j+1, m): + R[i,j] = zero + + # set the value of p (number of columns of Q to return) + p = m + if (mode == 'skinny') or (mode == 'SKINNY'): + p = n + + # add columns to A if needed and initialize + A.cols += (p-n) + for j in xrange(0, p): + A[j,j] = one + for i in xrange(0, j): + A[i,j] = zero + + # main loop to form Q + for j in xrange(n-1, -1, -1): + t = -tau[j] + A[j,j] += t + + for k in xrange(j+1, p): + if cmplx: + y = ctx.fsum(A[i,j] * ctx.conj(A[i,k]) for i in xrange(j+1, m)) + temp = t * ctx.conj(y) + else: + y = ctx.fsum(A[i,j] * A[i,k] for i in xrange(j+1, m)) + temp = t * y + A[j,k] = temp + for i in xrange(j+1, m): + A[i,k] += A[i,j] * temp + + for i in xrange(j+1, m): + A[i, j] *= t + + return A, R[0:p,0:n] + + # ------------------ + # END OF FUNCTION QR + # ------------------ diff --git a/lib/python3.12/site-packages/mpmath/matrices/matrices.py b/lib/python3.12/site-packages/mpmath/matrices/matrices.py new file mode 100644 index 0000000000000000000000000000000000000000..a97d5a9ca7e173195386dc7cb60860a826ab6a97 --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/matrices/matrices.py @@ -0,0 +1,1005 @@ +from ..libmp.backend import xrange +import warnings + +# TODO: interpret list as vectors (for multiplication) + +rowsep = '\n' +colsep = ' ' + +class _matrix(object): + """ + Numerical matrix. + + Specify the dimensions or the data as a nested list. + Elements default to zero. + Use a flat list to create a column vector easily. + + The datatype of the context (mpf for mp, mpi for iv, and float for fp) is used to store the data. + + Creating matrices + ----------------- + + Matrices in mpmath are implemented using dictionaries. Only non-zero values + are stored, so it is cheap to represent sparse matrices. + + The most basic way to create one is to use the ``matrix`` class directly. + You can create an empty matrix specifying the dimensions: + + >>> from mpmath import * + >>> mp.dps = 15 + >>> matrix(2) + matrix( + [['0.0', '0.0'], + ['0.0', '0.0']]) + >>> matrix(2, 3) + matrix( + [['0.0', '0.0', '0.0'], + ['0.0', '0.0', '0.0']]) + + Calling ``matrix`` with one dimension will create a square matrix. + + To access the dimensions of a matrix, use the ``rows`` or ``cols`` keyword: + + >>> A = matrix(3, 2) + >>> A + matrix( + [['0.0', '0.0'], + ['0.0', '0.0'], + ['0.0', '0.0']]) + >>> A.rows + 3 + >>> A.cols + 2 + + You can also change the dimension of an existing matrix. This will set the + new elements to 0. If the new dimension is smaller than before, the + concerning elements are discarded: + + >>> A.rows = 2 + >>> A + matrix( + [['0.0', '0.0'], + ['0.0', '0.0']]) + + Internally ``mpmathify`` is used every time an element is set. This + is done using the syntax A[row,column], counting from 0: + + >>> A = matrix(2) + >>> A[1,1] = 1 + 1j + >>> A + matrix( + [['0.0', '0.0'], + ['0.0', mpc(real='1.0', imag='1.0')]]) + + A more comfortable way to create a matrix lets you use nested lists: + + >>> matrix([[1, 2], [3, 4]]) + matrix( + [['1.0', '2.0'], + ['3.0', '4.0']]) + + Convenient advanced functions are available for creating various standard + matrices, see ``zeros``, ``ones``, ``diag``, ``eye``, ``randmatrix`` and + ``hilbert``. + + Vectors + ....... + + Vectors may also be represented by the ``matrix`` class (with rows = 1 or cols = 1). + For vectors there are some things which make life easier. A column vector can + be created using a flat list, a row vectors using an almost flat nested list:: + + >>> matrix([1, 2, 3]) + matrix( + [['1.0'], + ['2.0'], + ['3.0']]) + >>> matrix([[1, 2, 3]]) + matrix( + [['1.0', '2.0', '3.0']]) + + Optionally vectors can be accessed like lists, using only a single index:: + + >>> x = matrix([1, 2, 3]) + >>> x[1] + mpf('2.0') + >>> x[1,0] + mpf('2.0') + + Other + ..... + + Like you probably expected, matrices can be printed:: + + >>> print randmatrix(3) # doctest:+SKIP + [ 0.782963853573023 0.802057689719883 0.427895717335467] + [0.0541876859348597 0.708243266653103 0.615134039977379] + [ 0.856151514955773 0.544759264818486 0.686210904770947] + + Use ``nstr`` or ``nprint`` to specify the number of digits to print:: + + >>> nprint(randmatrix(5), 3) # doctest:+SKIP + [2.07e-1 1.66e-1 5.06e-1 1.89e-1 8.29e-1] + [6.62e-1 6.55e-1 4.47e-1 4.82e-1 2.06e-2] + [4.33e-1 7.75e-1 6.93e-2 2.86e-1 5.71e-1] + [1.01e-1 2.53e-1 6.13e-1 3.32e-1 2.59e-1] + [1.56e-1 7.27e-2 6.05e-1 6.67e-2 2.79e-1] + + As matrices are mutable, you will need to copy them sometimes:: + + >>> A = matrix(2) + >>> A + matrix( + [['0.0', '0.0'], + ['0.0', '0.0']]) + >>> B = A.copy() + >>> B[0,0] = 1 + >>> B + matrix( + [['1.0', '0.0'], + ['0.0', '0.0']]) + >>> A + matrix( + [['0.0', '0.0'], + ['0.0', '0.0']]) + + Finally, it is possible to convert a matrix to a nested list. This is very useful, + as most Python libraries involving matrices or arrays (namely NumPy or SymPy) + support this format:: + + >>> B.tolist() + [[mpf('1.0'), mpf('0.0')], [mpf('0.0'), mpf('0.0')]] + + + Matrix operations + ----------------- + + You can add and subtract matrices of compatible dimensions:: + + >>> A = matrix([[1, 2], [3, 4]]) + >>> B = matrix([[-2, 4], [5, 9]]) + >>> A + B + matrix( + [['-1.0', '6.0'], + ['8.0', '13.0']]) + >>> A - B + matrix( + [['3.0', '-2.0'], + ['-2.0', '-5.0']]) + >>> A + ones(3) # doctest:+ELLIPSIS + Traceback (most recent call last): + ... + ValueError: incompatible dimensions for addition + + It is possible to multiply or add matrices and scalars. In the latter case the + operation will be done element-wise:: + + >>> A * 2 + matrix( + [['2.0', '4.0'], + ['6.0', '8.0']]) + >>> A / 4 + matrix( + [['0.25', '0.5'], + ['0.75', '1.0']]) + >>> A - 1 + matrix( + [['0.0', '1.0'], + ['2.0', '3.0']]) + + Of course you can perform matrix multiplication, if the dimensions are + compatible, using ``@`` (for Python >= 3.5) or ``*``. For clarity, ``@`` is + recommended (`PEP 465 `), because + the meaning of ``*`` is different in many other Python libraries such as NumPy. + + >>> A @ B # doctest:+SKIP + matrix( + [['8.0', '22.0'], + ['14.0', '48.0']]) + >>> A * B # same as A @ B + matrix( + [['8.0', '22.0'], + ['14.0', '48.0']]) + >>> matrix([[1, 2, 3]]) * matrix([[-6], [7], [-2]]) + matrix( + [['2.0']]) + + .. + COMMENT: TODO: the above "doctest:+SKIP" may be removed as soon as we + have dropped support for Python 3.5 and below. + + You can raise powers of square matrices:: + + >>> A**2 + matrix( + [['7.0', '10.0'], + ['15.0', '22.0']]) + + Negative powers will calculate the inverse:: + + >>> A**-1 + matrix( + [['-2.0', '1.0'], + ['1.5', '-0.5']]) + >>> A * A**-1 + matrix( + [['1.0', '1.0842021724855e-19'], + ['-2.16840434497101e-19', '1.0']]) + + + + Matrix transposition is straightforward:: + + >>> A = ones(2, 3) + >>> A + matrix( + [['1.0', '1.0', '1.0'], + ['1.0', '1.0', '1.0']]) + >>> A.T + matrix( + [['1.0', '1.0'], + ['1.0', '1.0'], + ['1.0', '1.0']]) + + Norms + ..... + + Sometimes you need to know how "large" a matrix or vector is. Due to their + multidimensional nature it's not possible to compare them, but there are + several functions to map a matrix or a vector to a positive real number, the + so called norms. + + For vectors the p-norm is intended, usually the 1-, the 2- and the oo-norm are + used. + + >>> x = matrix([-10, 2, 100]) + >>> norm(x, 1) + mpf('112.0') + >>> norm(x, 2) + mpf('100.5186549850325') + >>> norm(x, inf) + mpf('100.0') + + Please note that the 2-norm is the most used one, though it is more expensive + to calculate than the 1- or oo-norm. + + It is possible to generalize some vector norms to matrix norm:: + + >>> A = matrix([[1, -1000], [100, 50]]) + >>> mnorm(A, 1) + mpf('1050.0') + >>> mnorm(A, inf) + mpf('1001.0') + >>> mnorm(A, 'F') + mpf('1006.2310867787777') + + The last norm (the "Frobenius-norm") is an approximation for the 2-norm, which + is hard to calculate and not available. The Frobenius-norm lacks some + mathematical properties you might expect from a norm. + """ + + def __init__(self, *args, **kwargs): + self.__data = {} + # LU decompostion cache, this is useful when solving the same system + # multiple times, when calculating the inverse and when calculating the + # determinant + self._LU = None + if "force_type" in kwargs: + warnings.warn("The force_type argument was removed, it did not work" + " properly anyway. If you want to force floating-point or" + " interval computations, use the respective methods from `fp`" + " or `mp` instead, e.g., `fp.matrix()` or `iv.matrix()`." + " If you want to truncate values to integer, use .apply(int) instead.") + if isinstance(args[0], (list, tuple)): + if isinstance(args[0][0], (list, tuple)): + # interpret nested list as matrix + A = args[0] + self.__rows = len(A) + self.__cols = len(A[0]) + for i, row in enumerate(A): + for j, a in enumerate(row): + # note: this will call __setitem__ which will call self.ctx.convert() to convert the datatype. + self[i, j] = a + else: + # interpret list as row vector + v = args[0] + self.__rows = len(v) + self.__cols = 1 + for i, e in enumerate(v): + self[i, 0] = e + elif isinstance(args[0], int): + # create empty matrix of given dimensions + if len(args) == 1: + self.__rows = self.__cols = args[0] + else: + if not isinstance(args[1], int): + raise TypeError("expected int") + self.__rows = args[0] + self.__cols = args[1] + elif isinstance(args[0], _matrix): + A = args[0] + self.__rows = A._matrix__rows + self.__cols = A._matrix__cols + for i in xrange(A.__rows): + for j in xrange(A.__cols): + self[i, j] = A[i, j] + elif hasattr(args[0], 'tolist'): + A = self.ctx.matrix(args[0].tolist()) + self.__data = A._matrix__data + self.__rows = A._matrix__rows + self.__cols = A._matrix__cols + else: + raise TypeError('could not interpret given arguments') + + def apply(self, f): + """ + Return a copy of self with the function `f` applied elementwise. + """ + new = self.ctx.matrix(self.__rows, self.__cols) + for i in xrange(self.__rows): + for j in xrange(self.__cols): + new[i,j] = f(self[i,j]) + return new + + def __nstr__(self, n=None, **kwargs): + # Build table of string representations of the elements + res = [] + # Track per-column max lengths for pretty alignment + maxlen = [0] * self.cols + for i in range(self.rows): + res.append([]) + for j in range(self.cols): + if n: + string = self.ctx.nstr(self[i,j], n, **kwargs) + else: + string = str(self[i,j]) + res[-1].append(string) + maxlen[j] = max(len(string), maxlen[j]) + # Patch strings together + for i, row in enumerate(res): + for j, elem in enumerate(row): + # Pad each element up to maxlen so the columns line up + row[j] = elem.rjust(maxlen[j]) + res[i] = "[" + colsep.join(row) + "]" + return rowsep.join(res) + + def __str__(self): + return self.__nstr__() + + def _toliststr(self, avoid_type=False): + """ + Create a list string from a matrix. + + If avoid_type: avoid multiple 'mpf's. + """ + # XXX: should be something like self.ctx._types + typ = self.ctx.mpf + s = '[' + for i in xrange(self.__rows): + s += '[' + for j in xrange(self.__cols): + if not avoid_type or not isinstance(self[i,j], typ): + a = repr(self[i,j]) + else: + a = "'" + str(self[i,j]) + "'" + s += a + ', ' + s = s[:-2] + s += '],\n ' + s = s[:-3] + s += ']' + return s + + def tolist(self): + """ + Convert the matrix to a nested list. + """ + return [[self[i,j] for j in range(self.__cols)] for i in range(self.__rows)] + + def __repr__(self): + if self.ctx.pretty: + return self.__str__() + s = 'matrix(\n' + s += self._toliststr(avoid_type=True) + ')' + return s + + def __get_element(self, key): + ''' + Fast extraction of the i,j element from the matrix + This function is for private use only because is unsafe: + 1. Does not check on the value of key it expects key to be a integer tuple (i,j) + 2. Does not check bounds + ''' + if key in self.__data: + return self.__data[key] + else: + return self.ctx.zero + + def __set_element(self, key, value): + ''' + Fast assignment of the i,j element in the matrix + This function is unsafe: + 1. Does not check on the value of key it expects key to be a integer tuple (i,j) + 2. Does not check bounds + 3. Does not check the value type + 4. Does not reset the LU cache + ''' + if value: # only store non-zeros + self.__data[key] = value + elif key in self.__data: + del self.__data[key] + + + def __getitem__(self, key): + ''' + Getitem function for mp matrix class with slice index enabled + it allows the following assingments + scalar to a slice of the matrix + B = A[:,2:6] + ''' + # Convert vector to matrix indexing + if isinstance(key, int) or isinstance(key,slice): + # only sufficent for vectors + if self.__rows == 1: + key = (0, key) + elif self.__cols == 1: + key = (key, 0) + else: + raise IndexError('insufficient indices for matrix') + + if isinstance(key[0],slice) or isinstance(key[1],slice): + + #Rows + if isinstance(key[0],slice): + #Check bounds + if (key[0].start is None or key[0].start >= 0) and \ + (key[0].stop is None or key[0].stop <= self.__rows+1): + # Generate indices + rows = xrange(*key[0].indices(self.__rows)) + else: + raise IndexError('Row index out of bounds') + else: + # Single row + rows = [key[0]] + + # Columns + if isinstance(key[1],slice): + # Check bounds + if (key[1].start is None or key[1].start >= 0) and \ + (key[1].stop is None or key[1].stop <= self.__cols+1): + # Generate indices + columns = xrange(*key[1].indices(self.__cols)) + else: + raise IndexError('Column index out of bounds') + + else: + # Single column + columns = [key[1]] + + # Create matrix slice + m = self.ctx.matrix(len(rows),len(columns)) + + # Assign elements to the output matrix + for i,x in enumerate(rows): + for j,y in enumerate(columns): + m.__set_element((i,j),self.__get_element((x,y))) + + return m + + else: + # single element extraction + if key[0] >= self.__rows or key[1] >= self.__cols: + raise IndexError('matrix index out of range') + if key in self.__data: + return self.__data[key] + else: + return self.ctx.zero + + def __setitem__(self, key, value): + # setitem function for mp matrix class with slice index enabled + # it allows the following assingments + # scalar to a slice of the matrix + # A[:,2:6] = 2.5 + # submatrix to matrix (the value matrix should be the same size as the slice size) + # A[3,:] = B where A is n x m and B is n x 1 + # Convert vector to matrix indexing + if isinstance(key, int) or isinstance(key,slice): + # only sufficent for vectors + if self.__rows == 1: + key = (0, key) + elif self.__cols == 1: + key = (key, 0) + else: + raise IndexError('insufficient indices for matrix') + # Slice indexing + if isinstance(key[0],slice) or isinstance(key[1],slice): + # Rows + if isinstance(key[0],slice): + # Check bounds + if (key[0].start is None or key[0].start >= 0) and \ + (key[0].stop is None or key[0].stop <= self.__rows+1): + # generate row indices + rows = xrange(*key[0].indices(self.__rows)) + else: + raise IndexError('Row index out of bounds') + else: + # Single row + rows = [key[0]] + # Columns + if isinstance(key[1],slice): + # Check bounds + if (key[1].start is None or key[1].start >= 0) and \ + (key[1].stop is None or key[1].stop <= self.__cols+1): + # Generate column indices + columns = xrange(*key[1].indices(self.__cols)) + else: + raise IndexError('Column index out of bounds') + else: + # Single column + columns = [key[1]] + # Assign slice with a scalar + if isinstance(value,self.ctx.matrix): + # Assign elements to matrix if input and output dimensions match + if len(rows) == value.rows and len(columns) == value.cols: + for i,x in enumerate(rows): + for j,y in enumerate(columns): + self.__set_element((x,y), value.__get_element((i,j))) + else: + raise ValueError('Dimensions do not match') + else: + # Assign slice with scalars + value = self.ctx.convert(value) + for i in rows: + for j in columns: + self.__set_element((i,j), value) + else: + # Single element assingment + # Check bounds + if key[0] >= self.__rows or key[1] >= self.__cols: + raise IndexError('matrix index out of range') + # Convert and store value + value = self.ctx.convert(value) + if value: # only store non-zeros + self.__data[key] = value + elif key in self.__data: + del self.__data[key] + + if self._LU: + self._LU = None + return + + def __iter__(self): + for i in xrange(self.__rows): + for j in xrange(self.__cols): + yield self[i,j] + + def __mul__(self, other): + if isinstance(other, self.ctx.matrix): + # dot multiplication + if self.__cols != other.__rows: + raise ValueError('dimensions not compatible for multiplication') + new = self.ctx.matrix(self.__rows, other.__cols) + self_zero = self.ctx.zero + self_get = self.__data.get + other_zero = other.ctx.zero + other_get = other.__data.get + for i in xrange(self.__rows): + for j in xrange(other.__cols): + new[i, j] = self.ctx.fdot((self_get((i,k), self_zero), other_get((k,j), other_zero)) + for k in xrange(other.__rows)) + return new + else: + # try scalar multiplication + new = self.ctx.matrix(self.__rows, self.__cols) + for i in xrange(self.__rows): + for j in xrange(self.__cols): + new[i, j] = other * self[i, j] + return new + + def __matmul__(self, other): + return self.__mul__(other) + + def __rmul__(self, other): + # assume other is scalar and thus commutative + if isinstance(other, self.ctx.matrix): + raise TypeError("other should not be type of ctx.matrix") + return self.__mul__(other) + + def __pow__(self, other): + # avoid cyclic import problems + #from linalg import inverse + if not isinstance(other, int): + raise ValueError('only integer exponents are supported') + if not self.__rows == self.__cols: + raise ValueError('only powers of square matrices are defined') + n = other + if n == 0: + return self.ctx.eye(self.__rows) + if n < 0: + n = -n + neg = True + else: + neg = False + i = n + y = 1 + z = self.copy() + while i != 0: + if i % 2 == 1: + y = y * z + z = z*z + i = i // 2 + if neg: + y = self.ctx.inverse(y) + return y + + def __div__(self, other): + # assume other is scalar and do element-wise divison + assert not isinstance(other, self.ctx.matrix) + new = self.ctx.matrix(self.__rows, self.__cols) + for i in xrange(self.__rows): + for j in xrange(self.__cols): + new[i,j] = self[i,j] / other + return new + + __truediv__ = __div__ + + def __add__(self, other): + if isinstance(other, self.ctx.matrix): + if not (self.__rows == other.__rows and self.__cols == other.__cols): + raise ValueError('incompatible dimensions for addition') + new = self.ctx.matrix(self.__rows, self.__cols) + for i in xrange(self.__rows): + for j in xrange(self.__cols): + new[i,j] = self[i,j] + other[i,j] + return new + else: + # assume other is scalar and add element-wise + new = self.ctx.matrix(self.__rows, self.__cols) + for i in xrange(self.__rows): + for j in xrange(self.__cols): + new[i,j] += self[i,j] + other + return new + + def __radd__(self, other): + return self.__add__(other) + + def __sub__(self, other): + if isinstance(other, self.ctx.matrix) and not (self.__rows == other.__rows + and self.__cols == other.__cols): + raise ValueError('incompatible dimensions for subtraction') + return self.__add__(other * (-1)) + + def __pos__(self): + """ + +M returns a copy of M, rounded to current working precision. + """ + return (+1) * self + + def __neg__(self): + return (-1) * self + + def __rsub__(self, other): + return -self + other + + def __eq__(self, other): + return self.__rows == other.__rows and self.__cols == other.__cols \ + and self.__data == other.__data + + def __len__(self): + if self.rows == 1: + return self.cols + elif self.cols == 1: + return self.rows + else: + return self.rows # do it like numpy + + def __getrows(self): + return self.__rows + + def __setrows(self, value): + for key in self.__data.copy(): + if key[0] >= value: + del self.__data[key] + self.__rows = value + + rows = property(__getrows, __setrows, doc='number of rows') + + def __getcols(self): + return self.__cols + + def __setcols(self, value): + for key in self.__data.copy(): + if key[1] >= value: + del self.__data[key] + self.__cols = value + + cols = property(__getcols, __setcols, doc='number of columns') + + def transpose(self): + new = self.ctx.matrix(self.__cols, self.__rows) + for i in xrange(self.__rows): + for j in xrange(self.__cols): + new[j,i] = self[i,j] + return new + + T = property(transpose) + + def conjugate(self): + return self.apply(self.ctx.conj) + + def transpose_conj(self): + return self.conjugate().transpose() + + H = property(transpose_conj) + + def copy(self): + new = self.ctx.matrix(self.__rows, self.__cols) + new.__data = self.__data.copy() + return new + + __copy__ = copy + + def column(self, n): + m = self.ctx.matrix(self.rows, 1) + for i in range(self.rows): + m[i] = self[i,n] + return m + +class MatrixMethods(object): + + def __init__(ctx): + # XXX: subclass + ctx.matrix = type('matrix', (_matrix,), {}) + ctx.matrix.ctx = ctx + ctx.matrix.convert = ctx.convert + + def eye(ctx, n, **kwargs): + """ + Create square identity matrix n x n. + """ + A = ctx.matrix(n, **kwargs) + for i in xrange(n): + A[i,i] = 1 + return A + + def diag(ctx, diagonal, **kwargs): + """ + Create square diagonal matrix using given list. + + Example: + >>> from mpmath import diag, mp + >>> mp.pretty = False + >>> diag([1, 2, 3]) + matrix( + [['1.0', '0.0', '0.0'], + ['0.0', '2.0', '0.0'], + ['0.0', '0.0', '3.0']]) + """ + A = ctx.matrix(len(diagonal), **kwargs) + for i in xrange(len(diagonal)): + A[i,i] = diagonal[i] + return A + + def zeros(ctx, *args, **kwargs): + """ + Create matrix m x n filled with zeros. + One given dimension will create square matrix n x n. + + Example: + >>> from mpmath import zeros, mp + >>> mp.pretty = False + >>> zeros(2) + matrix( + [['0.0', '0.0'], + ['0.0', '0.0']]) + """ + if len(args) == 1: + m = n = args[0] + elif len(args) == 2: + m = args[0] + n = args[1] + else: + raise TypeError('zeros expected at most 2 arguments, got %i' % len(args)) + A = ctx.matrix(m, n, **kwargs) + for i in xrange(m): + for j in xrange(n): + A[i,j] = 0 + return A + + def ones(ctx, *args, **kwargs): + """ + Create matrix m x n filled with ones. + One given dimension will create square matrix n x n. + + Example: + >>> from mpmath import ones, mp + >>> mp.pretty = False + >>> ones(2) + matrix( + [['1.0', '1.0'], + ['1.0', '1.0']]) + """ + if len(args) == 1: + m = n = args[0] + elif len(args) == 2: + m = args[0] + n = args[1] + else: + raise TypeError('ones expected at most 2 arguments, got %i' % len(args)) + A = ctx.matrix(m, n, **kwargs) + for i in xrange(m): + for j in xrange(n): + A[i,j] = 1 + return A + + def hilbert(ctx, m, n=None): + """ + Create (pseudo) hilbert matrix m x n. + One given dimension will create hilbert matrix n x n. + + The matrix is very ill-conditioned and symmetric, positive definite if + square. + """ + if n is None: + n = m + A = ctx.matrix(m, n) + for i in xrange(m): + for j in xrange(n): + A[i,j] = ctx.one / (i + j + 1) + return A + + def randmatrix(ctx, m, n=None, min=0, max=1, **kwargs): + """ + Create a random m x n matrix. + + All values are >= min and >> from mpmath import randmatrix + >>> randmatrix(2) # doctest:+SKIP + matrix( + [['0.53491598236191806', '0.57195669543302752'], + ['0.85589992269513615', '0.82444367501382143']]) + """ + if not n: + n = m + A = ctx.matrix(m, n, **kwargs) + for i in xrange(m): + for j in xrange(n): + A[i,j] = ctx.rand() * (max - min) + min + return A + + def swap_row(ctx, A, i, j): + """ + Swap row i with row j. + """ + if i == j: + return + if isinstance(A, ctx.matrix): + for k in xrange(A.cols): + A[i,k], A[j,k] = A[j,k], A[i,k] + elif isinstance(A, list): + A[i], A[j] = A[j], A[i] + else: + raise TypeError('could not interpret type') + + def extend(ctx, A, b): + """ + Extend matrix A with column b and return result. + """ + if not isinstance(A, ctx.matrix): + raise TypeError("A should be a type of ctx.matrix") + if A.rows != len(b): + raise ValueError("Value should be equal to len(b)") + A = A.copy() + A.cols += 1 + for i in xrange(A.rows): + A[i, A.cols-1] = b[i] + return A + + def norm(ctx, x, p=2): + r""" + Gives the entrywise `p`-norm of an iterable *x*, i.e. the vector norm + `\left(\sum_k |x_k|^p\right)^{1/p}`, for any given `1 \le p \le \infty`. + + Special cases: + + If *x* is not iterable, this just returns ``absmax(x)``. + + ``p=1`` gives the sum of absolute values. + + ``p=2`` is the standard Euclidean vector norm. + + ``p=inf`` gives the magnitude of the largest element. + + For *x* a matrix, ``p=2`` is the Frobenius norm. + For operator matrix norms, use :func:`~mpmath.mnorm` instead. + + You can use the string 'inf' as well as float('inf') or mpf('inf') + to specify the infinity norm. + + **Examples** + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = False + >>> x = matrix([-10, 2, 100]) + >>> norm(x, 1) + mpf('112.0') + >>> norm(x, 2) + mpf('100.5186549850325') + >>> norm(x, inf) + mpf('100.0') + + """ + try: + iter(x) + except TypeError: + return ctx.absmax(x) + if type(p) is not int: + p = ctx.convert(p) + if p == ctx.inf: + return max(ctx.absmax(i) for i in x) + elif p == 1: + return ctx.fsum(x, absolute=1) + elif p == 2: + return ctx.sqrt(ctx.fsum(x, absolute=1, squared=1)) + elif p > 1: + return ctx.nthroot(ctx.fsum(abs(i)**p for i in x), p) + else: + raise ValueError('p has to be >= 1') + + def mnorm(ctx, A, p=1): + r""" + Gives the matrix (operator) `p`-norm of A. Currently ``p=1`` and ``p=inf`` + are supported: + + ``p=1`` gives the 1-norm (maximal column sum) + + ``p=inf`` gives the `\infty`-norm (maximal row sum). + You can use the string 'inf' as well as float('inf') or mpf('inf') + + ``p=2`` (not implemented) for a square matrix is the usual spectral + matrix norm, i.e. the largest singular value. + + ``p='f'`` (or 'F', 'fro', 'Frobenius, 'frobenius') gives the + Frobenius norm, which is the elementwise 2-norm. The Frobenius norm is an + approximation of the spectral norm and satisfies + + .. math :: + + \frac{1}{\sqrt{\mathrm{rank}(A)}} \|A\|_F \le \|A\|_2 \le \|A\|_F + + The Frobenius norm lacks some mathematical properties that might + be expected of a norm. + + For general elementwise `p`-norms, use :func:`~mpmath.norm` instead. + + **Examples** + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = False + >>> A = matrix([[1, -1000], [100, 50]]) + >>> mnorm(A, 1) + mpf('1050.0') + >>> mnorm(A, inf) + mpf('1001.0') + >>> mnorm(A, 'F') + mpf('1006.2310867787777') + + """ + A = ctx.matrix(A) + if type(p) is not int: + if type(p) is str and 'frobenius'.startswith(p.lower()): + return ctx.norm(A, 2) + p = ctx.convert(p) + m, n = A.rows, A.cols + if p == 1: + return max(ctx.fsum((A[i,j] for i in xrange(m)), absolute=1) for j in xrange(n)) + elif p == ctx.inf: + return max(ctx.fsum((A[i,j] for j in xrange(n)), absolute=1) for i in xrange(m)) + else: + raise NotImplementedError("matrix p-norm for arbitrary p") + +if __name__ == '__main__': + import doctest + doctest.testmod() diff --git a/lib/python3.12/site-packages/mpmath/rational.py b/lib/python3.12/site-packages/mpmath/rational.py new file mode 100644 index 0000000000000000000000000000000000000000..58745205319ac3548ad5feb49371d2d154b2d3c8 --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/rational.py @@ -0,0 +1,240 @@ +import operator +import sys +from .libmp import int_types, mpf_hash, bitcount, from_man_exp, HASH_MODULUS + +new = object.__new__ + +def create_reduced(p, q, _cache={}): + key = p, q + if key in _cache: + return _cache[key] + x, y = p, q + while y: + x, y = y, x % y + if x != 1: + p //= x + q //= x + v = new(mpq) + v._mpq_ = p, q + # Speedup integers, half-integers and other small fractions + if q <= 4 and abs(key[0]) < 100: + _cache[key] = v + return v + +class mpq(object): + """ + Exact rational type, currently only intended for internal use. + """ + + __slots__ = ["_mpq_"] + + def __new__(cls, p, q=1): + if type(p) is tuple: + p, q = p + elif hasattr(p, '_mpq_'): + p, q = p._mpq_ + return create_reduced(p, q) + + def __repr__(s): + return "mpq(%s,%s)" % s._mpq_ + + def __str__(s): + return "(%s/%s)" % s._mpq_ + + def __int__(s): + a, b = s._mpq_ + return a // b + + def __nonzero__(s): + return bool(s._mpq_[0]) + + __bool__ = __nonzero__ + + def __hash__(s): + a, b = s._mpq_ + if sys.version_info >= (3, 2): + inverse = pow(b, HASH_MODULUS-2, HASH_MODULUS) + if not inverse: + h = sys.hash_info.inf + else: + h = (abs(a) * inverse) % HASH_MODULUS + if a < 0: h = -h + if h == -1: h = -2 + return h + else: + if b == 1: + return hash(a) + # Power of two: mpf compatible hash + if not (b & (b-1)): + return mpf_hash(from_man_exp(a, 1-bitcount(b))) + return hash((a,b)) + + def __eq__(s, t): + ttype = type(t) + if ttype is mpq: + return s._mpq_ == t._mpq_ + if ttype in int_types: + a, b = s._mpq_ + if b != 1: + return False + return a == t + return NotImplemented + + def __ne__(s, t): + ttype = type(t) + if ttype is mpq: + return s._mpq_ != t._mpq_ + if ttype in int_types: + a, b = s._mpq_ + if b != 1: + return True + return a != t + return NotImplemented + + def _cmp(s, t, op): + ttype = type(t) + if ttype in int_types: + a, b = s._mpq_ + return op(a, t*b) + if ttype is mpq: + a, b = s._mpq_ + c, d = t._mpq_ + return op(a*d, b*c) + return NotImplementedError + + def __lt__(s, t): return s._cmp(t, operator.lt) + def __le__(s, t): return s._cmp(t, operator.le) + def __gt__(s, t): return s._cmp(t, operator.gt) + def __ge__(s, t): return s._cmp(t, operator.ge) + + def __abs__(s): + a, b = s._mpq_ + if a >= 0: + return s + v = new(mpq) + v._mpq_ = -a, b + return v + + def __neg__(s): + a, b = s._mpq_ + v = new(mpq) + v._mpq_ = -a, b + return v + + def __pos__(s): + return s + + def __add__(s, t): + ttype = type(t) + if ttype is mpq: + a, b = s._mpq_ + c, d = t._mpq_ + return create_reduced(a*d+b*c, b*d) + if ttype in int_types: + a, b = s._mpq_ + v = new(mpq) + v._mpq_ = a+b*t, b + return v + return NotImplemented + + __radd__ = __add__ + + def __sub__(s, t): + ttype = type(t) + if ttype is mpq: + a, b = s._mpq_ + c, d = t._mpq_ + return create_reduced(a*d-b*c, b*d) + if ttype in int_types: + a, b = s._mpq_ + v = new(mpq) + v._mpq_ = a-b*t, b + return v + return NotImplemented + + def __rsub__(s, t): + ttype = type(t) + if ttype is mpq: + a, b = s._mpq_ + c, d = t._mpq_ + return create_reduced(b*c-a*d, b*d) + if ttype in int_types: + a, b = s._mpq_ + v = new(mpq) + v._mpq_ = b*t-a, b + return v + return NotImplemented + + def __mul__(s, t): + ttype = type(t) + if ttype is mpq: + a, b = s._mpq_ + c, d = t._mpq_ + return create_reduced(a*c, b*d) + if ttype in int_types: + a, b = s._mpq_ + return create_reduced(a*t, b) + return NotImplemented + + __rmul__ = __mul__ + + def __div__(s, t): + ttype = type(t) + if ttype is mpq: + a, b = s._mpq_ + c, d = t._mpq_ + return create_reduced(a*d, b*c) + if ttype in int_types: + a, b = s._mpq_ + return create_reduced(a, b*t) + return NotImplemented + + def __rdiv__(s, t): + ttype = type(t) + if ttype is mpq: + a, b = s._mpq_ + c, d = t._mpq_ + return create_reduced(b*c, a*d) + if ttype in int_types: + a, b = s._mpq_ + return create_reduced(b*t, a) + return NotImplemented + + def __pow__(s, t): + ttype = type(t) + if ttype in int_types: + a, b = s._mpq_ + if t: + if t < 0: + a, b, t = b, a, -t + v = new(mpq) + v._mpq_ = a**t, b**t + return v + raise ZeroDivisionError + return NotImplemented + + +mpq_1 = mpq((1,1)) +mpq_0 = mpq((0,1)) +mpq_1_2 = mpq((1,2)) +mpq_3_2 = mpq((3,2)) +mpq_1_4 = mpq((1,4)) +mpq_1_16 = mpq((1,16)) +mpq_3_16 = mpq((3,16)) +mpq_5_2 = mpq((5,2)) +mpq_3_4 = mpq((3,4)) +mpq_7_4 = mpq((7,4)) +mpq_5_4 = mpq((5,4)) + + +# Register with "numbers" ABC +# We do not subclass, hence we do not use the @abstractmethod checks. While +# this is less invasive it may turn out that we do not actually support +# parts of the expected interfaces. See +# http://docs.python.org/2/library/numbers.html for list of abstract +# methods. +try: + import numbers + numbers.Rational.register(mpq) +except ImportError: + pass diff --git a/lib/python3.12/site-packages/mpmath/tests/__init__.py b/lib/python3.12/site-packages/mpmath/tests/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/lib/python3.12/site-packages/mpmath/tests/__pycache__/__init__.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/tests/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..edacab5bde52b7f7ee378c057ada50e66072a9bc Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/tests/__pycache__/__init__.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/tests/__pycache__/extratest_gamma.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/tests/__pycache__/extratest_gamma.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c0a0abff63bdb57b84e6075773e124c9fb87b484 Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/tests/__pycache__/extratest_gamma.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/tests/__pycache__/extratest_zeta.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/tests/__pycache__/extratest_zeta.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8904f4a2c2bfad2c45d45275b724939c710a7aa9 Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/tests/__pycache__/extratest_zeta.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/tests/__pycache__/runtests.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/tests/__pycache__/runtests.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c0093a0a3c732248ebc693bf9c9c2f42a3d0dedd Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/tests/__pycache__/runtests.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_basic_ops.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_basic_ops.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4feceb16dfd05049be61540dad12ac37ee97f262 Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_basic_ops.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_bitwise.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_bitwise.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3ae61a302c188fd049d41081dadc01bf7edf6d27 Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_bitwise.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_calculus.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_calculus.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..10422c206f4de8773948f883601d941ff054dfdf Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_calculus.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_compatibility.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_compatibility.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e4d36016af5e06ca0d1f9bdc9f652c6266632e64 Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_compatibility.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_convert.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_convert.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..58045fdde4d40142d74bfc50e8e251caadfef6f3 Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_convert.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_diff.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_diff.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4e11edf9adf3d46d23e55d930c1939fa9aed0f00 Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_diff.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_division.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_division.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ca7d923ccfe4778c72d7491f75369f864ce459b5 Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_division.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_eigen.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_eigen.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..421ba44f9a3758b21c246e4999600dd23dfc6c17 Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_eigen.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_eigen_symmetric.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_eigen_symmetric.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..65038fc0c9d606b9b3afc920352232a92f47b246 Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_eigen_symmetric.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_elliptic.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_elliptic.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7dc33b21228173b2374c1ba559f274a44a368f88 Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_elliptic.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_functions.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_functions.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d192df76d651d69381976f083c9dfe1881961441 Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_functions.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_gammazeta.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_gammazeta.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8249ad7ca86fd44dba1e75e46021206399a5c665 Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_gammazeta.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_hp.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_hp.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d4582ada34a656abe670caaa974e24a1afe65c32 Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_hp.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_identify.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_identify.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c41f1299b0611c5e51bd0afdfd36fb4961d7aa04 Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_identify.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_interval.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_interval.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c1c0691752dca17450505b79f04de56f3d44b4eb Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_interval.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_levin.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_levin.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..772a98682161185d5fc3dbfb6b2a980454b76144 Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_levin.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_linalg.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_linalg.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..835d2d8da18563719ff2dda558263c71094e8da3 Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_linalg.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_matrices.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_matrices.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..403746c91f53bf0487af601ca713aa160da58bda Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_matrices.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_mpmath.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_mpmath.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fdfb00487951eb7c6cfff4a17b44c6987e9d1d84 Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_mpmath.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_ode.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_ode.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..94b47b6e79dcd0085ad1849652f54eab63b090f2 Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_ode.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_pickle.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_pickle.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..369e3fb4d9dedb19c360dbbf0aa702f56504e3d7 Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_pickle.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_power.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_power.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0117e45d6659e611e71b62b66810da548e7a59ed Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_power.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_quad.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_quad.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4956c848a4be1ab70d3326b2cf6596caf608170a Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_quad.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_rootfinding.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_rootfinding.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2fea83943e8a549addc0cfa41a8ee89a46bc8622 Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_rootfinding.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_special.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_special.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8c57b2813e12ed75d638a1d4cc86024dab06d34f Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_special.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_str.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_str.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8fe746cf508133e6f29ce97346c0c0338d92b467 Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_str.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_summation.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_summation.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..85c2e3bc74620f02863692f30369862d8d73ee6b Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_summation.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_trig.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_trig.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..463dadbd3f89e708f632cf563a9745b1f8addb3d Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_trig.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_visualization.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_visualization.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a1dcc7db9f975869607b7bfcfc2712ac637c02e5 Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_visualization.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/tests/__pycache__/torture.cpython-312.pyc b/lib/python3.12/site-packages/mpmath/tests/__pycache__/torture.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2c2cf1204514b3afb0d998c59ed7bb0ad07fd187 Binary files /dev/null and b/lib/python3.12/site-packages/mpmath/tests/__pycache__/torture.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/mpmath/tests/extratest_gamma.py b/lib/python3.12/site-packages/mpmath/tests/extratest_gamma.py new file mode 100644 index 0000000000000000000000000000000000000000..5a27b61b19aba0abf6bdb8adc16fc1ec7689b67a --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/tests/extratest_gamma.py @@ -0,0 +1,215 @@ +from mpmath import * +from mpmath.libmp import ifac + +import sys +if "-dps" in sys.argv: + maxdps = int(sys.argv[sys.argv.index("-dps")+1]) +else: + maxdps = 1000 + +raise_ = "-raise" in sys.argv + +errcount = 0 + +def check(name, func, z, y): + global errcount + try: + x = func(z) + except: + errcount += 1 + if raise_: + raise + print() + print(name) + print("EXCEPTION") + import traceback + traceback.print_tb(sys.exc_info()[2]) + print() + return + xre = x.real + xim = x.imag + yre = y.real + yim = y.imag + tol = eps*8 + err = 0 + if abs(xre-yre) > abs(yre)*tol: + err = 1 + print() + print("Error! %s (re = %s, wanted %s, err=%s)" % (name, nstr(xre,10), nstr(yre,10), nstr(abs(xre-yre)))) + errcount += 1 + if raise_: + raise SystemExit + if abs(xim-yim) > abs(yim)*tol: + err = 1 + print() + print("Error! %s (im = %s, wanted %s, err=%s)" % (name, nstr(xim,10), nstr(yim,10), nstr(abs(xim-yim)))) + errcount += 1 + if raise_: + raise SystemExit + if not err: + sys.stdout.write("%s ok; " % name) + +def testcase(case): + z, result = case + print("Testing z =", z) + mp.dps = 1010 + z = eval(z) + mp.dps = maxdps + 50 + if result is None: + gamma_val = gamma(z) + loggamma_val = loggamma(z) + factorial_val = factorial(z) + rgamma_val = rgamma(z) + else: + loggamma_val = eval(result) + gamma_val = exp(loggamma_val) + factorial_val = z * gamma_val + rgamma_val = 1/gamma_val + for dps in [5, 10, 15, 25, 40, 60, 90, 120, 250, 600, 1000, 1800, 3600]: + if dps > maxdps: + break + mp.dps = dps + print("dps = %s" % dps) + check("gamma", gamma, z, gamma_val) + check("rgamma", rgamma, z, rgamma_val) + check("loggamma", loggamma, z, loggamma_val) + check("factorial", factorial, z, factorial_val) + print() + mp.dps = 15 + +testcases = [] + +# Basic values +for n in list(range(1,200)) + list(range(201,2000,17)): + testcases.append(["%s" % n, None]) +for n in range(-200,200): + testcases.append(["%s+0.5" % n, None]) + testcases.append(["%s+0.37" % n, None]) + +testcases += [\ +["(0.1+1j)", None], +["(-0.1+1j)", None], +["(0.1-1j)", None], +["(-0.1-1j)", None], +["10j", None], +["-10j", None], +["100j", None], +["10000j", None], +["-10000000j", None], +["(10**100)*j", None], +["125+(10**100)*j", None], +["-125+(10**100)*j", None], +["(10**10)*(1+j)", None], +["(10**10)*(-1+j)", None], +["(10**100)*(1+j)", None], +["(10**100)*(-1+j)", None], +["(1.5-1j)", None], +["(6+4j)", None], +["(4+1j)", None], +["(3.5+2j)", None], +["(1.5-1j)", None], +["(-6-4j)", None], +["(-2-3j)", None], +["(-2.5-2j)", None], +["(4+1j)", None], +["(3+3j)", None], +["(2-2j)", None], +["1", "0"], +["2", "0"], +["3", "log(2)"], +["4", "log(6)"], +["5", "log(24)"], +["0.5", "log(pi)/2"], +["1.5", "log(sqrt(pi)/2)"], +["2.5", "log(3*sqrt(pi)/4)"], +["mpf('0.37')", None], +["0.25", "log(sqrt(2*sqrt(2*pi**3)/agm(1,sqrt(2))))"], +["-0.4", None], +["mpf('-1.9')", None], +["mpf('12.8')", None], +["mpf('33.7')", None], +["mpf('95.2')", None], +["mpf('160.3')", None], +["mpf('2057.8')", None], +["25", "log(ifac(24))"], +["80", "log(ifac(79))"], +["500", "log(ifac(500-1))"], +["8000", "log(ifac(8000-1))"], +["8000.5", None], +["mpf('8000.1')", None], +["mpf('1.37e10')", None], +["mpf('1.37e10')*(1+j)", None], +["mpf('1.37e10')*(-1+j)", None], +["mpf('1.37e10')*(-1-j)", None], +["mpf('1.37e10')*(-1+j)", None], +["mpf('1.37e100')", None], +["mpf('1.37e100')*(1+j)", None], +["mpf('1.37e100')*(-1+j)", None], +["mpf('1.37e100')*(-1-j)", None], +["mpf('1.37e100')*(-1+j)", None], +["3+4j", +"mpc('" +"-1.7566267846037841105306041816232757851567066070613445016197619371316057169" +"4723618263960834804618463052988607348289672535780644470689771115236512106002" +"5970873471563240537307638968509556191696167970488390423963867031934333890838" +"8009531786948197210025029725361069435208930363494971027388382086721660805397" +"9163230643216054580167976201709951509519218635460317367338612500626714783631" +"7498317478048447525674016344322545858832610325861086336204591943822302971823" +"5161814175530618223688296232894588415495615809337292518431903058265147109853" +"1710568942184987827643886816200452860853873815413367529829631430146227470517" +"6579967222200868632179482214312673161276976117132204633283806161971389519137" +"1243359764435612951384238091232760634271570950240717650166551484551654327989" +"9360285030081716934130446150245110557038117075172576825490035434069388648124" +"6678152254554001586736120762641422590778766100376515737713938521275749049949" +"1284143906816424244705094759339932733567910991920631339597278805393743140853" +"391550313363278558195609260225928','" +"4.74266443803465792819488940755002274088830335171164611359052405215840070271" +"5906813009373171139767051863542508136875688550817670379002790304870822775498" +"2809996675877564504192565392367259119610438951593128982646945990372179860613" +"4294436498090428077839141927485901735557543641049637962003652638924845391650" +"9546290137755550107224907606529385248390667634297183361902055842228798984200" +"9591180450211798341715874477629099687609819466457990642030707080894518168924" +"6805549314043258530272479246115112769957368212585759640878745385160943755234" +"9398036774908108204370323896757543121853650025529763655312360354244898913463" +"7115955702828838923393113618205074162812089732064414530813087483533203244056" +"0546577484241423134079056537777170351934430586103623577814746004431994179990" +"5318522939077992613855205801498201930221975721246498720895122345420698451980" +"0051215797310305885845964334761831751370672996984756815410977750799748813563" +"8784405288158432214886648743541773208808731479748217023665577802702269468013" +"673719173759245720489020315779001')"], +] + +for z in [4, 14, 34, 64]: + testcases.append(["(2+j)*%s/3" % z, None]) + testcases.append(["(-2+j)*%s/3" % z, None]) + testcases.append(["(1+2*j)*%s/3" % z, None]) + testcases.append(["(2-j)*%s/3" % z, None]) + testcases.append(["(20+j)*%s/3" % z, None]) + testcases.append(["(-20+j)*%s/3" % z, None]) + testcases.append(["(1+20*j)*%s/3" % z, None]) + testcases.append(["(20-j)*%s/3" % z, None]) + testcases.append(["(200+j)*%s/3" % z, None]) + testcases.append(["(-200+j)*%s/3" % z, None]) + testcases.append(["(1+200*j)*%s/3" % z, None]) + testcases.append(["(200-j)*%s/3" % z, None]) + +# Poles +for n in [0,1,2,3,4,25,-1,-2,-3,-4,-20,-21,-50,-51,-200,-201,-20000,-20001]: + for t in ['1e-5', '1e-20', '1e-100', '1e-10000']: + testcases.append(["fadd(%s,'%s',exact=True)" % (n, t), None]) + testcases.append(["fsub(%s,'%s',exact=True)" % (n, t), None]) + testcases.append(["fadd(%s,'%sj',exact=True)" % (n, t), None]) + testcases.append(["fsub(%s,'%sj',exact=True)" % (n, t), None]) + +if __name__ == "__main__": + from timeit import default_timer as clock + tot_time = 0.0 + for case in testcases: + t1 = clock() + testcase(case) + t2 = clock() + print("Test time:", t2-t1) + print() + tot_time += (t2-t1) + print("Total time:", tot_time) + print("Errors:", errcount) diff --git a/lib/python3.12/site-packages/mpmath/tests/extratest_zeta.py b/lib/python3.12/site-packages/mpmath/tests/extratest_zeta.py new file mode 100644 index 0000000000000000000000000000000000000000..582b3d9cbd956b9cdf94309e0e718371fe716101 --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/tests/extratest_zeta.py @@ -0,0 +1,30 @@ +from mpmath import zetazero +from timeit import default_timer as clock + +def test_zetazero(): + cases = [\ + (399999999, 156762524.6750591511), + (241389216, 97490234.2276711795), + (526196239, 202950727.691229534), + (542964976, 209039046.578535272), + (1048449112, 388858885.231056486), + (1048449113, 388858885.384337406), + (1048449114, 388858886.002285122), + (1048449115, 388858886.00239369), + (1048449116, 388858886.690745053) + ] + for n, v in cases: + print(n, v) + t1 = clock() + ok = zetazero(n).ae(complex(0.5,v)) + t2 = clock() + print("ok =", ok, ("(time = %s)" % round(t2-t1,3))) + print("Now computing two huge zeros (this may take hours)") + print("Computing zetazero(8637740722917)") + ok = zetazero(8637740722917).ae(complex(0.5,2124447368584.39296466152)) + print("ok =", ok) + ok = zetazero(8637740722918).ae(complex(0.5,2124447368584.39298170604)) + print("ok =", ok) + +if __name__ == "__main__": + test_zetazero() diff --git a/lib/python3.12/site-packages/mpmath/tests/runtests.py b/lib/python3.12/site-packages/mpmath/tests/runtests.py new file mode 100644 index 0000000000000000000000000000000000000000..70fde272fdc0e05e3d8951edddca380bd36139ab --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/tests/runtests.py @@ -0,0 +1,161 @@ +#!/usr/bin/env python + +""" +python runtests.py -py + Use py.test to run tests (more useful for debugging) + +python runtests.py -coverage + Generate test coverage report. Statistics are written to /tmp + +python runtests.py -profile + Generate profile stats (this is much slower) + +python runtests.py -nogmpy + Run tests without using GMPY even if it exists + +python runtests.py -strict + Enforce extra tests in normalize() + +python runtests.py -local + Insert '../..' at the beginning of sys.path to use local mpmath + +python runtests.py -skip ... + Skip tests from the listed modules + +Additional arguments are used to filter the tests to run. Only files that have +one of the arguments in their name are executed. + +""" + +import sys, os, traceback + +profile = False +if "-profile" in sys.argv: + sys.argv.remove('-profile') + profile = True + +coverage = False +if "-coverage" in sys.argv: + sys.argv.remove('-coverage') + coverage = True + +if "-nogmpy" in sys.argv: + sys.argv.remove('-nogmpy') + os.environ['MPMATH_NOGMPY'] = 'Y' + +if "-strict" in sys.argv: + sys.argv.remove('-strict') + os.environ['MPMATH_STRICT'] = 'Y' + +if "-local" in sys.argv: + sys.argv.remove('-local') + importdir = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]), + '../..')) +else: + importdir = '' + +# TODO: add a flag for this +testdir = '' + +def testit(importdir='', testdir='', exit_on_fail=False): + """Run all tests in testdir while importing from importdir.""" + if importdir: + sys.path.insert(1, importdir) + if testdir: + sys.path.insert(1, testdir) + import os.path + import mpmath + print("mpmath imported from %s" % os.path.dirname(mpmath.__file__)) + print("mpmath backend: %s" % mpmath.libmp.backend.BACKEND) + print("mpmath mp class: %s" % repr(mpmath.mp)) + print("mpmath version: %s" % mpmath.__version__) + print("Python version: %s" % sys.version) + print("") + if "-py" in sys.argv: + sys.argv.remove('-py') + import py + py.test.cmdline.main() + else: + import glob + from timeit import default_timer as clock + modules = [] + args = sys.argv[1:] + excluded = [] + if '-skip' in args: + excluded = args[args.index('-skip')+1:] + args = args[:args.index('-skip')] + # search for tests in directory of this file if not otherwise specified + if not testdir: + pattern = os.path.dirname(sys.argv[0]) + else: + pattern = testdir + if pattern: + pattern += '/' + pattern += 'test*.py' + # look for tests (respecting specified filter) + for f in glob.glob(pattern): + name = os.path.splitext(os.path.basename(f))[0] + # If run as a script, only run tests given as args, if any are given + if args and __name__ == "__main__": + ok = False + for arg in args: + if arg in name: + ok = True + break + if not ok: + continue + elif name in excluded: + continue + module = __import__(name) + priority = module.__dict__.get('priority', 100) + if priority == 666: + modules = [[priority, name, module]] + break + modules.append([priority, name, module]) + # execute tests + modules.sort() + tstart = clock() + for priority, name, module in modules: + print(name) + for f in sorted(module.__dict__.keys()): + if f.startswith('test_'): + if coverage and ('numpy' in f): + continue + sys.stdout.write(" " + f[5:].ljust(25) + " ") + t1 = clock() + try: + module.__dict__[f]() + except: + etype, evalue, trb = sys.exc_info() + if etype in (KeyboardInterrupt, SystemExit): + raise + print("") + print("TEST FAILED!") + print("") + traceback.print_exc() + if exit_on_fail: + return + t2 = clock() + print("ok " + " " + ("%.7f" % (t2-t1)) + " s") + tend = clock() + print("") + print("finished tests in " + ("%.2f" % (tend-tstart)) + " seconds") + # clean sys.path + if importdir: + sys.path.remove(importdir) + if testdir: + sys.path.remove(testdir) + +if __name__ == '__main__': + if profile: + import cProfile + cProfile.run("testit('%s', '%s')" % (importdir, testdir), sort=1) + elif coverage: + import trace + tracer = trace.Trace(ignoredirs=[sys.prefix, sys.exec_prefix], + trace=0, count=1) + tracer.run('testit(importdir, testdir)') + r = tracer.results() + r.write_results(show_missing=True, summary=True, coverdir="/tmp") + else: + testit(importdir, testdir) diff --git a/lib/python3.12/site-packages/mpmath/tests/test_basic_ops.py b/lib/python3.12/site-packages/mpmath/tests/test_basic_ops.py new file mode 100644 index 0000000000000000000000000000000000000000..f577c7fa9f9734876b6767f6cc21144df305d82f --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/tests/test_basic_ops.py @@ -0,0 +1,451 @@ +import mpmath +from mpmath import * +from mpmath.libmp import * +import random +import sys + +try: + long = long +except NameError: + long = int + +def test_type_compare(): + assert mpf(2) == mpc(2,0) + assert mpf(0) == mpc(0) + assert mpf(2) != mpc(2, 0.00001) + assert mpf(2) == 2.0 + assert mpf(2) != 3.0 + assert mpf(2) == 2 + assert mpf(2) != '2.0' + assert mpc(2) != '2.0' + +def test_add(): + assert mpf(2.5) + mpf(3) == 5.5 + assert mpf(2.5) + 3 == 5.5 + assert mpf(2.5) + 3.0 == 5.5 + assert 3 + mpf(2.5) == 5.5 + assert 3.0 + mpf(2.5) == 5.5 + assert (3+0j) + mpf(2.5) == 5.5 + assert mpc(2.5) + mpf(3) == 5.5 + assert mpc(2.5) + 3 == 5.5 + assert mpc(2.5) + 3.0 == 5.5 + assert mpc(2.5) + (3+0j) == 5.5 + assert 3 + mpc(2.5) == 5.5 + assert 3.0 + mpc(2.5) == 5.5 + assert (3+0j) + mpc(2.5) == 5.5 + +def test_sub(): + assert mpf(2.5) - mpf(3) == -0.5 + assert mpf(2.5) - 3 == -0.5 + assert mpf(2.5) - 3.0 == -0.5 + assert 3 - mpf(2.5) == 0.5 + assert 3.0 - mpf(2.5) == 0.5 + assert (3+0j) - mpf(2.5) == 0.5 + assert mpc(2.5) - mpf(3) == -0.5 + assert mpc(2.5) - 3 == -0.5 + assert mpc(2.5) - 3.0 == -0.5 + assert mpc(2.5) - (3+0j) == -0.5 + assert 3 - mpc(2.5) == 0.5 + assert 3.0 - mpc(2.5) == 0.5 + assert (3+0j) - mpc(2.5) == 0.5 + +def test_mul(): + assert mpf(2.5) * mpf(3) == 7.5 + assert mpf(2.5) * 3 == 7.5 + assert mpf(2.5) * 3.0 == 7.5 + assert 3 * mpf(2.5) == 7.5 + assert 3.0 * mpf(2.5) == 7.5 + assert (3+0j) * mpf(2.5) == 7.5 + assert mpc(2.5) * mpf(3) == 7.5 + assert mpc(2.5) * 3 == 7.5 + assert mpc(2.5) * 3.0 == 7.5 + assert mpc(2.5) * (3+0j) == 7.5 + assert 3 * mpc(2.5) == 7.5 + assert 3.0 * mpc(2.5) == 7.5 + assert (3+0j) * mpc(2.5) == 7.5 + +def test_div(): + assert mpf(6) / mpf(3) == 2.0 + assert mpf(6) / 3 == 2.0 + assert mpf(6) / 3.0 == 2.0 + assert 6 / mpf(3) == 2.0 + assert 6.0 / mpf(3) == 2.0 + assert (6+0j) / mpf(3.0) == 2.0 + assert mpc(6) / mpf(3) == 2.0 + assert mpc(6) / 3 == 2.0 + assert mpc(6) / 3.0 == 2.0 + assert mpc(6) / (3+0j) == 2.0 + assert 6 / mpc(3) == 2.0 + assert 6.0 / mpc(3) == 2.0 + assert (6+0j) / mpc(3) == 2.0 + +def test_pow(): + assert mpf(6) ** mpf(3) == 216.0 + assert mpf(6) ** 3 == 216.0 + assert mpf(6) ** 3.0 == 216.0 + assert 6 ** mpf(3) == 216.0 + assert 6.0 ** mpf(3) == 216.0 + assert (6+0j) ** mpf(3.0) == 216.0 + assert mpc(6) ** mpf(3) == 216.0 + assert mpc(6) ** 3 == 216.0 + assert mpc(6) ** 3.0 == 216.0 + assert mpc(6) ** (3+0j) == 216.0 + assert 6 ** mpc(3) == 216.0 + assert 6.0 ** mpc(3) == 216.0 + assert (6+0j) ** mpc(3) == 216.0 + +def test_mixed_misc(): + assert 1 + mpf(3) == mpf(3) + 1 == 4 + assert 1 - mpf(3) == -(mpf(3) - 1) == -2 + assert 3 * mpf(2) == mpf(2) * 3 == 6 + assert 6 / mpf(2) == mpf(6) / 2 == 3 + assert 1.0 + mpf(3) == mpf(3) + 1.0 == 4 + assert 1.0 - mpf(3) == -(mpf(3) - 1.0) == -2 + assert 3.0 * mpf(2) == mpf(2) * 3.0 == 6 + assert 6.0 / mpf(2) == mpf(6) / 2.0 == 3 + +def test_add_misc(): + mp.dps = 15 + assert mpf(4) + mpf(-70) == -66 + assert mpf(1) + mpf(1.1)/80 == 1 + 1.1/80 + assert mpf((1, 10000000000)) + mpf(3) == mpf((1, 10000000000)) + assert mpf(3) + mpf((1, 10000000000)) == mpf((1, 10000000000)) + assert mpf((1, -10000000000)) + mpf(3) == mpf(3) + assert mpf(3) + mpf((1, -10000000000)) == mpf(3) + assert mpf(1) + 1e-15 != 1 + assert mpf(1) + 1e-20 == 1 + assert mpf(1.07e-22) + 0 == mpf(1.07e-22) + assert mpf(0) + mpf(1.07e-22) == mpf(1.07e-22) + +def test_complex_misc(): + # many more tests needed + assert 1 + mpc(2) == 3 + assert not mpc(2).ae(2 + 1e-13) + assert mpc(2+1e-15j).ae(2) + +def test_complex_zeros(): + for a in [0,2]: + for b in [0,3]: + for c in [0,4]: + for d in [0,5]: + assert mpc(a,b)*mpc(c,d) == complex(a,b)*complex(c,d) + +def test_hash(): + for i in range(-256, 256): + assert hash(mpf(i)) == hash(i) + assert hash(mpf(0.5)) == hash(0.5) + assert hash(mpc(2,3)) == hash(2+3j) + # Check that this doesn't fail + assert hash(inf) + # Check that overflow doesn't assign equal hashes to large numbers + assert hash(mpf('1e1000')) != hash('1e10000') + assert hash(mpc(100,'1e1000')) != hash(mpc(200,'1e1000')) + from mpmath.rational import mpq + assert hash(mp.mpq(1,3)) + assert hash(mp.mpq(0,1)) == 0 + assert hash(mp.mpq(-1,1)) == hash(-1) + assert hash(mp.mpq(1,1)) == hash(1) + assert hash(mp.mpq(5,1)) == hash(5) + assert hash(mp.mpq(1,2)) == hash(0.5) + if sys.version_info >= (3, 2): + assert hash(mpf(1)*2**2000) == hash(2**2000) + assert hash(mpf(1)/2**2000) == hash(mpq(1,2**2000)) + +# Advanced rounding test +def test_add_rounding(): + mp.dps = 15 + a = from_float(1e-50) + assert mpf_sub(mpf_add(fone, a, 53, round_up), fone, 53, round_up) == from_float(2.2204460492503131e-16) + assert mpf_sub(fone, a, 53, round_up) == fone + assert mpf_sub(fone, mpf_sub(fone, a, 53, round_down), 53, round_down) == from_float(1.1102230246251565e-16) + assert mpf_add(fone, a, 53, round_down) == fone + +def test_almost_equal(): + assert mpf(1.2).ae(mpf(1.20000001), 1e-7) + assert not mpf(1.2).ae(mpf(1.20000001), 1e-9) + assert not mpf(-0.7818314824680298).ae(mpf(-0.774695868667929)) + +def test_arithmetic_functions(): + import operator + ops = [(operator.add, fadd), (operator.sub, fsub), (operator.mul, fmul), + (operator.truediv, fdiv)] + a = mpf(0.27) + b = mpf(1.13) + c = mpc(0.51+2.16j) + d = mpc(1.08-0.99j) + for x in [a,b,c,d]: + for y in [a,b,c,d]: + for op, fop in ops: + if fop is not fdiv: + mp.prec = 200 + z0 = op(x,y) + mp.prec = 60 + z1 = op(x,y) + mp.prec = 53 + z2 = op(x,y) + assert fop(x, y, prec=60) == z1 + assert fop(x, y) == z2 + if fop is not fdiv: + assert fop(x, y, prec=inf) == z0 + assert fop(x, y, dps=inf) == z0 + assert fop(x, y, exact=True) == z0 + assert fneg(fneg(z1, exact=True), prec=inf) == z1 + assert fneg(z1) == -(+z1) + mp.dps = 15 + +def test_exact_integer_arithmetic(): + # XXX: re-fix this so that all operations are tested with all rounding modes + random.seed(0) + for prec in [6, 10, 25, 40, 100, 250, 725]: + for rounding in ['d', 'u', 'f', 'c', 'n']: + mp.dps = prec + M = 10**(prec-2) + M2 = 10**(prec//2-2) + for i in range(10): + a = random.randint(-M, M) + b = random.randint(-M, M) + assert mpf(a, rounding=rounding) == a + assert int(mpf(a, rounding=rounding)) == a + assert int(mpf(str(a), rounding=rounding)) == a + assert mpf(a) + mpf(b) == a + b + assert mpf(a) - mpf(b) == a - b + assert -mpf(a) == -a + a = random.randint(-M2, M2) + b = random.randint(-M2, M2) + assert mpf(a) * mpf(b) == a*b + assert mpf_mul(from_int(a), from_int(b), mp.prec, rounding) == from_int(a*b) + mp.dps = 15 + +def test_odd_int_bug(): + assert to_int(from_int(3), round_nearest) == 3 + +def test_str_1000_digits(): + mp.dps = 1001 + # last digit may be wrong + assert str(mpf(2)**0.5)[-10:-1] == '9518488472'[:9] + assert str(pi)[-10:-1] == '2164201989'[:9] + mp.dps = 15 + +def test_str_10000_digits(): + mp.dps = 10001 + # last digit may be wrong + assert str(mpf(2)**0.5)[-10:-1] == '5873258351'[:9] + assert str(pi)[-10:-1] == '5256375678'[:9] + mp.dps = 15 + +def test_monitor(): + f = lambda x: x**2 + a = [] + b = [] + g = monitor(f, a.append, b.append) + assert g(3) == 9 + assert g(4) == 16 + assert a[0] == ((3,), {}) + assert b[0] == 9 + +def test_nint_distance(): + assert nint_distance(mpf(-3)) == (-3, -inf) + assert nint_distance(mpc(-3)) == (-3, -inf) + assert nint_distance(mpf(-3.1)) == (-3, -3) + assert nint_distance(mpf(-3.01)) == (-3, -6) + assert nint_distance(mpf(-3.001)) == (-3, -9) + assert nint_distance(mpf(-3.0001)) == (-3, -13) + assert nint_distance(mpf(-2.9)) == (-3, -3) + assert nint_distance(mpf(-2.99)) == (-3, -6) + assert nint_distance(mpf(-2.999)) == (-3, -9) + assert nint_distance(mpf(-2.9999)) == (-3, -13) + assert nint_distance(mpc(-3+0.1j)) == (-3, -3) + assert nint_distance(mpc(-3+0.01j)) == (-3, -6) + assert nint_distance(mpc(-3.1+0.1j)) == (-3, -3) + assert nint_distance(mpc(-3.01+0.01j)) == (-3, -6) + assert nint_distance(mpc(-3.001+0.001j)) == (-3, -9) + assert nint_distance(mpf(0)) == (0, -inf) + assert nint_distance(mpf(0.01)) == (0, -6) + assert nint_distance(mpf('1e-100')) == (0, -332) + +def test_floor_ceil_nint_frac(): + mp.dps = 15 + for n in range(-10,10): + assert floor(n) == n + assert floor(n+0.5) == n + assert ceil(n) == n + assert ceil(n+0.5) == n+1 + assert nint(n) == n + # nint rounds to even + if n % 2 == 1: + assert nint(n+0.5) == n+1 + else: + assert nint(n+0.5) == n + assert floor(inf) == inf + assert floor(ninf) == ninf + assert isnan(floor(nan)) + assert ceil(inf) == inf + assert ceil(ninf) == ninf + assert isnan(ceil(nan)) + assert nint(inf) == inf + assert nint(ninf) == ninf + assert isnan(nint(nan)) + assert floor(0.1) == 0 + assert floor(0.9) == 0 + assert floor(-0.1) == -1 + assert floor(-0.9) == -1 + assert floor(10000000000.1) == 10000000000 + assert floor(10000000000.9) == 10000000000 + assert floor(-10000000000.1) == -10000000000-1 + assert floor(-10000000000.9) == -10000000000-1 + assert floor(1e-100) == 0 + assert floor(-1e-100) == -1 + assert floor(1e100) == 1e100 + assert floor(-1e100) == -1e100 + assert ceil(0.1) == 1 + assert ceil(0.9) == 1 + assert ceil(-0.1) == 0 + assert ceil(-0.9) == 0 + assert ceil(10000000000.1) == 10000000000+1 + assert ceil(10000000000.9) == 10000000000+1 + assert ceil(-10000000000.1) == -10000000000 + assert ceil(-10000000000.9) == -10000000000 + assert ceil(1e-100) == 1 + assert ceil(-1e-100) == 0 + assert ceil(1e100) == 1e100 + assert ceil(-1e100) == -1e100 + assert nint(0.1) == 0 + assert nint(0.9) == 1 + assert nint(-0.1) == 0 + assert nint(-0.9) == -1 + assert nint(10000000000.1) == 10000000000 + assert nint(10000000000.9) == 10000000000+1 + assert nint(-10000000000.1) == -10000000000 + assert nint(-10000000000.9) == -10000000000-1 + assert nint(1e-100) == 0 + assert nint(-1e-100) == 0 + assert nint(1e100) == 1e100 + assert nint(-1e100) == -1e100 + assert floor(3.2+4.6j) == 3+4j + assert ceil(3.2+4.6j) == 4+5j + assert nint(3.2+4.6j) == 3+5j + for n in range(-10,10): + assert frac(n) == 0 + assert frac(0.25) == 0.25 + assert frac(1.25) == 0.25 + assert frac(2.25) == 0.25 + assert frac(-0.25) == 0.75 + assert frac(-1.25) == 0.75 + assert frac(-2.25) == 0.75 + assert frac('1e100000000000000') == 0 + u = mpf('1e-100000000000000') + assert frac(u) == u + assert frac(-u) == 1 # rounding! + u = mpf('1e-400') + assert frac(-u, prec=0) == fsub(1, u, exact=True) + assert frac(3.25+4.75j) == 0.25+0.75j + +def test_isnan_etc(): + from mpmath.rational import mpq + assert isnan(nan) == True + assert isnan(3) == False + assert isnan(mpf(3)) == False + assert isnan(inf) == False + assert isnan(mpc(2,nan)) == True + assert isnan(mpc(2,nan)) == True + assert isnan(mpc(nan,nan)) == True + assert isnan(mpc(2,2)) == False + assert isnan(mpc(nan,inf)) == True + assert isnan(mpc(inf,inf)) == False + assert isnan(mpq((3,2))) == False + assert isnan(mpq((0,1))) == False + assert isinf(inf) == True + assert isinf(-inf) == True + assert isinf(3) == False + assert isinf(nan) == False + assert isinf(3+4j) == False + assert isinf(mpc(inf)) == True + assert isinf(mpc(3,inf)) == True + assert isinf(mpc(inf,3)) == True + assert isinf(mpc(inf,inf)) == True + assert isinf(mpc(nan,inf)) == True + assert isinf(mpc(inf,nan)) == True + assert isinf(mpc(nan,nan)) == False + assert isinf(mpq((3,2))) == False + assert isinf(mpq((0,1))) == False + assert isnormal(3) == True + assert isnormal(3.5) == True + assert isnormal(mpf(3.5)) == True + assert isnormal(0) == False + assert isnormal(mpf(0)) == False + assert isnormal(0.0) == False + assert isnormal(inf) == False + assert isnormal(-inf) == False + assert isnormal(nan) == False + assert isnormal(float(inf)) == False + assert isnormal(mpc(0,0)) == False + assert isnormal(mpc(3,0)) == True + assert isnormal(mpc(0,3)) == True + assert isnormal(mpc(3,3)) == True + assert isnormal(mpc(0,nan)) == False + assert isnormal(mpc(0,inf)) == False + assert isnormal(mpc(3,nan)) == False + assert isnormal(mpc(3,inf)) == False + assert isnormal(mpc(3,-inf)) == False + assert isnormal(mpc(nan,0)) == False + assert isnormal(mpc(inf,0)) == False + assert isnormal(mpc(nan,3)) == False + assert isnormal(mpc(inf,3)) == False + assert isnormal(mpc(inf,nan)) == False + assert isnormal(mpc(nan,inf)) == False + assert isnormal(mpc(nan,nan)) == False + assert isnormal(mpc(inf,inf)) == False + assert isnormal(mpq((3,2))) == True + assert isnormal(mpq((0,1))) == False + assert isint(3) == True + assert isint(0) == True + assert isint(long(3)) == True + assert isint(long(0)) == True + assert isint(mpf(3)) == True + assert isint(mpf(0)) == True + assert isint(mpf(-3)) == True + assert isint(mpf(3.2)) == False + assert isint(3.2) == False + assert isint(nan) == False + assert isint(inf) == False + assert isint(-inf) == False + assert isint(mpc(0)) == True + assert isint(mpc(3)) == True + assert isint(mpc(3.2)) == False + assert isint(mpc(3,inf)) == False + assert isint(mpc(inf)) == False + assert isint(mpc(3,2)) == False + assert isint(mpc(0,2)) == False + assert isint(mpc(3,2),gaussian=True) == True + assert isint(mpc(3,0),gaussian=True) == True + assert isint(mpc(0,3),gaussian=True) == True + assert isint(3+4j) == False + assert isint(3+4j, gaussian=True) == True + assert isint(3+0j) == True + assert isint(mpq((3,2))) == False + assert isint(mpq((3,9))) == False + assert isint(mpq((9,3))) == True + assert isint(mpq((0,4))) == True + assert isint(mpq((1,1))) == True + assert isint(mpq((-1,1))) == True + assert mp.isnpint(0) == True + assert mp.isnpint(1) == False + assert mp.isnpint(-1) == True + assert mp.isnpint(-1.1) == False + assert mp.isnpint(-1.0) == True + assert mp.isnpint(mp.mpq(1,2)) == False + assert mp.isnpint(mp.mpq(-1,2)) == False + assert mp.isnpint(mp.mpq(-3,1)) == True + assert mp.isnpint(mp.mpq(0,1)) == True + assert mp.isnpint(mp.mpq(1,1)) == False + assert mp.isnpint(0+0j) == True + assert mp.isnpint(-1+0j) == True + assert mp.isnpint(-1.1+0j) == False + assert mp.isnpint(-1+0.1j) == False + assert mp.isnpint(0+0.1j) == False + + +def test_issue_438(): + assert mpf(finf) == mpf('inf') + assert mpf(fninf) == mpf('-inf') + assert mpf(fnan)._mpf_ == mpf('nan')._mpf_ diff --git a/lib/python3.12/site-packages/mpmath/tests/test_bitwise.py b/lib/python3.12/site-packages/mpmath/tests/test_bitwise.py new file mode 100644 index 0000000000000000000000000000000000000000..4f61b69fc8819cf275abaedd98847c58c3b5924a --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/tests/test_bitwise.py @@ -0,0 +1,188 @@ +""" +Test bit-level integer and mpf operations +""" + +from mpmath import * +from mpmath.libmp import * + +def test_bitcount(): + assert bitcount(0) == 0 + assert bitcount(1) == 1 + assert bitcount(7) == 3 + assert bitcount(8) == 4 + assert bitcount(2**100) == 101 + assert bitcount(2**100-1) == 100 + +def test_trailing(): + assert trailing(0) == 0 + assert trailing(1) == 0 + assert trailing(2) == 1 + assert trailing(7) == 0 + assert trailing(8) == 3 + assert trailing(2**100) == 100 + assert trailing(2**100-1) == 0 + +def test_round_down(): + assert from_man_exp(0, -4, 4, round_down)[:3] == (0, 0, 0) + assert from_man_exp(0xf0, -4, 4, round_down)[:3] == (0, 15, 0) + assert from_man_exp(0xf1, -4, 4, round_down)[:3] == (0, 15, 0) + assert from_man_exp(0xff, -4, 4, round_down)[:3] == (0, 15, 0) + assert from_man_exp(-0xf0, -4, 4, round_down)[:3] == (1, 15, 0) + assert from_man_exp(-0xf1, -4, 4, round_down)[:3] == (1, 15, 0) + assert from_man_exp(-0xff, -4, 4, round_down)[:3] == (1, 15, 0) + +def test_round_up(): + assert from_man_exp(0, -4, 4, round_up)[:3] == (0, 0, 0) + assert from_man_exp(0xf0, -4, 4, round_up)[:3] == (0, 15, 0) + assert from_man_exp(0xf1, -4, 4, round_up)[:3] == (0, 1, 4) + assert from_man_exp(0xff, -4, 4, round_up)[:3] == (0, 1, 4) + assert from_man_exp(-0xf0, -4, 4, round_up)[:3] == (1, 15, 0) + assert from_man_exp(-0xf1, -4, 4, round_up)[:3] == (1, 1, 4) + assert from_man_exp(-0xff, -4, 4, round_up)[:3] == (1, 1, 4) + +def test_round_floor(): + assert from_man_exp(0, -4, 4, round_floor)[:3] == (0, 0, 0) + assert from_man_exp(0xf0, -4, 4, round_floor)[:3] == (0, 15, 0) + assert from_man_exp(0xf1, -4, 4, round_floor)[:3] == (0, 15, 0) + assert from_man_exp(0xff, -4, 4, round_floor)[:3] == (0, 15, 0) + assert from_man_exp(-0xf0, -4, 4, round_floor)[:3] == (1, 15, 0) + assert from_man_exp(-0xf1, -4, 4, round_floor)[:3] == (1, 1, 4) + assert from_man_exp(-0xff, -4, 4, round_floor)[:3] == (1, 1, 4) + +def test_round_ceiling(): + assert from_man_exp(0, -4, 4, round_ceiling)[:3] == (0, 0, 0) + assert from_man_exp(0xf0, -4, 4, round_ceiling)[:3] == (0, 15, 0) + assert from_man_exp(0xf1, -4, 4, round_ceiling)[:3] == (0, 1, 4) + assert from_man_exp(0xff, -4, 4, round_ceiling)[:3] == (0, 1, 4) + assert from_man_exp(-0xf0, -4, 4, round_ceiling)[:3] == (1, 15, 0) + assert from_man_exp(-0xf1, -4, 4, round_ceiling)[:3] == (1, 15, 0) + assert from_man_exp(-0xff, -4, 4, round_ceiling)[:3] == (1, 15, 0) + +def test_round_nearest(): + assert from_man_exp(0, -4, 4, round_nearest)[:3] == (0, 0, 0) + assert from_man_exp(0xf0, -4, 4, round_nearest)[:3] == (0, 15, 0) + assert from_man_exp(0xf7, -4, 4, round_nearest)[:3] == (0, 15, 0) + assert from_man_exp(0xf8, -4, 4, round_nearest)[:3] == (0, 1, 4) # 1111.1000 -> 10000.0 + assert from_man_exp(0xf9, -4, 4, round_nearest)[:3] == (0, 1, 4) # 1111.1001 -> 10000.0 + assert from_man_exp(0xe8, -4, 4, round_nearest)[:3] == (0, 7, 1) # 1110.1000 -> 1110.0 + assert from_man_exp(0xe9, -4, 4, round_nearest)[:3] == (0, 15, 0) # 1110.1001 -> 1111.0 + assert from_man_exp(-0xf0, -4, 4, round_nearest)[:3] == (1, 15, 0) + assert from_man_exp(-0xf7, -4, 4, round_nearest)[:3] == (1, 15, 0) + assert from_man_exp(-0xf8, -4, 4, round_nearest)[:3] == (1, 1, 4) + assert from_man_exp(-0xf9, -4, 4, round_nearest)[:3] == (1, 1, 4) + assert from_man_exp(-0xe8, -4, 4, round_nearest)[:3] == (1, 7, 1) + assert from_man_exp(-0xe9, -4, 4, round_nearest)[:3] == (1, 15, 0) + +def test_rounding_bugs(): + # 1 less than power-of-two cases + assert from_man_exp(72057594037927935, -56, 53, round_up) == (0, 1, 0, 1) + assert from_man_exp(73786976294838205979, -65, 53, round_nearest) == (0, 1, 1, 1) + assert from_man_exp(31, 0, 4, round_up) == (0, 1, 5, 1) + assert from_man_exp(-31, 0, 4, round_floor) == (1, 1, 5, 1) + assert from_man_exp(255, 0, 7, round_up) == (0, 1, 8, 1) + assert from_man_exp(-255, 0, 7, round_floor) == (1, 1, 8, 1) + +def test_rounding_issue_200(): + a = from_man_exp(9867,-100) + b = from_man_exp(9867,-200) + c = from_man_exp(-1,0) + z = (1, 1023, -10, 10) + assert mpf_add(a, c, 10, 'd') == z + assert mpf_add(b, c, 10, 'd') == z + assert mpf_add(c, a, 10, 'd') == z + assert mpf_add(c, b, 10, 'd') == z + +def test_perturb(): + a = fone + b = from_float(0.99999999999999989) + c = from_float(1.0000000000000002) + assert mpf_perturb(a, 0, 53, round_nearest) == a + assert mpf_perturb(a, 1, 53, round_nearest) == a + assert mpf_perturb(a, 0, 53, round_up) == c + assert mpf_perturb(a, 0, 53, round_ceiling) == c + assert mpf_perturb(a, 0, 53, round_down) == a + assert mpf_perturb(a, 0, 53, round_floor) == a + assert mpf_perturb(a, 1, 53, round_up) == a + assert mpf_perturb(a, 1, 53, round_ceiling) == a + assert mpf_perturb(a, 1, 53, round_down) == b + assert mpf_perturb(a, 1, 53, round_floor) == b + a = mpf_neg(a) + b = mpf_neg(b) + c = mpf_neg(c) + assert mpf_perturb(a, 0, 53, round_nearest) == a + assert mpf_perturb(a, 1, 53, round_nearest) == a + assert mpf_perturb(a, 0, 53, round_up) == a + assert mpf_perturb(a, 0, 53, round_floor) == a + assert mpf_perturb(a, 0, 53, round_down) == b + assert mpf_perturb(a, 0, 53, round_ceiling) == b + assert mpf_perturb(a, 1, 53, round_up) == c + assert mpf_perturb(a, 1, 53, round_floor) == c + assert mpf_perturb(a, 1, 53, round_down) == a + assert mpf_perturb(a, 1, 53, round_ceiling) == a + +def test_add_exact(): + ff = from_float + assert mpf_add(ff(3.0), ff(2.5)) == ff(5.5) + assert mpf_add(ff(3.0), ff(-2.5)) == ff(0.5) + assert mpf_add(ff(-3.0), ff(2.5)) == ff(-0.5) + assert mpf_add(ff(-3.0), ff(-2.5)) == ff(-5.5) + assert mpf_sub(mpf_add(fone, ff(1e-100)), fone) == ff(1e-100) + assert mpf_sub(mpf_add(ff(1e-100), fone), fone) == ff(1e-100) + assert mpf_sub(mpf_add(fone, ff(-1e-100)), fone) == ff(-1e-100) + assert mpf_sub(mpf_add(ff(-1e-100), fone), fone) == ff(-1e-100) + assert mpf_add(fone, fzero) == fone + assert mpf_add(fzero, fone) == fone + assert mpf_add(fzero, fzero) == fzero + +def test_long_exponent_shifts(): + mp.dps = 15 + # Check for possible bugs due to exponent arithmetic overflow + # in a C implementation + x = mpf(1) + for p in [32, 64]: + a = ldexp(1,2**(p-1)) + b = ldexp(1,2**p) + c = ldexp(1,2**(p+1)) + d = ldexp(1,-2**(p-1)) + e = ldexp(1,-2**p) + f = ldexp(1,-2**(p+1)) + assert (x+a) == a + assert (x+b) == b + assert (x+c) == c + assert (x+d) == x + assert (x+e) == x + assert (x+f) == x + assert (a+x) == a + assert (b+x) == b + assert (c+x) == c + assert (d+x) == x + assert (e+x) == x + assert (f+x) == x + assert (x-a) == -a + assert (x-b) == -b + assert (x-c) == -c + assert (x-d) == x + assert (x-e) == x + assert (x-f) == x + assert (a-x) == a + assert (b-x) == b + assert (c-x) == c + assert (d-x) == -x + assert (e-x) == -x + assert (f-x) == -x + +def test_float_rounding(): + mp.prec = 64 + for x in [mpf(1), mpf(1)+eps, mpf(1)-eps, -mpf(1)+eps, -mpf(1)-eps]: + fa = float(x) + fb = float(fadd(x,0,prec=53,rounding='n')) + assert fa == fb + z = mpc(x,x) + ca = complex(z) + cb = complex(fadd(z,0,prec=53,rounding='n')) + assert ca == cb + for rnd in ['n', 'd', 'u', 'f', 'c']: + fa = to_float(x._mpf_, rnd=rnd) + fb = to_float(fadd(x,0,prec=53,rounding=rnd)._mpf_, rnd=rnd) + assert fa == fb + mp.prec = 53 diff --git a/lib/python3.12/site-packages/mpmath/tests/test_calculus.py b/lib/python3.12/site-packages/mpmath/tests/test_calculus.py new file mode 100644 index 0000000000000000000000000000000000000000..f0a59773d672f0db20bb5072773472a5a3cc1d1f --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/tests/test_calculus.py @@ -0,0 +1,216 @@ +import pytest +from mpmath import * + +def test_approximation(): + mp.dps = 15 + f = lambda x: cos(2-2*x)/x + p, err = chebyfit(f, [2, 4], 8, error=True) + assert err < 1e-5 + for i in range(10): + x = 2 + i/5. + assert abs(polyval(p, x) - f(x)) < err + +def test_limits(): + mp.dps = 15 + assert limit(lambda x: (x-sin(x))/x**3, 0).ae(mpf(1)/6) + assert limit(lambda n: (1+1/n)**n, inf).ae(e) + +def test_polyval(): + assert polyval([], 3) == 0 + assert polyval([0], 3) == 0 + assert polyval([5], 3) == 5 + # 4x^3 - 2x + 5 + p = [4, 0, -2, 5] + assert polyval(p,4) == 253 + assert polyval(p,4,derivative=True) == (253, 190) + +def test_polyroots(): + p = polyroots([1,-4]) + assert p[0].ae(4) + p, q = polyroots([1,2,3]) + assert p.ae(-1 - sqrt(2)*j) + assert q.ae(-1 + sqrt(2)*j) + #this is not a real test, it only tests a specific case + assert polyroots([1]) == [] + pytest.raises(ValueError, lambda: polyroots([0])) + +def test_polyroots_legendre(): + n = 64 + coeffs = [11975573020964041433067793888190275875, 0, + -190100434726484311252477736051902332000, 0, + 1437919688271127330313741595496589239248, 0, + -6897338342113537600691931230430793911840, 0, + 23556405536185284408974715545252277554280, 0, + -60969520211303089058522793175947071316960, 0, + 124284021969194758465450309166353645376880, 0, + -204721258548015217049921875719981284186016, 0, + 277415422258095841688223780704620656114900, 0, + -313237834141273382807123548182995095192800, 0, + 297432255354328395601259515935229287637200, 0, + -239057700565161140389797367947941296605600, 0, + 163356095386193445933028201431093219347160, 0, + -95158890516229191805647495979277603503200, 0, + 47310254620162038075933656063247634556400, 0, + -20071017111583894941305187420771723751200, 0, + 7255051932731034189479516844750603752850, 0, + -2228176940331017311443863996901733412640, 0, + 579006552594977616773047095969088431600, 0, + -126584428502545713788439446082310831200, 0, + 23112325428835593809686977515028663000, 0, + -3491517141958743235617737161547844000, 0, + 431305058712550634988073414073557200, 0, + -42927166660756742088912492757452000, 0, + 3378527005707706553294038781836500, 0, + -205277590220215081719131470288800, 0, + 9330799555464321896324157740400, 0, + -304114948474392713657972548576, 0, + 6695289961520387531608984680, 0, + -91048139350447232095702560, 0, + 659769125727878493447120, 0, + -1905929106580294155360, 0, + 916312070471295267] + + with mp.workdps(3): + with pytest.raises(mp.NoConvergence): + polyroots(coeffs, maxsteps=5, cleanup=True, error=False, + extraprec=n*10) + + roots = polyroots(coeffs, maxsteps=50, cleanup=True, error=False, + extraprec=n*10) + roots = [str(r) for r in roots] + assert roots == \ + ['-0.999', '-0.996', '-0.991', '-0.983', '-0.973', '-0.961', + '-0.946', '-0.93', '-0.911', '-0.889', '-0.866', '-0.841', + '-0.813', '-0.784', '-0.753', '-0.72', '-0.685', '-0.649', + '-0.611', '-0.572', '-0.531', '-0.489', '-0.446', '-0.402', + '-0.357', '-0.311', '-0.265', '-0.217', '-0.17', '-0.121', + '-0.073', '-0.0243', '0.0243', '0.073', '0.121', '0.17', '0.217', + '0.265', '0.311', '0.357', '0.402', '0.446', '0.489', '0.531', + '0.572', '0.611', '0.649', '0.685', '0.72', '0.753', '0.784', + '0.813', '0.841', '0.866', '0.889', '0.911', '0.93', '0.946', + '0.961', '0.973', '0.983', '0.991', '0.996', '0.999'] + +def test_polyroots_legendre_init(): + extra_prec = 100 + coeffs = [11975573020964041433067793888190275875, 0, + -190100434726484311252477736051902332000, 0, + 1437919688271127330313741595496589239248, 0, + -6897338342113537600691931230430793911840, 0, + 23556405536185284408974715545252277554280, 0, + -60969520211303089058522793175947071316960, 0, + 124284021969194758465450309166353645376880, 0, + -204721258548015217049921875719981284186016, 0, + 277415422258095841688223780704620656114900, 0, + -313237834141273382807123548182995095192800, 0, + 297432255354328395601259515935229287637200, 0, + -239057700565161140389797367947941296605600, 0, + 163356095386193445933028201431093219347160, 0, + -95158890516229191805647495979277603503200, 0, + 47310254620162038075933656063247634556400, 0, + -20071017111583894941305187420771723751200, 0, + 7255051932731034189479516844750603752850, 0, + -2228176940331017311443863996901733412640, 0, + 579006552594977616773047095969088431600, 0, + -126584428502545713788439446082310831200, 0, + 23112325428835593809686977515028663000, 0, + -3491517141958743235617737161547844000, 0, + 431305058712550634988073414073557200, 0, + -42927166660756742088912492757452000, 0, + 3378527005707706553294038781836500, 0, + -205277590220215081719131470288800, 0, + 9330799555464321896324157740400, 0, + -304114948474392713657972548576, 0, + 6695289961520387531608984680, 0, + -91048139350447232095702560, 0, + 659769125727878493447120, 0, + -1905929106580294155360, 0, + 916312070471295267] + + roots_init = matrix(['-0.999', '-0.996', '-0.991', '-0.983', '-0.973', + '-0.961', '-0.946', '-0.93', '-0.911', '-0.889', + '-0.866', '-0.841', '-0.813', '-0.784', '-0.753', + '-0.72', '-0.685', '-0.649', '-0.611', '-0.572', + '-0.531', '-0.489', '-0.446', '-0.402', '-0.357', + '-0.311', '-0.265', '-0.217', '-0.17', '-0.121', + '-0.073', '-0.0243', '0.0243', '0.073', '0.121', + '0.17', '0.217', '0.265', ' 0.311', '0.357', + '0.402', '0.446', '0.489', '0.531', '0.572', + '0.611', '0.649', '0.685', '0.72', '0.753', + '0.784', '0.813', '0.841', '0.866', '0.889', + '0.911', '0.93', '0.946', '0.961', '0.973', + '0.983', '0.991', '0.996', '0.999', '1.0']) + with mp.workdps(2*mp.dps): + roots_exact = polyroots(coeffs, maxsteps=50, cleanup=True, error=False, + extraprec=2*extra_prec) + with pytest.raises(mp.NoConvergence): + polyroots(coeffs, maxsteps=5, cleanup=True, error=False, + extraprec=extra_prec) + roots,err = polyroots(coeffs, maxsteps=5, cleanup=True, error=True, + extraprec=extra_prec,roots_init=roots_init) + assert max(matrix(roots_exact)-matrix(roots).apply(abs)) < err + roots1,err1 = polyroots(coeffs, maxsteps=25, cleanup=True, error=True, + extraprec=extra_prec,roots_init=roots_init[:60]) + assert max(matrix(roots_exact)-matrix(roots1).apply(abs)) < err1 + +def test_pade(): + one = mpf(1) + mp.dps = 20 + N = 10 + a = [one] + k = 1 + for i in range(1, N+1): + k *= i + a.append(one/k) + p, q = pade(a, N//2, N//2) + for x in arange(0, 1, 0.1): + r = polyval(p[::-1], x)/polyval(q[::-1], x) + assert(r.ae(exp(x), 1.0e-10)) + mp.dps = 15 + +def test_fourier(): + mp.dps = 15 + c, s = fourier(lambda x: x+1, [-1, 2], 2) + #plot([lambda x: x+1, lambda x: fourierval((c, s), [-1, 2], x)], [-1, 2]) + assert c[0].ae(1.5) + assert c[1].ae(-3*sqrt(3)/(2*pi)) + assert c[2].ae(3*sqrt(3)/(4*pi)) + assert s[0] == 0 + assert s[1].ae(3/(2*pi)) + assert s[2].ae(3/(4*pi)) + assert fourierval((c, s), [-1, 2], 1).ae(1.9134966715663442) + +def test_differint(): + mp.dps = 15 + assert differint(lambda t: t, 2, -0.5).ae(8*sqrt(2/pi)/3) + +def test_invlap(): + mp.dps = 15 + t = 0.01 + fp = lambda p: 1/(p+1)**2 + ft = lambda t: t*exp(-t) + ftt = ft(t) + assert invertlaplace(fp,t,method='talbot').ae(ftt) + assert invertlaplace(fp,t,method='stehfest').ae(ftt) + assert invertlaplace(fp,t,method='dehoog').ae(ftt) + assert invertlaplace(fp,t,method='cohen').ae(ftt) + t = 1.0 + ftt = ft(t) + assert invertlaplace(fp,t,method='talbot').ae(ftt) + assert invertlaplace(fp,t,method='stehfest').ae(ftt) + assert invertlaplace(fp,t,method='dehoog').ae(ftt) + assert invertlaplace(fp,t,method='cohen').ae(ftt) + + t = 0.01 + fp = lambda p: log(p)/p + ft = lambda t: -euler-log(t) + ftt = ft(t) + assert invertlaplace(fp,t,method='talbot').ae(ftt) + assert invertlaplace(fp,t,method='stehfest').ae(ftt) + assert invertlaplace(fp,t,method='dehoog').ae(ftt) + assert invertlaplace(fp,t,method='cohen').ae(ftt) + t = 1.0 + ftt = ft(t) + assert invertlaplace(fp,t,method='talbot').ae(ftt) + assert invertlaplace(fp,t,method='stehfest').ae(ftt) + assert invertlaplace(fp,t,method='dehoog').ae(ftt) + assert invertlaplace(fp,t,method='cohen').ae(ftt) diff --git a/lib/python3.12/site-packages/mpmath/tests/test_compatibility.py b/lib/python3.12/site-packages/mpmath/tests/test_compatibility.py new file mode 100644 index 0000000000000000000000000000000000000000..f26d6044b521306b6d1eaeadc5c7839be226dc54 --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/tests/test_compatibility.py @@ -0,0 +1,77 @@ +from mpmath import * +from random import seed, randint, random +import math + +# Test compatibility with Python floats, which are +# IEEE doubles (53-bit) + +N = 5000 +seed(1) + +# Choosing exponents between roughly -140, 140 ensures that +# the Python floats don't overflow or underflow +xs = [(random()-1) * 10**randint(-140, 140) for x in range(N)] +ys = [(random()-1) * 10**randint(-140, 140) for x in range(N)] + +# include some equal values +ys[int(N*0.8):] = xs[int(N*0.8):] + +# Detect whether Python is compiled to use 80-bit floating-point +# instructions, in which case the double compatibility test breaks +uses_x87 = -4.1974624032366689e+117 / -8.4657370748010221e-47 \ + == 4.9581771393902231e+163 + +def test_double_compatibility(): + mp.prec = 53 + for x, y in zip(xs, ys): + mpx = mpf(x) + mpy = mpf(y) + assert mpf(x) == x + assert (mpx < mpy) == (x < y) + assert (mpx > mpy) == (x > y) + assert (mpx == mpy) == (x == y) + assert (mpx != mpy) == (x != y) + assert (mpx <= mpy) == (x <= y) + assert (mpx >= mpy) == (x >= y) + assert mpx == mpx + if uses_x87: + mp.prec = 64 + a = mpx + mpy + b = mpx * mpy + c = mpx / mpy + d = mpx % mpy + mp.prec = 53 + assert +a == x + y + assert +b == x * y + assert +c == x / y + assert +d == x % y + else: + assert mpx + mpy == x + y + assert mpx * mpy == x * y + assert mpx / mpy == x / y + assert mpx % mpy == x % y + assert abs(mpx) == abs(x) + assert mpf(repr(x)) == x + assert ceil(mpx) == math.ceil(x) + assert floor(mpx) == math.floor(x) + +def test_sqrt(): + # this fails quite often. it appers to be float + # that rounds the wrong way, not mpf + fail = 0 + mp.prec = 53 + for x in xs: + x = abs(x) + mp.prec = 100 + mp_high = mpf(x)**0.5 + mp.prec = 53 + mp_low = mpf(x)**0.5 + fp = x**0.5 + assert abs(mp_low-mp_high) <= abs(fp-mp_high) + fail += mp_low != fp + assert fail < N/10 + +def test_bugs(): + # particular bugs + assert mpf(4.4408920985006262E-16) < mpf(1.7763568394002505E-15) + assert mpf(-4.4408920985006262E-16) > mpf(-1.7763568394002505E-15) diff --git a/lib/python3.12/site-packages/mpmath/tests/test_convert.py b/lib/python3.12/site-packages/mpmath/tests/test_convert.py new file mode 100644 index 0000000000000000000000000000000000000000..cb1db5b55c89e980e08fc3fa43cc9715ad68cac9 --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/tests/test_convert.py @@ -0,0 +1,233 @@ +import random +from mpmath import * +from mpmath.libmp import * + + +def test_basic_string(): + """ + Test basic string conversion + """ + mp.dps = 15 + assert mpf('3') == mpf('3.0') == mpf('0003.') == mpf('0.03e2') == mpf(3.0) + assert mpf('30') == mpf('30.0') == mpf('00030.') == mpf(30.0) + for i in range(10): + for j in range(10): + assert mpf('%ie%i' % (i,j)) == i * 10**j + assert str(mpf('25000.0')) == '25000.0' + assert str(mpf('2500.0')) == '2500.0' + assert str(mpf('250.0')) == '250.0' + assert str(mpf('25.0')) == '25.0' + assert str(mpf('2.5')) == '2.5' + assert str(mpf('0.25')) == '0.25' + assert str(mpf('0.025')) == '0.025' + assert str(mpf('0.0025')) == '0.0025' + assert str(mpf('0.00025')) == '0.00025' + assert str(mpf('0.000025')) == '2.5e-5' + assert str(mpf(0)) == '0.0' + assert str(mpf('2.5e1000000000000000000000')) == '2.5e+1000000000000000000000' + assert str(mpf('2.6e-1000000000000000000000')) == '2.6e-1000000000000000000000' + assert str(mpf(1.23402834e-15)) == '1.23402834e-15' + assert str(mpf(-1.23402834e-15)) == '-1.23402834e-15' + assert str(mpf(-1.2344e-15)) == '-1.2344e-15' + assert repr(mpf(-1.2344e-15)) == "mpf('-1.2343999999999999e-15')" + assert str(mpf("2163048125L")) == '2163048125.0' + assert str(mpf("-2163048125l")) == '-2163048125.0' + assert str(mpf("-2163048125L/1088391168")) == '-1.98738118113799' + assert str(mpf("2163048125/1088391168l")) == '1.98738118113799' + +def test_pretty(): + mp.pretty = True + assert repr(mpf(2.5)) == '2.5' + assert repr(mpc(2.5,3.5)) == '(2.5 + 3.5j)' + mp.pretty = False + iv.pretty = True + assert repr(mpi(2.5,3.5)) == '[2.5, 3.5]' + iv.pretty = False + +def test_str_whitespace(): + assert mpf('1.26 ') == 1.26 + +def test_unicode(): + mp.dps = 15 + try: + unicode = unicode + except NameError: + unicode = str + assert mpf(unicode('2.76')) == 2.76 + assert mpf(unicode('inf')) == inf + +def test_str_format(): + assert to_str(from_float(0.1),15,strip_zeros=False) == '0.100000000000000' + assert to_str(from_float(0.0),15,show_zero_exponent=True) == '0.0e+0' + assert to_str(from_float(0.0),0,show_zero_exponent=True) == '.0e+0' + assert to_str(from_float(0.0),0,show_zero_exponent=False) == '.0' + assert to_str(from_float(0.0),1,show_zero_exponent=True) == '0.0e+0' + assert to_str(from_float(0.0),1,show_zero_exponent=False) == '0.0' + assert to_str(from_float(1.23),3,show_zero_exponent=True) == '1.23e+0' + assert to_str(from_float(1.23456789000000e-2),15,strip_zeros=False,min_fixed=0,max_fixed=0) == '1.23456789000000e-2' + assert to_str(from_float(1.23456789000000e+2),15,strip_zeros=False,min_fixed=0,max_fixed=0) == '1.23456789000000e+2' + assert to_str(from_float(2.1287e14), 15, max_fixed=1000) == '212870000000000.0' + assert to_str(from_float(2.1287e15), 15, max_fixed=1000) == '2128700000000000.0' + assert to_str(from_float(2.1287e16), 15, max_fixed=1000) == '21287000000000000.0' + assert to_str(from_float(2.1287e30), 15, max_fixed=1000) == '2128700000000000000000000000000.0' + +def test_tight_string_conversion(): + mp.dps = 15 + # In an old version, '0.5' wasn't recognized as representing + # an exact binary number and was erroneously rounded up or down + assert from_str('0.5', 10, round_floor) == fhalf + assert from_str('0.5', 10, round_ceiling) == fhalf + +def test_eval_repr_invariant(): + """Test that eval(repr(x)) == x""" + random.seed(123) + for dps in [10, 15, 20, 50, 100]: + mp.dps = dps + for i in range(1000): + a = mpf(random.random())**0.5 * 10**random.randint(-100, 100) + assert eval(repr(a)) == a + mp.dps = 15 + +def test_str_bugs(): + mp.dps = 15 + # Decimal rounding used to give the wrong exponent in some cases + assert str(mpf('1e600')) == '1.0e+600' + assert str(mpf('1e10000')) == '1.0e+10000' + +def test_str_prec0(): + assert to_str(from_float(1.234), 0) == '.0e+0' + assert to_str(from_float(1e-15), 0) == '.0e-15' + assert to_str(from_float(1e+15), 0) == '.0e+15' + assert to_str(from_float(-1e-15), 0) == '-.0e-15' + assert to_str(from_float(-1e+15), 0) == '-.0e+15' + +def test_convert_rational(): + mp.dps = 15 + assert from_rational(30, 5, 53, round_nearest) == (0, 3, 1, 2) + assert from_rational(-7, 4, 53, round_nearest) == (1, 7, -2, 3) + assert to_rational((0, 1, -1, 1)) == (1, 2) + +def test_custom_class(): + class mympf: + @property + def _mpf_(self): + return mpf(3.5)._mpf_ + class mympc: + @property + def _mpc_(self): + return mpf(3.5)._mpf_, mpf(2.5)._mpf_ + assert mpf(2) + mympf() == 5.5 + assert mympf() + mpf(2) == 5.5 + assert mpf(mympf()) == 3.5 + assert mympc() + mpc(2) == mpc(5.5, 2.5) + assert mpc(2) + mympc() == mpc(5.5, 2.5) + assert mpc(mympc()) == (3.5+2.5j) + +def test_conversion_methods(): + class SomethingRandom: + pass + class SomethingReal: + def _mpmath_(self, prec, rounding): + return mp.make_mpf(from_str('1.3', prec, rounding)) + class SomethingComplex: + def _mpmath_(self, prec, rounding): + return mp.make_mpc((from_str('1.3', prec, rounding), \ + from_str('1.7', prec, rounding))) + x = mpf(3) + z = mpc(3) + a = SomethingRandom() + y = SomethingReal() + w = SomethingComplex() + for d in [15, 45]: + mp.dps = d + assert (x+y).ae(mpf('4.3')) + assert (y+x).ae(mpf('4.3')) + assert (x+w).ae(mpc('4.3', '1.7')) + assert (w+x).ae(mpc('4.3', '1.7')) + assert (z+y).ae(mpc('4.3')) + assert (y+z).ae(mpc('4.3')) + assert (z+w).ae(mpc('4.3', '1.7')) + assert (w+z).ae(mpc('4.3', '1.7')) + x-y; y-x; x-w; w-x; z-y; y-z; z-w; w-z + x*y; y*x; x*w; w*x; z*y; y*z; z*w; w*z + x/y; y/x; x/w; w/x; z/y; y/z; z/w; w/z + x**y; y**x; x**w; w**x; z**y; y**z; z**w; w**z + x==y; y==x; x==w; w==x; z==y; y==z; z==w; w==z + mp.dps = 15 + assert x.__add__(a) is NotImplemented + assert x.__radd__(a) is NotImplemented + assert x.__lt__(a) is NotImplemented + assert x.__gt__(a) is NotImplemented + assert x.__le__(a) is NotImplemented + assert x.__ge__(a) is NotImplemented + assert x.__eq__(a) is NotImplemented + assert x.__ne__(a) is NotImplemented + # implementation detail + if hasattr(x, "__cmp__"): + assert x.__cmp__(a) is NotImplemented + assert x.__sub__(a) is NotImplemented + assert x.__rsub__(a) is NotImplemented + assert x.__mul__(a) is NotImplemented + assert x.__rmul__(a) is NotImplemented + assert x.__div__(a) is NotImplemented + assert x.__rdiv__(a) is NotImplemented + assert x.__mod__(a) is NotImplemented + assert x.__rmod__(a) is NotImplemented + assert x.__pow__(a) is NotImplemented + assert x.__rpow__(a) is NotImplemented + assert z.__add__(a) is NotImplemented + assert z.__radd__(a) is NotImplemented + assert z.__eq__(a) is NotImplemented + assert z.__ne__(a) is NotImplemented + assert z.__sub__(a) is NotImplemented + assert z.__rsub__(a) is NotImplemented + assert z.__mul__(a) is NotImplemented + assert z.__rmul__(a) is NotImplemented + assert z.__div__(a) is NotImplemented + assert z.__rdiv__(a) is NotImplemented + assert z.__pow__(a) is NotImplemented + assert z.__rpow__(a) is NotImplemented + +def test_mpmathify(): + assert mpmathify('1/2') == 0.5 + assert mpmathify('(1.0+1.0j)') == mpc(1, 1) + assert mpmathify('(1.2e-10 - 3.4e5j)') == mpc('1.2e-10', '-3.4e5') + assert mpmathify('1j') == mpc(1j) + +def test_issue548(): + try: + # This expression is invalid, but may trigger the ReDOS vulnerability + # in the regular expression for parsing complex numbers. + mpmathify('(' + '1' * 5000 + '!j') + except: + return + # The expression is invalid and should raise an exception. + assert False + +def test_compatibility(): + try: + import numpy as np + from fractions import Fraction + from decimal import Decimal + import decimal + except ImportError: + return + # numpy types + for nptype in np.core.numerictypes.typeDict.values(): + if issubclass(nptype, np.complexfloating): + x = nptype(complex(0.5, -0.5)) + elif issubclass(nptype, np.floating): + x = nptype(0.5) + elif issubclass(nptype, np.integer): + x = nptype(2) + # Handle the weird types + try: diff = np.abs(type(np.sqrt(x))(sqrt(x)) - np.sqrt(x)) + except: continue + assert diff < 2.0**-53 + #Fraction and Decimal + oldprec = mp.prec + mp.prec = 1000 + decimal.getcontext().prec = mp.dps + assert sqrt(Fraction(2, 3)).ae(sqrt(mpf('2/3'))) + assert sqrt(Decimal(2)/Decimal(3)).ae(sqrt(mpf('2/3'))) + mp.prec = oldprec diff --git a/lib/python3.12/site-packages/mpmath/tests/test_diff.py b/lib/python3.12/site-packages/mpmath/tests/test_diff.py new file mode 100644 index 0000000000000000000000000000000000000000..f5711609da38862eb4fd62c88d35f1704c9425a4 --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/tests/test_diff.py @@ -0,0 +1,61 @@ +from mpmath import * + +def test_diff(): + mp.dps = 15 + assert diff(log, 2.0, n=0).ae(log(2)) + assert diff(cos, 1.0).ae(-sin(1)) + assert diff(abs, 0.0) == 0 + assert diff(abs, 0.0, direction=1) == 1 + assert diff(abs, 0.0, direction=-1) == -1 + assert diff(exp, 1.0).ae(e) + assert diff(exp, 1.0, n=5).ae(e) + assert diff(exp, 2.0, n=5, direction=3*j).ae(e**2) + assert diff(lambda x: x**2, 3.0, method='quad').ae(6) + assert diff(lambda x: 3+x**5, 3.0, n=2, method='quad').ae(540) + assert diff(lambda x: 3+x**5, 3.0, n=2, method='step').ae(540) + assert diffun(sin)(2).ae(cos(2)) + assert diffun(sin, n=2)(2).ae(-sin(2)) + +def test_diffs(): + mp.dps = 15 + assert [chop(d) for d in diffs(sin, 0, 1)] == [0, 1] + assert [chop(d) for d in diffs(sin, 0, 1, method='quad')] == [0, 1] + assert [chop(d) for d in diffs(sin, 0, 2)] == [0, 1, 0] + assert [chop(d) for d in diffs(sin, 0, 2, method='quad')] == [0, 1, 0] + +def test_taylor(): + mp.dps = 15 + # Easy to test since the coefficients are exact in floating-point + assert taylor(sqrt, 1, 4) == [1, 0.5, -0.125, 0.0625, -0.0390625] + +def test_diff_partial(): + mp.dps = 15 + x,y,z = xyz = 2,3,7 + f = lambda x,y,z: 3*x**2 * (y+2)**3 * z**5 + assert diff(f, xyz, (0,0,0)).ae(25210500) + assert diff(f, xyz, (0,0,1)).ae(18007500) + assert diff(f, xyz, (0,0,2)).ae(10290000) + assert diff(f, xyz, (0,1,0)).ae(15126300) + assert diff(f, xyz, (0,1,1)).ae(10804500) + assert diff(f, xyz, (0,1,2)).ae(6174000) + assert diff(f, xyz, (0,2,0)).ae(6050520) + assert diff(f, xyz, (0,2,1)).ae(4321800) + assert diff(f, xyz, (0,2,2)).ae(2469600) + assert diff(f, xyz, (1,0,0)).ae(25210500) + assert diff(f, xyz, (1,0,1)).ae(18007500) + assert diff(f, xyz, (1,0,2)).ae(10290000) + assert diff(f, xyz, (1,1,0)).ae(15126300) + assert diff(f, xyz, (1,1,1)).ae(10804500) + assert diff(f, xyz, (1,1,2)).ae(6174000) + assert diff(f, xyz, (1,2,0)).ae(6050520) + assert diff(f, xyz, (1,2,1)).ae(4321800) + assert diff(f, xyz, (1,2,2)).ae(2469600) + assert diff(f, xyz, (2,0,0)).ae(12605250) + assert diff(f, xyz, (2,0,1)).ae(9003750) + assert diff(f, xyz, (2,0,2)).ae(5145000) + assert diff(f, xyz, (2,1,0)).ae(7563150) + assert diff(f, xyz, (2,1,1)).ae(5402250) + assert diff(f, xyz, (2,1,2)).ae(3087000) + assert diff(f, xyz, (2,2,0)).ae(3025260) + assert diff(f, xyz, (2,2,1)).ae(2160900) + assert diff(f, xyz, (2,2,2)).ae(1234800) diff --git a/lib/python3.12/site-packages/mpmath/tests/test_division.py b/lib/python3.12/site-packages/mpmath/tests/test_division.py new file mode 100644 index 0000000000000000000000000000000000000000..c704cadeb953793ac0a887aa09c4278cf68a2824 --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/tests/test_division.py @@ -0,0 +1,143 @@ +from mpmath.libmp import * +from mpmath import mpf, mp + +from random import randint, choice, seed + +all_modes = [round_floor, round_ceiling, round_down, round_up, round_nearest] + +fb = from_bstr +fi = from_int +ff = from_float + + +def test_div_1_3(): + a = fi(1) + b = fi(3) + c = fi(-1) + + # floor rounds down, ceiling rounds up + assert mpf_div(a, b, 7, round_floor) == fb('0.01010101') + assert mpf_div(a, b, 7, round_ceiling) == fb('0.01010110') + assert mpf_div(a, b, 7, round_down) == fb('0.01010101') + assert mpf_div(a, b, 7, round_up) == fb('0.01010110') + assert mpf_div(a, b, 7, round_nearest) == fb('0.01010101') + + # floor rounds up, ceiling rounds down + assert mpf_div(c, b, 7, round_floor) == fb('-0.01010110') + assert mpf_div(c, b, 7, round_ceiling) == fb('-0.01010101') + assert mpf_div(c, b, 7, round_down) == fb('-0.01010101') + assert mpf_div(c, b, 7, round_up) == fb('-0.01010110') + assert mpf_div(c, b, 7, round_nearest) == fb('-0.01010101') + +def test_mpf_divi_1_3(): + a = 1 + b = fi(3) + c = -1 + assert mpf_rdiv_int(a, b, 7, round_floor) == fb('0.01010101') + assert mpf_rdiv_int(a, b, 7, round_ceiling) == fb('0.01010110') + assert mpf_rdiv_int(a, b, 7, round_down) == fb('0.01010101') + assert mpf_rdiv_int(a, b, 7, round_up) == fb('0.01010110') + assert mpf_rdiv_int(a, b, 7, round_nearest) == fb('0.01010101') + assert mpf_rdiv_int(c, b, 7, round_floor) == fb('-0.01010110') + assert mpf_rdiv_int(c, b, 7, round_ceiling) == fb('-0.01010101') + assert mpf_rdiv_int(c, b, 7, round_down) == fb('-0.01010101') + assert mpf_rdiv_int(c, b, 7, round_up) == fb('-0.01010110') + assert mpf_rdiv_int(c, b, 7, round_nearest) == fb('-0.01010101') + + +def test_div_300(): + + q = fi(1000000) + a = fi(300499999) # a/q is a little less than a half-integer + b = fi(300500000) # b/q exactly a half-integer + c = fi(300500001) # c/q is a little more than a half-integer + + # Check nearest integer rounding (prec=9 as 2**8 < 300 < 2**9) + + assert mpf_div(a, q, 9, round_down) == fi(300) + assert mpf_div(b, q, 9, round_down) == fi(300) + assert mpf_div(c, q, 9, round_down) == fi(300) + assert mpf_div(a, q, 9, round_up) == fi(301) + assert mpf_div(b, q, 9, round_up) == fi(301) + assert mpf_div(c, q, 9, round_up) == fi(301) + + # Nearest even integer is down + assert mpf_div(a, q, 9, round_nearest) == fi(300) + assert mpf_div(b, q, 9, round_nearest) == fi(300) + assert mpf_div(c, q, 9, round_nearest) == fi(301) + + # Nearest even integer is up + a = fi(301499999) + b = fi(301500000) + c = fi(301500001) + assert mpf_div(a, q, 9, round_nearest) == fi(301) + assert mpf_div(b, q, 9, round_nearest) == fi(302) + assert mpf_div(c, q, 9, round_nearest) == fi(302) + + +def test_tight_integer_division(): + # Test that integer division at tightest possible precision is exact + N = 100 + seed(1) + for i in range(N): + a = choice([1, -1]) * randint(1, 1< 1: + print("original matrix (hessenberg):\n", A) + + n = A.rows + + Q, H = mp.hessenberg(A) + + if verbose > 1: + print("Q:\n",Q) + print("H:\n",H) + + B = Q * H * Q.transpose_conj() + + eps = mp.exp(0.8 * mp.log(mp.eps)) + + err0 = 0 + for x in xrange(n): + for y in xrange(n): + err0 += abs(A[y,x] - B[y,x]) + err0 /= n * n + + err1 = 0 + for x in xrange(n): + for y in xrange(x + 2, n): + err1 += abs(H[y,x]) + + if verbose > 0: + print("difference (H):", err0, err1) + + if verbose > 1: + print("B:\n", B) + + assert err0 < eps + assert err1 == 0 + + +def run_schur(A, verbose = 0): + if verbose > 1: + print("original matrix (schur):\n", A) + + n = A.rows + + Q, R = mp.schur(A) + + if verbose > 1: + print("Q:\n", Q) + print("R:\n", R) + + B = Q * R * Q.transpose_conj() + C = Q * Q.transpose_conj() + + eps = mp.exp(0.8 * mp.log(mp.eps)) + + err0 = 0 + for x in xrange(n): + for y in xrange(n): + err0 += abs(A[y,x] - B[y,x]) + err0 /= n * n + + err1 = 0 + for x in xrange(n): + for y in xrange(n): + if x == y: + C[y,x] -= 1 + err1 += abs(C[y,x]) + err1 /= n * n + + err2 = 0 + for x in xrange(n): + for y in xrange(x + 1, n): + err2 += abs(R[y,x]) + + if verbose > 0: + print("difference (S):", err0, err1, err2) + + if verbose > 1: + print("B:\n", B) + + assert err0 < eps + assert err1 < eps + assert err2 == 0 + +def run_eig(A, verbose = 0): + if verbose > 1: + print("original matrix (eig):\n", A) + + n = A.rows + + E, EL, ER = mp.eig(A, left = True, right = True) + + if verbose > 1: + print("E:\n", E) + print("EL:\n", EL) + print("ER:\n", ER) + + eps = mp.exp(0.8 * mp.log(mp.eps)) + + err0 = 0 + for i in xrange(n): + B = A * ER[:,i] - E[i] * ER[:,i] + err0 = max(err0, mp.mnorm(B)) + + B = EL[i,:] * A - EL[i,:] * E[i] + err0 = max(err0, mp.mnorm(B)) + + err0 /= n * n + + if verbose > 0: + print("difference (E):", err0) + + assert err0 < eps + +##################### + +def test_eig_dyn(): + v = 0 + for i in xrange(5): + n = 1 + int(mp.rand() * 5) + if mp.rand() > 0.5: + # real + A = 2 * mp.randmatrix(n, n) - 1 + if mp.rand() > 0.5: + A *= 10 + for x in xrange(n): + for y in xrange(n): + A[x,y] = int(A[x,y]) + else: + A = (2 * mp.randmatrix(n, n) - 1) + 1j * (2 * mp.randmatrix(n, n) - 1) + if mp.rand() > 0.5: + A *= 10 + for x in xrange(n): + for y in xrange(n): + A[x,y] = int(mp.re(A[x,y])) + 1j * int(mp.im(A[x,y])) + + run_hessenberg(A, verbose = v) + run_schur(A, verbose = v) + run_eig(A, verbose = v) + +def test_eig(): + v = 0 + AS = [] + + A = mp.matrix([[2, 1, 0], # jordan block of size 3 + [0, 2, 1], + [0, 0, 2]]) + AS.append(A) + AS.append(A.transpose()) + + A = mp.matrix([[2, 0, 0], # jordan block of size 2 + [0, 2, 1], + [0, 0, 2]]) + AS.append(A) + AS.append(A.transpose()) + + A = mp.matrix([[2, 0, 1], # jordan block of size 2 + [0, 2, 0], + [0, 0, 2]]) + AS.append(A) + AS.append(A.transpose()) + + A= mp.matrix([[0, 0, 1], # cyclic + [1, 0, 0], + [0, 1, 0]]) + AS.append(A) + AS.append(A.transpose()) + + for A in AS: + run_hessenberg(A, verbose = v) + run_schur(A, verbose = v) + run_eig(A, verbose = v) diff --git a/lib/python3.12/site-packages/mpmath/tests/test_eigen_symmetric.py b/lib/python3.12/site-packages/mpmath/tests/test_eigen_symmetric.py new file mode 100644 index 0000000000000000000000000000000000000000..aab3d8ea3142aada6e14ad6d3ea25a7e8293554d --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/tests/test_eigen_symmetric.py @@ -0,0 +1,357 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +from mpmath import mp +from mpmath import libmp + +xrange = libmp.backend.xrange + +def run_eigsy(A, verbose = False): + if verbose: + print("original matrix:\n", str(A)) + + D, Q = mp.eigsy(A) + B = Q * mp.diag(D) * Q.transpose() + C = A - B + E = Q * Q.transpose() - mp.eye(A.rows) + + if verbose: + print("eigenvalues:\n", D) + print("eigenvectors:\n", Q) + + NC = mp.mnorm(C) + NE = mp.mnorm(E) + + if verbose: + print("difference:", NC, "\n", C, "\n") + print("difference:", NE, "\n", E, "\n") + + eps = mp.exp( 0.8 * mp.log(mp.eps)) + + assert NC < eps + assert NE < eps + + return NC + +def run_eighe(A, verbose = False): + if verbose: + print("original matrix:\n", str(A)) + + D, Q = mp.eighe(A) + B = Q * mp.diag(D) * Q.transpose_conj() + C = A - B + E = Q * Q.transpose_conj() - mp.eye(A.rows) + + if verbose: + print("eigenvalues:\n", D) + print("eigenvectors:\n", Q) + + NC = mp.mnorm(C) + NE = mp.mnorm(E) + + if verbose: + print("difference:", NC, "\n", C, "\n") + print("difference:", NE, "\n", E, "\n") + + eps = mp.exp( 0.8 * mp.log(mp.eps)) + + assert NC < eps + assert NE < eps + + return NC + +def run_svd_r(A, full_matrices = False, verbose = True): + + m, n = A.rows, A.cols + + eps = mp.exp(0.8 * mp.log(mp.eps)) + + if verbose: + print("original matrix:\n", str(A)) + print("full", full_matrices) + + U, S0, V = mp.svd_r(A, full_matrices = full_matrices) + + S = mp.zeros(U.cols, V.rows) + for j in xrange(min(m, n)): + S[j,j] = S0[j] + + if verbose: + print("U:\n", str(U)) + print("S:\n", str(S0)) + print("V:\n", str(V)) + + C = U * S * V - A + err = mp.mnorm(C) + if verbose: + print("C\n", str(C), "\n", err) + assert err < eps + + D = V * V.transpose() - mp.eye(V.rows) + err = mp.mnorm(D) + if verbose: + print("D:\n", str(D), "\n", err) + assert err < eps + + E = U.transpose() * U - mp.eye(U.cols) + err = mp.mnorm(E) + if verbose: + print("E:\n", str(E), "\n", err) + assert err < eps + +def run_svd_c(A, full_matrices = False, verbose = True): + + m, n = A.rows, A.cols + + eps = mp.exp(0.8 * mp.log(mp.eps)) + + if verbose: + print("original matrix:\n", str(A)) + print("full", full_matrices) + + U, S0, V = mp.svd_c(A, full_matrices = full_matrices) + + S = mp.zeros(U.cols, V.rows) + for j in xrange(min(m, n)): + S[j,j] = S0[j] + + if verbose: + print("U:\n", str(U)) + print("S:\n", str(S0)) + print("V:\n", str(V)) + + C = U * S * V - A + err = mp.mnorm(C) + if verbose: + print("C\n", str(C), "\n", err) + assert err < eps + + D = V * V.transpose_conj() - mp.eye(V.rows) + err = mp.mnorm(D) + if verbose: + print("D:\n", str(D), "\n", err) + assert err < eps + + E = U.transpose_conj() * U - mp.eye(U.cols) + err = mp.mnorm(E) + if verbose: + print("E:\n", str(E), "\n", err) + assert err < eps + +def run_gauss(qtype, a, b): + eps = 1e-5 + + d, e = mp.gauss_quadrature(len(a), qtype) + d -= mp.matrix(a) + e -= mp.matrix(b) + + assert mp.mnorm(d) < eps + assert mp.mnorm(e) < eps + +def irandmatrix(n, range = 10): + """ + random matrix with integer entries + """ + A = mp.matrix(n, n) + for i in xrange(n): + for j in xrange(n): + A[i,j]=int( (2 * mp.rand() - 1) * range) + return A + +####################### + +def test_eighe_fixed_matrix(): + A = mp.matrix([[2, 3], [3, 5]]) + run_eigsy(A) + run_eighe(A) + + A = mp.matrix([[7, -11], [-11, 13]]) + run_eigsy(A) + run_eighe(A) + + A = mp.matrix([[2, 11, 7], [11, 3, 13], [7, 13, 5]]) + run_eigsy(A) + run_eighe(A) + + A = mp.matrix([[2, 0, 7], [0, 3, 1], [7, 1, 5]]) + run_eigsy(A) + run_eighe(A) + + # + + A = mp.matrix([[2, 3+7j], [3-7j, 5]]) + run_eighe(A) + + A = mp.matrix([[2, -11j, 0], [+11j, 3, 29j], [0, -29j, 5]]) + run_eighe(A) + + A = mp.matrix([[2, 11 + 17j, 7 + 19j], [11 - 17j, 3, -13 + 23j], [7 - 19j, -13 - 23j, 5]]) + run_eighe(A) + +def test_eigsy_randmatrix(): + N = 5 + + for a in xrange(10): + A = 2 * mp.randmatrix(N, N) - 1 + + for i in xrange(0, N): + for j in xrange(i + 1, N): + A[j,i] = A[i,j] + + run_eigsy(A) + +def test_eighe_randmatrix(): + N = 5 + + for a in xrange(10): + A = (2 * mp.randmatrix(N, N) - 1) + 1j * (2 * mp.randmatrix(N, N) - 1) + + for i in xrange(0, N): + A[i,i] = mp.re(A[i,i]) + for j in xrange(i + 1, N): + A[j,i] = mp.conj(A[i,j]) + + run_eighe(A) + +def test_eigsy_irandmatrix(): + N = 4 + R = 4 + + for a in xrange(10): + A=irandmatrix(N, R) + + for i in xrange(0, N): + for j in xrange(i + 1, N): + A[j,i] = A[i,j] + + run_eigsy(A) + +def test_eighe_irandmatrix(): + N = 4 + R = 4 + + for a in xrange(10): + A=irandmatrix(N, R) + 1j * irandmatrix(N, R) + + for i in xrange(0, N): + A[i,i] = mp.re(A[i,i]) + for j in xrange(i + 1, N): + A[j,i] = mp.conj(A[i,j]) + + run_eighe(A) + +def test_svd_r_rand(): + for i in xrange(5): + full = mp.rand() > 0.5 + m = 1 + int(mp.rand() * 10) + n = 1 + int(mp.rand() * 10) + A = 2 * mp.randmatrix(m, n) - 1 + if mp.rand() > 0.5: + A *= 10 + for x in xrange(m): + for y in xrange(n): + A[x,y]=int(A[x,y]) + + run_svd_r(A, full_matrices = full, verbose = False) + +def test_svd_c_rand(): + for i in xrange(5): + full = mp.rand() > 0.5 + m = 1 + int(mp.rand() * 10) + n = 1 + int(mp.rand() * 10) + A = (2 * mp.randmatrix(m, n) - 1) + 1j * (2 * mp.randmatrix(m, n) - 1) + if mp.rand() > 0.5: + A *= 10 + for x in xrange(m): + for y in xrange(n): + A[x,y]=int(mp.re(A[x,y])) + 1j * int(mp.im(A[x,y])) + + run_svd_c(A, full_matrices=full, verbose=False) + +def test_svd_test_case(): + # a test case from Golub and Reinsch + # (see wilkinson/reinsch: handbook for auto. comp., vol ii-linear algebra, 134-151(1971).) + + eps = mp.exp(0.8 * mp.log(mp.eps)) + + a = [[22, 10, 2, 3, 7], + [14, 7, 10, 0, 8], + [-1, 13, -1, -11, 3], + [-3, -2, 13, -2, 4], + [ 9, 8, 1, -2, 4], + [ 9, 1, -7, 5, -1], + [ 2, -6, 6, 5, 1], + [ 4, 5, 0, -2, 2]] + + a = mp.matrix(a) + b = mp.matrix([mp.sqrt(1248), 20, mp.sqrt(384), 0, 0]) + + S = mp.svd_r(a, compute_uv = False) + S -= b + assert mp.mnorm(S) < eps + + S = mp.svd_c(a, compute_uv = False) + S -= b + assert mp.mnorm(S) < eps + + +def test_gauss_quadrature_static(): + a = [-0.57735027, 0.57735027] + b = [ 1, 1] + run_gauss("legendre", a , b) + + a = [ -0.906179846, -0.538469310, 0, 0.538469310, 0.906179846] + b = [ 0.23692689, 0.47862867, 0.56888889, 0.47862867, 0.23692689] + run_gauss("legendre", a , b) + + a = [ 0.06943184, 0.33000948, 0.66999052, 0.93056816] + b = [ 0.17392742, 0.32607258, 0.32607258, 0.17392742] + run_gauss("legendre01", a , b) + + a = [-0.70710678, 0.70710678] + b = [ 0.88622693, 0.88622693] + run_gauss("hermite", a , b) + + a = [ -2.02018287, -0.958572465, 0, 0.958572465, 2.02018287] + b = [ 0.01995324, 0.39361932, 0.94530872, 0.39361932, 0.01995324] + run_gauss("hermite", a , b) + + a = [ 0.41577456, 2.29428036, 6.28994508] + b = [ 0.71109301, 0.27851773, 0.01038926] + run_gauss("laguerre", a , b) + +def test_gauss_quadrature_dynamic(verbose = False): + n = 5 + + A = mp.randmatrix(2 * n, 1) + + def F(x): + r = 0 + for i in xrange(len(A) - 1, -1, -1): + r = r * x + A[i] + return r + + def run(qtype, FW, R, alpha = 0, beta = 0): + X, W = mp.gauss_quadrature(n, qtype, alpha = alpha, beta = beta) + + a = 0 + for i in xrange(len(X)): + a += W[i] * F(X[i]) + + b = mp.quad(lambda x: FW(x) * F(x), R) + + c = mp.fabs(a - b) + + if verbose: + print(qtype, c, a, b) + + assert c < 1e-5 + + run("legendre", lambda x: 1, [-1, 1]) + run("legendre01", lambda x: 1, [0, 1]) + run("hermite", lambda x: mp.exp(-x*x), [-mp.inf, mp.inf]) + run("laguerre", lambda x: mp.exp(-x), [0, mp.inf]) + run("glaguerre", lambda x: mp.sqrt(x)*mp.exp(-x), [0, mp.inf], alpha = 1 / mp.mpf(2)) + run("chebyshev1", lambda x: 1/mp.sqrt(1-x*x), [-1, 1]) + run("chebyshev2", lambda x: mp.sqrt(1-x*x), [-1, 1]) + run("jacobi", lambda x: (1-x)**(1/mp.mpf(3)) * (1+x)**(1/mp.mpf(5)), [-1, 1], alpha = 1 / mp.mpf(3), beta = 1 / mp.mpf(5) ) diff --git a/lib/python3.12/site-packages/mpmath/tests/test_elliptic.py b/lib/python3.12/site-packages/mpmath/tests/test_elliptic.py new file mode 100644 index 0000000000000000000000000000000000000000..4dddc2df34b8d2fa7f2028b3501e5b7f140d8912 --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/tests/test_elliptic.py @@ -0,0 +1,670 @@ +""" +Limited tests of the elliptic functions module. A full suite of +extensive testing can be found in elliptic_torture_tests.py + +Author of the first version: M.T. Taschuk + +References: + +[1] Abramowitz & Stegun. 'Handbook of Mathematical Functions, 9th Ed.', + (Dover duplicate of 1972 edition) +[2] Whittaker 'A Course of Modern Analysis, 4th Ed.', 1946, + Cambridge University Press + +""" + +import mpmath +import random +import pytest + +from mpmath import * + +def mpc_ae(a, b, eps=eps): + res = True + res = res and a.real.ae(b.real, eps) + res = res and a.imag.ae(b.imag, eps) + return res + +zero = mpf(0) +one = mpf(1) + +jsn = ellipfun('sn') +jcn = ellipfun('cn') +jdn = ellipfun('dn') + +calculate_nome = lambda k: qfrom(k=k) + +def test_ellipfun(): + mp.dps = 15 + assert ellipfun('ss', 0, 0) == 1 + assert ellipfun('cc', 0, 0) == 1 + assert ellipfun('dd', 0, 0) == 1 + assert ellipfun('nn', 0, 0) == 1 + assert ellipfun('sn', 0.25, 0).ae(sin(0.25)) + assert ellipfun('cn', 0.25, 0).ae(cos(0.25)) + assert ellipfun('dn', 0.25, 0).ae(1) + assert ellipfun('ns', 0.25, 0).ae(csc(0.25)) + assert ellipfun('nc', 0.25, 0).ae(sec(0.25)) + assert ellipfun('nd', 0.25, 0).ae(1) + assert ellipfun('sc', 0.25, 0).ae(tan(0.25)) + assert ellipfun('sd', 0.25, 0).ae(sin(0.25)) + assert ellipfun('cd', 0.25, 0).ae(cos(0.25)) + assert ellipfun('cs', 0.25, 0).ae(cot(0.25)) + assert ellipfun('dc', 0.25, 0).ae(sec(0.25)) + assert ellipfun('ds', 0.25, 0).ae(csc(0.25)) + assert ellipfun('sn', 0.25, 1).ae(tanh(0.25)) + assert ellipfun('cn', 0.25, 1).ae(sech(0.25)) + assert ellipfun('dn', 0.25, 1).ae(sech(0.25)) + assert ellipfun('ns', 0.25, 1).ae(coth(0.25)) + assert ellipfun('nc', 0.25, 1).ae(cosh(0.25)) + assert ellipfun('nd', 0.25, 1).ae(cosh(0.25)) + assert ellipfun('sc', 0.25, 1).ae(sinh(0.25)) + assert ellipfun('sd', 0.25, 1).ae(sinh(0.25)) + assert ellipfun('cd', 0.25, 1).ae(1) + assert ellipfun('cs', 0.25, 1).ae(csch(0.25)) + assert ellipfun('dc', 0.25, 1).ae(1) + assert ellipfun('ds', 0.25, 1).ae(csch(0.25)) + assert ellipfun('sn', 0.25, 0.5).ae(0.24615967096986145833) + assert ellipfun('cn', 0.25, 0.5).ae(0.96922928989378439337) + assert ellipfun('dn', 0.25, 0.5).ae(0.98473484156599474563) + assert ellipfun('ns', 0.25, 0.5).ae(4.0624038700573130369) + assert ellipfun('nc', 0.25, 0.5).ae(1.0317476065024692949) + assert ellipfun('nd', 0.25, 0.5).ae(1.0155017958029488665) + assert ellipfun('sc', 0.25, 0.5).ae(0.25397465134058993408) + assert ellipfun('sd', 0.25, 0.5).ae(0.24997558792415733063) + assert ellipfun('cd', 0.25, 0.5).ae(0.98425408443195497052) + assert ellipfun('cs', 0.25, 0.5).ae(3.9374008182374110826) + assert ellipfun('dc', 0.25, 0.5).ae(1.0159978158253033913) + assert ellipfun('ds', 0.25, 0.5).ae(4.0003906313579720593) + + + + +def test_calculate_nome(): + mp.dps = 100 + + q = calculate_nome(zero) + assert(q == zero) + + mp.dps = 25 + # used Mathematica's EllipticNomeQ[m] + math1 = [(mpf(1)/10, mpf('0.006584651553858370274473060')), + (mpf(2)/10, mpf('0.01394285727531826872146409')), + (mpf(3)/10, mpf('0.02227743615715350822901627')), + (mpf(4)/10, mpf('0.03188334731336317755064299')), + (mpf(5)/10, mpf('0.04321391826377224977441774')), + (mpf(6)/10, mpf('0.05702025781460967637754953')), + (mpf(7)/10, mpf('0.07468994353717944761143751')), + (mpf(8)/10, mpf('0.09927369733882489703607378')), + (mpf(9)/10, mpf('0.1401731269542615524091055')), + (mpf(9)/10, mpf('0.1401731269542615524091055'))] + + for i in math1: + m = i[0] + q = calculate_nome(sqrt(m)) + assert q.ae(i[1]) + + mp.dps = 15 + +def test_jtheta(): + mp.dps = 25 + + z = q = zero + for n in range(1,5): + value = jtheta(n, z, q) + assert(value == (n-1)//2) + + for q in [one, mpf(2)]: + for n in range(1,5): + pytest.raises(ValueError, lambda: jtheta(n, z, q)) + + z = one/10 + q = one/11 + + # Mathematical N[EllipticTheta[1, 1/10, 1/11], 25] + res = mpf('0.1069552990104042681962096') + result = jtheta(1, z, q) + assert(result.ae(res)) + + # Mathematica N[EllipticTheta[2, 1/10, 1/11], 25] + res = mpf('1.101385760258855791140606') + result = jtheta(2, z, q) + assert(result.ae(res)) + + # Mathematica N[EllipticTheta[3, 1/10, 1/11], 25] + res = mpf('1.178319743354331061795905') + result = jtheta(3, z, q) + assert(result.ae(res)) + + # Mathematica N[EllipticTheta[4, 1/10, 1/11], 25] + res = mpf('0.8219318954665153577314573') + result = jtheta(4, z, q) + assert(result.ae(res)) + + # test for sin zeros for jtheta(1, z, q) + # test for cos zeros for jtheta(2, z, q) + z1 = pi + z2 = pi/2 + for i in range(10): + qstring = str(random.random()) + q = mpf(qstring) + result = jtheta(1, z1, q) + assert(result.ae(0)) + result = jtheta(2, z2, q) + assert(result.ae(0)) + mp.dps = 15 + + +def test_jtheta_issue_79(): + # near the circle of covergence |q| = 1 the convergence slows + # down; for |q| > Q_LIM the theta functions raise ValueError + mp.dps = 30 + mp.dps += 30 + q = mpf(6)/10 - one/10**6 - mpf(8)/10 * j + mp.dps -= 30 + # Mathematica run first + # N[EllipticTheta[3, 1, 6/10 - 10^-6 - 8/10*I], 2000] + # then it works: + # N[EllipticTheta[3, 1, 6/10 - 10^-6 - 8/10*I], 30] + res = mpf('32.0031009628901652627099524264') + \ + mpf('16.6153027998236087899308935624') * j + result = jtheta(3, 1, q) + # check that for abs(q) > Q_LIM a ValueError exception is raised + mp.dps += 30 + q = mpf(6)/10 - one/10**7 - mpf(8)/10 * j + mp.dps -= 30 + pytest.raises(ValueError, lambda: jtheta(3, 1, q)) + + # bug reported in issue 79 + mp.dps = 100 + z = (1+j)/3 + q = mpf(368983957219251)/10**15 + mpf(636363636363636)/10**15 * j + # Mathematica N[EllipticTheta[1, z, q], 35] + res = mpf('2.4439389177990737589761828991467471') + \ + mpf('0.5446453005688226915290954851851490') *j + mp.dps = 30 + result = jtheta(1, z, q) + assert(result.ae(res)) + mp.dps = 80 + z = 3 + 4*j + q = 0.5 + 0.5*j + r1 = jtheta(1, z, q) + mp.dps = 15 + r2 = jtheta(1, z, q) + assert r1.ae(r2) + mp.dps = 80 + z = 3 + j + q1 = exp(j*3) + # longer test + # for n in range(1, 6) + for n in range(1, 2): + mp.dps = 80 + q = q1*(1 - mpf(1)/10**n) + r1 = jtheta(1, z, q) + mp.dps = 15 + r2 = jtheta(1, z, q) + assert r1.ae(r2) + mp.dps = 15 + # issue 79 about high derivatives + assert jtheta(3, 4.5, 0.25, 9).ae(1359.04892680683) + assert jtheta(3, 4.5, 0.25, 50).ae(-6.14832772630905e+33) + mp.dps = 50 + r = jtheta(3, 4.5, 0.25, 9) + assert r.ae('1359.048926806828939547859396600218966947753213803') + r = jtheta(3, 4.5, 0.25, 50) + assert r.ae('-6148327726309051673317975084654262.4119215720343656') + +def test_jtheta_identities(): + """ + Tests the some of the jacobi identidies found in Abramowitz, + Sec. 16.28, Pg. 576. The identities are tested to 1 part in 10^98. + """ + mp.dps = 110 + eps1 = ldexp(eps, 30) + + for i in range(10): + qstring = str(random.random()) + q = mpf(qstring) + + zstring = str(10*random.random()) + z = mpf(zstring) + # Abramowitz 16.28.1 + # v_1(z, q)**2 * v_4(0, q)**2 = v_3(z, q)**2 * v_2(0, q)**2 + # - v_2(z, q)**2 * v_3(0, q)**2 + term1 = (jtheta(1, z, q)**2) * (jtheta(4, zero, q)**2) + term2 = (jtheta(3, z, q)**2) * (jtheta(2, zero, q)**2) + term3 = (jtheta(2, z, q)**2) * (jtheta(3, zero, q)**2) + equality = term1 - term2 + term3 + assert(equality.ae(0, eps1)) + + zstring = str(100*random.random()) + z = mpf(zstring) + # Abramowitz 16.28.2 + # v_2(z, q)**2 * v_4(0, q)**2 = v_4(z, q)**2 * v_2(0, q)**2 + # - v_1(z, q)**2 * v_3(0, q)**2 + term1 = (jtheta(2, z, q)**2) * (jtheta(4, zero, q)**2) + term2 = (jtheta(4, z, q)**2) * (jtheta(2, zero, q)**2) + term3 = (jtheta(1, z, q)**2) * (jtheta(3, zero, q)**2) + equality = term1 - term2 + term3 + assert(equality.ae(0, eps1)) + + # Abramowitz 16.28.3 + # v_3(z, q)**2 * v_4(0, q)**2 = v_4(z, q)**2 * v_3(0, q)**2 + # - v_1(z, q)**2 * v_2(0, q)**2 + term1 = (jtheta(3, z, q)**2) * (jtheta(4, zero, q)**2) + term2 = (jtheta(4, z, q)**2) * (jtheta(3, zero, q)**2) + term3 = (jtheta(1, z, q)**2) * (jtheta(2, zero, q)**2) + equality = term1 - term2 + term3 + assert(equality.ae(0, eps1)) + + # Abramowitz 16.28.4 + # v_4(z, q)**2 * v_4(0, q)**2 = v_3(z, q)**2 * v_3(0, q)**2 + # - v_2(z, q)**2 * v_2(0, q)**2 + term1 = (jtheta(4, z, q)**2) * (jtheta(4, zero, q)**2) + term2 = (jtheta(3, z, q)**2) * (jtheta(3, zero, q)**2) + term3 = (jtheta(2, z, q)**2) * (jtheta(2, zero, q)**2) + equality = term1 - term2 + term3 + assert(equality.ae(0, eps1)) + + # Abramowitz 16.28.5 + # v_2(0, q)**4 + v_4(0, q)**4 == v_3(0, q)**4 + term1 = (jtheta(2, zero, q))**4 + term2 = (jtheta(4, zero, q))**4 + term3 = (jtheta(3, zero, q))**4 + equality = term1 + term2 - term3 + assert(equality.ae(0, eps1)) + mp.dps = 15 + +def test_jtheta_complex(): + mp.dps = 30 + z = mpf(1)/4 + j/8 + q = mpf(1)/3 + j/7 + # Mathematica N[EllipticTheta[1, 1/4 + I/8, 1/3 + I/7], 35] + res = mpf('0.31618034835986160705729105731678285') + \ + mpf('0.07542013825835103435142515194358975') * j + r = jtheta(1, z, q) + assert(mpc_ae(r, res)) + + # Mathematica N[EllipticTheta[2, 1/4 + I/8, 1/3 + I/7], 35] + res = mpf('1.6530986428239765928634711417951828') + \ + mpf('0.2015344864707197230526742145361455') * j + r = jtheta(2, z, q) + assert(mpc_ae(r, res)) + + # Mathematica N[EllipticTheta[3, 1/4 + I/8, 1/3 + I/7], 35] + res = mpf('1.6520564411784228184326012700348340') + \ + mpf('0.1998129119671271328684690067401823') * j + r = jtheta(3, z, q) + assert(mpc_ae(r, res)) + + # Mathematica N[EllipticTheta[4, 1/4 + I/8, 1/3 + I/7], 35] + res = mpf('0.37619082382228348252047624089973824') - \ + mpf('0.15623022130983652972686227200681074') * j + r = jtheta(4, z, q) + assert(mpc_ae(r, res)) + + # check some theta function identities + mp.dos = 100 + z = mpf(1)/4 + j/8 + q = mpf(1)/3 + j/7 + mp.dps += 10 + a = [0,0, jtheta(2, 0, q), jtheta(3, 0, q), jtheta(4, 0, q)] + t = [0, jtheta(1, z, q), jtheta(2, z, q), jtheta(3, z, q), jtheta(4, z, q)] + r = [(t[2]*a[4])**2 - (t[4]*a[2])**2 + (t[1] *a[3])**2, + (t[3]*a[4])**2 - (t[4]*a[3])**2 + (t[1] *a[2])**2, + (t[1]*a[4])**2 - (t[3]*a[2])**2 + (t[2] *a[3])**2, + (t[4]*a[4])**2 - (t[3]*a[3])**2 + (t[2] *a[2])**2, + a[2]**4 + a[4]**4 - a[3]**4] + mp.dps -= 10 + for x in r: + assert(mpc_ae(x, mpc(0))) + mp.dps = 15 + +def test_djtheta(): + mp.dps = 30 + + z = one/7 + j/3 + q = one/8 + j/5 + # Mathematica N[EllipticThetaPrime[1, 1/7 + I/3, 1/8 + I/5], 35] + res = mpf('1.5555195883277196036090928995803201') - \ + mpf('0.02439761276895463494054149673076275') * j + result = jtheta(1, z, q, 1) + assert(mpc_ae(result, res)) + + # Mathematica N[EllipticThetaPrime[2, 1/7 + I/3, 1/8 + I/5], 35] + res = mpf('0.19825296689470982332701283509685662') - \ + mpf('0.46038135182282106983251742935250009') * j + result = jtheta(2, z, q, 1) + assert(mpc_ae(result, res)) + + # Mathematica N[EllipticThetaPrime[3, 1/7 + I/3, 1/8 + I/5], 35] + res = mpf('0.36492498415476212680896699407390026') - \ + mpf('0.57743812698666990209897034525640369') * j + result = jtheta(3, z, q, 1) + assert(mpc_ae(result, res)) + + # Mathematica N[EllipticThetaPrime[4, 1/7 + I/3, 1/8 + I/5], 35] + res = mpf('-0.38936892528126996010818803742007352') + \ + mpf('0.66549886179739128256269617407313625') * j + result = jtheta(4, z, q, 1) + assert(mpc_ae(result, res)) + + for i in range(10): + q = (one*random.random() + j*random.random())/2 + # identity in Wittaker, Watson &21.41 + a = jtheta(1, 0, q, 1) + b = jtheta(2, 0, q)*jtheta(3, 0, q)*jtheta(4, 0, q) + assert(a.ae(b)) + + # test higher derivatives + mp.dps = 20 + for q,z in [(one/3, one/5), (one/3 + j/8, one/5), + (one/3, one/5 + j/8), (one/3 + j/7, one/5 + j/8)]: + for n in [1, 2, 3, 4]: + r = jtheta(n, z, q, 2) + r1 = diff(lambda zz: jtheta(n, zz, q), z, n=2) + assert r.ae(r1) + r = jtheta(n, z, q, 3) + r1 = diff(lambda zz: jtheta(n, zz, q), z, n=3) + assert r.ae(r1) + + # identity in Wittaker, Watson &21.41 + q = one/3 + z = zero + a = [0]*5 + a[1] = jtheta(1, z, q, 3)/jtheta(1, z, q, 1) + for n in [2,3,4]: + a[n] = jtheta(n, z, q, 2)/jtheta(n, z, q) + equality = a[2] + a[3] + a[4] - a[1] + assert(equality.ae(0)) + mp.dps = 15 + +def test_jsn(): + """ + Test some special cases of the sn(z, q) function. + """ + mp.dps = 100 + + # trival case + result = jsn(zero, zero) + assert(result == zero) + + # Abramowitz Table 16.5 + # + # sn(0, m) = 0 + + for i in range(10): + qstring = str(random.random()) + q = mpf(qstring) + + equality = jsn(zero, q) + assert(equality.ae(0)) + + # Abramowitz Table 16.6.1 + # + # sn(z, 0) = sin(z), m == 0 + # + # sn(z, 1) = tanh(z), m == 1 + # + # It would be nice to test these, but I find that they run + # in to numerical trouble. I'm currently treating as a boundary + # case for sn function. + + mp.dps = 25 + arg = one/10 + #N[JacobiSN[1/10, 2^-100], 25] + res = mpf('0.09983341664682815230681420') + m = ldexp(one, -100) + result = jsn(arg, m) + assert(result.ae(res)) + + # N[JacobiSN[1/10, 1/10], 25] + res = mpf('0.09981686718599080096451168') + result = jsn(arg, arg) + assert(result.ae(res)) + mp.dps = 15 + +def test_jcn(): + """ + Test some special cases of the cn(z, q) function. + """ + mp.dps = 100 + + # Abramowitz Table 16.5 + # cn(0, q) = 1 + qstring = str(random.random()) + q = mpf(qstring) + cn = jcn(zero, q) + assert(cn.ae(one)) + + # Abramowitz Table 16.6.2 + # + # cn(u, 0) = cos(u), m == 0 + # + # cn(u, 1) = sech(z), m == 1 + # + # It would be nice to test these, but I find that they run + # in to numerical trouble. I'm currently treating as a boundary + # case for cn function. + + mp.dps = 25 + arg = one/10 + m = ldexp(one, -100) + #N[JacobiCN[1/10, 2^-100], 25] + res = mpf('0.9950041652780257660955620') + result = jcn(arg, m) + assert(result.ae(res)) + + # N[JacobiCN[1/10, 1/10], 25] + res = mpf('0.9950058256237368748520459') + result = jcn(arg, arg) + assert(result.ae(res)) + mp.dps = 15 + +def test_jdn(): + """ + Test some special cases of the dn(z, q) function. + """ + mp.dps = 100 + + # Abramowitz Table 16.5 + # dn(0, q) = 1 + mstring = str(random.random()) + m = mpf(mstring) + + dn = jdn(zero, m) + assert(dn.ae(one)) + + mp.dps = 25 + # N[JacobiDN[1/10, 1/10], 25] + res = mpf('0.9995017055025556219713297') + arg = one/10 + result = jdn(arg, arg) + assert(result.ae(res)) + mp.dps = 15 + + +def test_sn_cn_dn_identities(): + """ + Tests the some of the jacobi elliptic function identities found + on Mathworld. Haven't found in Abramowitz. + """ + mp.dps = 100 + N = 5 + for i in range(N): + qstring = str(random.random()) + q = mpf(qstring) + zstring = str(100*random.random()) + z = mpf(zstring) + + # MathWorld + # sn(z, q)**2 + cn(z, q)**2 == 1 + term1 = jsn(z, q)**2 + term2 = jcn(z, q)**2 + equality = one - term1 - term2 + assert(equality.ae(0)) + + # MathWorld + # k**2 * sn(z, m)**2 + dn(z, m)**2 == 1 + for i in range(N): + mstring = str(random.random()) + m = mpf(qstring) + k = m.sqrt() + zstring = str(10*random.random()) + z = mpf(zstring) + term1 = k**2 * jsn(z, m)**2 + term2 = jdn(z, m)**2 + equality = one - term1 - term2 + assert(equality.ae(0)) + + + for i in range(N): + mstring = str(random.random()) + m = mpf(mstring) + k = m.sqrt() + zstring = str(random.random()) + z = mpf(zstring) + + # MathWorld + # k**2 * cn(z, m)**2 + (1 - k**2) = dn(z, m)**2 + term1 = k**2 * jcn(z, m)**2 + term2 = 1 - k**2 + term3 = jdn(z, m)**2 + equality = term3 - term1 - term2 + assert(equality.ae(0)) + + K = ellipk(k**2) + # Abramowitz Table 16.5 + # sn(K, m) = 1; K is K(k), first complete elliptic integral + r = jsn(K, m) + assert(r.ae(one)) + + # Abramowitz Table 16.5 + # cn(K, q) = 0; K is K(k), first complete elliptic integral + equality = jcn(K, m) + assert(equality.ae(0)) + + # Abramowitz Table 16.6.3 + # dn(z, 0) = 1, m == 0 + z = m + value = jdn(z, zero) + assert(value.ae(one)) + + mp.dps = 15 + +def test_sn_cn_dn_complex(): + mp.dps = 30 + # N[JacobiSN[1/4 + I/8, 1/3 + I/7], 35] in Mathematica + res = mpf('0.2495674401066275492326652143537') + \ + mpf('0.12017344422863833381301051702823') * j + u = mpf(1)/4 + j/8 + m = mpf(1)/3 + j/7 + r = jsn(u, m) + assert(mpc_ae(r, res)) + + #N[JacobiCN[1/4 + I/8, 1/3 + I/7], 35] + res = mpf('0.9762691700944007312693721148331') - \ + mpf('0.0307203994181623243583169154824')*j + r = jcn(u, m) + #assert r.real.ae(res.real) + #assert r.imag.ae(res.imag) + assert(mpc_ae(r, res)) + + #N[JacobiDN[1/4 + I/8, 1/3 + I/7], 35] + res = mpf('0.99639490163039577560547478589753039') - \ + mpf('0.01346296520008176393432491077244994')*j + r = jdn(u, m) + assert(mpc_ae(r, res)) + mp.dps = 15 + +def test_elliptic_integrals(): + # Test cases from Carlson's paper + mp.dps = 15 + assert elliprd(0,2,1).ae(1.7972103521033883112) + assert elliprd(2,3,4).ae(0.16510527294261053349) + assert elliprd(j,-j,2).ae(0.65933854154219768919) + assert elliprd(0,j,-j).ae(1.2708196271909686299 + 2.7811120159520578777j) + assert elliprd(0,j-1,j).ae(-1.8577235439239060056 - 0.96193450888838559989j) + assert elliprd(-2-j,-j,-1+j).ae(1.8249027393703805305 - 1.2218475784827035855j) + # extra test cases + assert elliprg(0,0,0) == 0 + assert elliprg(0,0,16).ae(2) + assert elliprg(0,16,0).ae(2) + assert elliprg(16,0,0).ae(2) + assert elliprg(1,4,0).ae(1.2110560275684595248036) + assert elliprg(1,0,4).ae(1.2110560275684595248036) + assert elliprg(0,4,1).ae(1.2110560275684595248036) + # should be symmetric -- fixes a bug present in the paper + x,y,z = 1,1j,-1+1j + assert elliprg(x,y,z).ae(0.64139146875812627545 + 0.58085463774808290907j) + assert elliprg(x,z,y).ae(0.64139146875812627545 + 0.58085463774808290907j) + assert elliprg(y,x,z).ae(0.64139146875812627545 + 0.58085463774808290907j) + assert elliprg(y,z,x).ae(0.64139146875812627545 + 0.58085463774808290907j) + assert elliprg(z,x,y).ae(0.64139146875812627545 + 0.58085463774808290907j) + assert elliprg(z,y,x).ae(0.64139146875812627545 + 0.58085463774808290907j) + + for n in [5, 15, 30, 60, 100]: + mp.dps = n + assert elliprf(1,2,0).ae('1.3110287771460599052324197949455597068413774757158115814084108519003952935352071251151477664807145467230678763') + assert elliprf(0.5,1,0).ae('1.854074677301371918433850347195260046217598823521766905585928045056021776838119978357271861650371897277771871') + assert elliprf(j,-j,0).ae('1.854074677301371918433850347195260046217598823521766905585928045056021776838119978357271861650371897277771871') + assert elliprf(j-1,j,0).ae(mpc('0.79612586584233913293056938229563057846592264089185680214929401744498956943287031832657642790719940442165621412', + '-1.2138566698364959864300942567386038975419875860741507618279563735753073152507112254567291141460317931258599889')) + assert elliprf(2,3,4).ae('0.58408284167715170669284916892566789240351359699303216166309375305508295130412919665541330837704050454472379308') + assert elliprf(j,-j,2).ae('1.0441445654064360931078658361850779139591660747973017593275012615517220315993723776182276555339288363064476126') + assert elliprf(j-1,j,1-j).ae(mpc('0.93912050218619371196624617169781141161485651998254431830645241993282941057500174238125105410055253623847335313', + '-0.53296252018635269264859303449447908970360344322834582313172115220559316331271520508208025270300138589669326136')) + assert elliprc(0,0.25).ae(+pi) + assert elliprc(2.25,2).ae(+ln2) + assert elliprc(0,j).ae(mpc('1.1107207345395915617539702475151734246536554223439225557713489017391086982748684776438317336911913093408525532', + '-1.1107207345395915617539702475151734246536554223439225557713489017391086982748684776438317336911913093408525532')) + assert elliprc(-j,j).ae(mpc('1.2260849569072198222319655083097718755633725139745941606203839524036426936825652935738621522906572884239069297', + '-0.34471136988767679699935618332997956653521218571295874986708834375026550946053920574015526038040124556716711353')) + assert elliprc(0.25,-2).ae(ln2/3) + assert elliprc(j,-1).ae(mpc('0.77778596920447389875196055840799837589537035343923012237628610795937014001905822029050288316217145443865649819', + '0.1983248499342877364755170948292130095921681309577950696116251029742793455964385947473103628983664877025779304')) + assert elliprj(0,1,2,3).ae('0.77688623778582332014190282640545501102298064276022952731669118325952563819813258230708177398475643634103990878') + assert elliprj(2,3,4,5).ae('0.14297579667156753833233879421985774801466647854232626336218889885463800128817976132826443904216546421431528308') + assert elliprj(2,3,4,-1+j).ae(mpc('0.13613945827770535203521374457913768360237593025944342652613569368333226052158214183059386307242563164036672709', + '-0.38207561624427164249600936454845112611060375760094156571007648297226090050927156176977091273224510621553615189')) + assert elliprj(j,-j,0,2).ae('1.6490011662710884518243257224860232300246792717163891216346170272567376981346412066066050103935109581019055806') + assert elliprj(-1+j,-1-j,1,2).ae('0.94148358841220238083044612133767270187474673547917988681610772381758628963408843935027667916713866133196845063') + assert elliprj(j,-j,0,1-j).ae(mpc('1.8260115229009316249372594065790946657011067182850435297162034335356430755397401849070610280860044610878657501', + '1.2290661908643471500163617732957042849283739403009556715926326841959667290840290081010472716420690899886276961')) + assert elliprj(-1+j,-1-j,1,-3+j).ae(mpc('-0.61127970812028172123588152373622636829986597243716610650831553882054127570542477508023027578037045504958619422', + '-1.0684038390006807880182112972232562745485871763154040245065581157751693730095703406209466903752930797510491155')) + assert elliprj(-1+j,-2-j,-j,-1+j).ae(mpc('1.8249027393703805304622013339009022294368078659619988943515764258335975852685224202567854526307030593012768954', + '-1.2218475784827035854568450371590419833166777535029296025352291308244564398645467465067845461070602841312456831')) + + assert elliprg(0,16,16).ae(+pi) + assert elliprg(2,3,4).ae('1.7255030280692277601061148835701141842692457170470456590515892070736643637303053506944907685301315299153040991') + assert elliprg(0,j,-j).ae('0.42360654239698954330324956174109581824072295516347109253028968632986700241706737986160014699730561497106114281') + assert elliprg(j-1,j,0).ae(mpc('0.44660591677018372656731970402124510811555212083508861036067729944477855594654762496407405328607219895053798354', + '0.70768352357515390073102719507612395221369717586839400605901402910893345301718731499237159587077682267374159282')) + assert elliprg(-j,j-1,j).ae(mpc('0.36023392184473309033675652092928695596803358846377334894215349632203382573844427952830064383286995172598964266', + '0.40348623401722113740956336997761033878615232917480045914551915169013722542827052849476969199578321834819903921')) + assert elliprg(0, mpf('0.0796'), 4).ae('1.0284758090288040009838871385180217366569777284430590125081211090574701293154645750017813190805144572673802094') + mp.dps = 15 + + # more test cases for the branch of ellippi / elliprj + assert elliprj(-1-0.5j, -10-6j, -10-3j, -5+10j).ae(0.128470516743927699 + 0.102175950778504625j, abs_eps=1e-8) + assert elliprj(1.987, 4.463 - 1.614j, 0, -3.965).ae(-0.341575118513811305 - 0.394703757004268486j, abs_eps=1e-8) + assert elliprj(0.3068, -4.037+0.632j, 1.654, -0.9609).ae(-1.14735199581485639 - 0.134450158867472264j, abs_eps=1e-8) + assert elliprj(0.3068, -4.037-0.632j, 1.654, -0.9609).ae(1.758765901861727 - 0.161002343366626892j, abs_eps=1e-5) + assert elliprj(0.3068, -4.037+0.0632j, 1.654, -0.9609).ae(-1.17157627949475577 - 0.069182614173988811j, abs_eps=1e-8) + assert elliprj(0.3068, -4.037+0.00632j, 1.654, -0.9609).ae(-1.17337595670549633 - 0.0623069224526925j, abs_eps=1e-8) + + # these require accurate integration + assert elliprj(0.3068, -4.037-0.0632j, 1.654, -0.9609).ae(1.77940452391261626 + 0.0388711305592447234j) + assert elliprj(0.3068, -4.037-0.00632j, 1.654, -0.9609).ae(1.77806722756403055 + 0.0592749824572262329j) + # issue #571 + assert ellippi(2.1 + 0.94j, 2.3 + 0.98j, 2.5 + 0.01j).ae(-0.40652414240811963438 + 2.1547659461404749309j) + + assert ellippi(2.0-1.0j, 2.0+1.0j).ae(1.8578723151271115 - 1.18642180609983531j) + assert ellippi(2.0-0.5j, 0.5+1.0j).ae(0.936761970766645807 - 1.61876787838890786j) + assert ellippi(2.0, 1.0+1.0j).ae(0.999881420735506708 - 2.4139272867045391j) + assert ellippi(2.0+1.0j, 2.0-1.0j).ae(1.8578723151271115 + 1.18642180609983531j) + assert ellippi(2.0+1.0j, 2.0).ae(2.78474654927885845 + 2.02204728966993314j) + +def test_issue_238(): + assert isnan(qfrom(m=nan)) diff --git a/lib/python3.12/site-packages/mpmath/tests/test_fp.py b/lib/python3.12/site-packages/mpmath/tests/test_fp.py new file mode 100644 index 0000000000000000000000000000000000000000..99f3759c3071c4d55e0481472f3d16c1f5df1fef --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/tests/test_fp.py @@ -0,0 +1,1671 @@ +""" +Easy-to-use test-generating code: + +cases = ''' +exp 2.25 +log 2.25 +''' + +from mpmath import * +mp.dps = 20 +for test in cases.splitlines(): + if not test: + continue + words = test.split() + fname = words[0] + args = words[1:] + argstr = ", ".join(args) + testline = "%s(%s)" % (fname, argstr) + ans = str(eval(testline)) + print " assert ae(fp.%s, %s)" % (testline, ans) + +""" + +from mpmath import fp + +def ae(x, y, tol=1e-12): + if x == y: + return True + return abs(x-y) <= tol*abs(y) + +def test_conj(): + assert fp.conj(4) == 4 + assert fp.conj(3+4j) == 3-4j + assert fp.fdot([1,2],[3,2+1j], conjugate=True) == 7-2j + +def test_fp_number_parts(): + assert ae(fp.arg(3), 0.0) + assert ae(fp.arg(-3), 3.1415926535897932385) + assert ae(fp.arg(3j), 1.5707963267948966192) + assert ae(fp.arg(-3j), -1.5707963267948966192) + assert ae(fp.arg(2+3j), 0.98279372324732906799) + assert ae(fp.arg(-1-1j), -2.3561944901923449288) + assert ae(fp.re(2.5), 2.5) + assert ae(fp.re(2.5+3j), 2.5) + assert ae(fp.im(2.5), 0.0) + assert ae(fp.im(2.5+3j), 3.0) + assert ae(fp.floor(2.5), 2.0) + assert ae(fp.floor(2), 2.0) + assert ae(fp.floor(2.0+0j), (2.0 + 0.0j)) + assert ae(fp.floor(-1.5-0.5j), (-2.0 - 1.0j)) + assert ae(fp.ceil(2.5), 3.0) + assert ae(fp.ceil(2), 2.0) + assert ae(fp.ceil(2.0+0j), (2.0 + 0.0j)) + assert ae(fp.ceil(-1.5-0.5j), (-1.0 + 0.0j)) + +def test_fp_cospi_sinpi(): + assert ae(fp.sinpi(0), 0.0) + assert ae(fp.sinpi(0.25), 0.7071067811865475244) + assert ae(fp.sinpi(0.5), 1.0) + assert ae(fp.sinpi(0.75), 0.7071067811865475244) + assert ae(fp.sinpi(1), 0.0) + assert ae(fp.sinpi(1.25), -0.7071067811865475244) + assert ae(fp.sinpi(1.5), -1.0) + assert ae(fp.sinpi(1.75), -0.7071067811865475244) + assert ae(fp.sinpi(2), 0.0) + assert ae(fp.sinpi(2.25), 0.7071067811865475244) + assert ae(fp.sinpi(0+3j), (0.0 + 6195.8238636085899556j)) + assert ae(fp.sinpi(0.25+3j), (4381.1091260582448033 + 4381.1090689950686908j)) + assert ae(fp.sinpi(0.5+3j), (6195.8239443081075259 + 0.0j)) + assert ae(fp.sinpi(0.75+3j), (4381.1091260582448033 - 4381.1090689950686908j)) + assert ae(fp.sinpi(1+3j), (0.0 - 6195.8238636085899556j)) + assert ae(fp.sinpi(1.25+3j), (-4381.1091260582448033 - 4381.1090689950686908j)) + assert ae(fp.sinpi(1.5+3j), (-6195.8239443081075259 + 0.0j)) + assert ae(fp.sinpi(1.75+3j), (-4381.1091260582448033 + 4381.1090689950686908j)) + assert ae(fp.sinpi(2+3j), (0.0 + 6195.8238636085899556j)) + assert ae(fp.sinpi(2.25+3j), (4381.1091260582448033 + 4381.1090689950686908j)) + assert ae(fp.sinpi(-0.75), -0.7071067811865475244) + assert ae(fp.sinpi(-1e-10), -3.1415926535897933529e-10) + assert ae(fp.sinpi(1e-10), 3.1415926535897933529e-10) + assert ae(fp.sinpi(1e-10+1e-10j), (3.141592653589793353e-10 + 3.1415926535897933528e-10j)) + assert ae(fp.sinpi(1e-10-1e-10j), (3.141592653589793353e-10 - 3.1415926535897933528e-10j)) + assert ae(fp.sinpi(-1e-10+1e-10j), (-3.141592653589793353e-10 + 3.1415926535897933528e-10j)) + assert ae(fp.sinpi(-1e-10-1e-10j), (-3.141592653589793353e-10 - 3.1415926535897933528e-10j)) + assert ae(fp.cospi(0), 1.0) + assert ae(fp.cospi(0.25), 0.7071067811865475244) + assert ae(fp.cospi(0.5), 0.0) + assert ae(fp.cospi(0.75), -0.7071067811865475244) + assert ae(fp.cospi(1), -1.0) + assert ae(fp.cospi(1.25), -0.7071067811865475244) + assert ae(fp.cospi(1.5), 0.0) + assert ae(fp.cospi(1.75), 0.7071067811865475244) + assert ae(fp.cospi(2), 1.0) + assert ae(fp.cospi(2.25), 0.7071067811865475244) + assert ae(fp.cospi(0+3j), (6195.8239443081075259 + 0.0j)) + assert ae(fp.cospi(0.25+3j), (4381.1091260582448033 - 4381.1090689950686908j)) + assert ae(fp.cospi(0.5+3j), (0.0 - 6195.8238636085899556j)) + assert ae(fp.cospi(0.75+3j), (-4381.1091260582448033 - 4381.1090689950686908j)) + assert ae(fp.cospi(1+3j), (-6195.8239443081075259 + 0.0j)) + assert ae(fp.cospi(1.25+3j), (-4381.1091260582448033 + 4381.1090689950686908j)) + assert ae(fp.cospi(1.5+3j), (0.0 + 6195.8238636085899556j)) + assert ae(fp.cospi(1.75+3j), (4381.1091260582448033 + 4381.1090689950686908j)) + assert ae(fp.cospi(2+3j), (6195.8239443081075259 + 0.0j)) + assert ae(fp.cospi(2.25+3j), (4381.1091260582448033 - 4381.1090689950686908j)) + assert ae(fp.cospi(-0.75), -0.7071067811865475244) + assert ae(fp.sinpi(-0.7), -0.80901699437494750611) + assert ae(fp.cospi(-0.7), -0.5877852522924730163) + assert ae(fp.cospi(-3+2j), (-267.74676148374822225 + 0.0j)) + assert ae(fp.sinpi(-3+2j), (0.0 - 267.74489404101651426j)) + assert ae(fp.sinpi(-0.7+2j), (-216.6116802292079471 - 157.37650009392034693j)) + assert ae(fp.cospi(-0.7+2j), (-157.37759774921754565 + 216.61016943630197336j)) + +def test_fp_expj(): + assert ae(fp.expj(0), (1.0 + 0.0j)) + assert ae(fp.expj(1), (0.5403023058681397174 + 0.84147098480789650665j)) + assert ae(fp.expj(2), (-0.416146836547142387 + 0.9092974268256816954j)) + assert ae(fp.expj(0.75), (0.73168886887382088631 + 0.68163876002333416673j)) + assert ae(fp.expj(2+3j), (-0.020718731002242879378 + 0.045271253156092975488j)) + assert ae(fp.expjpi(0), (1.0 + 0.0j)) + assert ae(fp.expjpi(1), (-1.0 + 0.0j)) + assert ae(fp.expjpi(2), (1.0 + 0.0j)) + assert ae(fp.expjpi(0.75), (-0.7071067811865475244 + 0.7071067811865475244j)) + assert ae(fp.expjpi(2+3j), (0.000080699517570304599239 + 0.0j)) + +def test_fp_bernoulli(): + assert ae(fp.bernoulli(0), 1.0) + assert ae(fp.bernoulli(1), -0.5) + assert ae(fp.bernoulli(2), 0.16666666666666666667) + assert ae(fp.bernoulli(10), 0.075757575757575757576) + assert ae(fp.bernoulli(11), 0.0) + +def test_fp_gamma(): + assert ae(fp.gamma(1), 1.0) + assert ae(fp.gamma(1.5), 0.88622692545275801365) + assert ae(fp.gamma(10), 362880.0) + assert ae(fp.gamma(-0.5), -3.5449077018110320546) + assert ae(fp.gamma(-7.1), 0.0016478244570263333622) + assert ae(fp.gamma(12.3), 83385367.899970000963) + assert ae(fp.gamma(2+0j), (1.0 + 0.0j)) + assert ae(fp.gamma(-2.5+0j), (-0.94530872048294188123 + 0.0j)) + assert ae(fp.gamma(3+4j), (0.0052255384713692141947 - 0.17254707929430018772j)) + assert ae(fp.gamma(-3-4j), (0.00001460997305874775607 - 0.000020760733311509070396j)) + assert ae(fp.fac(0), 1.0) + assert ae(fp.fac(1), 1.0) + assert ae(fp.fac(20), 2432902008176640000.0) + assert ae(fp.fac(-3.5), -0.94530872048294188123) + assert ae(fp.fac(2+3j), (-0.44011340763700171113 - 0.06363724312631702183j)) + assert ae(fp.loggamma(1.0), 0.0) + assert ae(fp.loggamma(2.0), 0.0) + assert ae(fp.loggamma(3.0), 0.69314718055994530942) + assert ae(fp.loggamma(7.25), 7.0521854507385394449) + assert ae(fp.loggamma(1000.0), 5905.2204232091812118) + assert ae(fp.loggamma(1e50), 1.1412925464970229298e+52) + assert ae(fp.loggamma(1e25+1e25j), (5.6125802751733671621e+26 + 5.7696599078528568383e+26j)) + assert ae(fp.loggamma(3+4j), (-1.7566267846037841105 + 4.7426644380346579282j)) + assert ae(fp.loggamma(-0.5), (1.2655121234846453965 - 3.1415926535897932385j)) + assert ae(fp.loggamma(-1.25), (1.3664317612369762346 - 6.2831853071795864769j)) + assert ae(fp.loggamma(-2.75), (0.0044878975359557733115 - 9.4247779607693797154j)) + assert ae(fp.loggamma(-3.5), (-1.3090066849930420464 - 12.566370614359172954j)) + assert ae(fp.loggamma(-4.5), (-2.8130840817693161197 - 15.707963267948966192j)) + assert ae(fp.loggamma(-2+3j), (-6.776523813485657093 - 4.568791367260286402j)) + assert ae(fp.loggamma(-1000.3), (-5912.8440347785205041 - 3144.7342462433830317j)) + assert ae(fp.loggamma(-100-100j), (-632.35117666833135562 - 158.37641469650352462j)) + assert ae(fp.loggamma(1e-10), 23.025850929882735237) + assert ae(fp.loggamma(-1e-10), (23.02585092999817837 - 3.1415926535897932385j)) + assert ae(fp.loggamma(1e-10j), (23.025850929940456804 - 1.5707963268526181857j)) + assert ae(fp.loggamma(1e-10j-1e-10), (22.679277339718205716 - 2.3561944902500664954j)) + +def test_fp_psi(): + assert ae(fp.psi(0, 3.7), 1.1671535393615114409) + assert ae(fp.psi(0, 0.5), -1.9635100260214234794) + assert ae(fp.psi(0, 1), -0.57721566490153286061) + assert ae(fp.psi(0, -2.5), 1.1031566406452431872) + assert ae(fp.psi(0, 12.9), 2.5179671503279156347) + assert ae(fp.psi(0, 100), 4.6001618527380874002) + assert ae(fp.psi(0, 2500.3), 7.8239660143238547877) + assert ae(fp.psi(0, 1e40), 92.103403719761827391) + assert ae(fp.psi(0, 1e200), 460.51701859880913677) + assert ae(fp.psi(0, 3.7+0j), (1.1671535393615114409 + 0.0j)) + assert ae(fp.psi(1, 3), 0.39493406684822643647) + assert ae(fp.psi(3, 2+3j), (-0.05383196209159972116 + 0.0076890935247364805218j)) + assert ae(fp.psi(4, -0.5+1j), (1.2719531355492328195 - 18.211833410936276774j)) + assert ae(fp.harmonic(0), 0.0) + assert ae(fp.harmonic(1), 1.0) + assert ae(fp.harmonic(2), 1.5) + assert ae(fp.harmonic(100), 5.1873775176396202608) + assert ae(fp.harmonic(-2.5), 1.2803723055467760478) + assert ae(fp.harmonic(2+3j), (1.9390425294578375875 + 0.87336044981834544043j)) + assert ae(fp.harmonic(-5-4j), (2.3725754822349437733 - 2.4160904444801621j)) + +def test_fp_zeta(): + assert ae(fp.zeta(1e100), 1.0) + assert ae(fp.zeta(3), 1.2020569031595942854) + assert ae(fp.zeta(2+0j), (1.6449340668482264365 + 0.0j)) + assert ae(fp.zeta(0.93), -13.713619351638164784) + assert ae(fp.zeta(1.74), 1.9796863545771774095) + assert ae(fp.zeta(0.0), -0.5) + assert ae(fp.zeta(-1.0), -0.083333333333333333333) + assert ae(fp.zeta(-2.0), 0.0) + assert ae(fp.zeta(-3.0), 0.0083333333333333333333) + assert ae(fp.zeta(-500.0), 0.0) + assert ae(fp.zeta(-7.4), 0.0036537321227995882447) + assert ae(fp.zeta(2.1), 1.5602165335033620158) + assert ae(fp.zeta(26.9), 1.0000000079854809935) + assert ae(fp.zeta(26), 1.0000000149015548284) + assert ae(fp.zeta(27), 1.0000000074507117898) + assert ae(fp.zeta(28), 1.0000000037253340248) + assert ae(fp.zeta(27.1), 1.000000006951755045) + assert ae(fp.zeta(32.7), 1.0000000001433243232) + assert ae(fp.zeta(100), 1.0) + assert ae(fp.altzeta(3.5), 0.92755357777394803511) + assert ae(fp.altzeta(1), 0.69314718055994530942) + assert ae(fp.altzeta(2), 0.82246703342411321824) + assert ae(fp.altzeta(0), 0.5) + assert ae(fp.zeta(-2+3j, 1), (0.13297115587929864827 + 0.12305330040458776494j)) + assert ae(fp.zeta(-2+3j, 5), (18.384866151867576927 - 11.377015110597711009j)) + assert ae(fp.zeta(1.0000000001), 9999999173.1735741337) + assert ae(fp.zeta(0.9999999999), -9999999172.0191428039) + assert ae(fp.zeta(1+0.000000001j), (0.57721566490153286061 - 999999999.99999993765j)) + assert ae(fp.primezeta(2.5+4j), (-0.16922458243438033385 - 0.010847965298387727811j)) + assert ae(fp.primezeta(4), 0.076993139764246844943) + assert ae(fp.riemannr(3.7), 2.3034079839110855717) + assert ae(fp.riemannr(8), 3.9011860449341499474) + assert ae(fp.riemannr(3+4j), (2.2369653314259991796 + 1.6339943856990281694j)) + +def test_fp_hyp2f1(): + assert ae(fp.hyp2f1(1, (3,2), 3.25, 5.0), (-0.46600275923108143059 - 0.74393667908854842325j)) + assert ae(fp.hyp2f1(1+1j, (3,2), 3.25, 5.0), (-5.9208875603806515987 - 2.3813557707889590686j)) + assert ae(fp.hyp2f1(1+1j, (3,2), 3.25, 2+3j), (0.17174552030925080445 + 0.19589781970539389999j)) + +def test_fp_erf(): + assert fp.erf(2) == fp.erf(2.0) == fp.erf(2.0+0.0j) + assert fp.erf(fp.inf) == 1.0 + assert fp.erf(fp.ninf) == -1.0 + assert ae(fp.erf(0), 0.0) + assert ae(fp.erf(-0), -0.0) + assert ae(fp.erf(0.3), 0.32862675945912741619) + assert ae(fp.erf(-0.3), -0.32862675945912741619) + assert ae(fp.erf(0.9), 0.79690821242283213966) + assert ae(fp.erf(-0.9), -0.79690821242283213966) + assert ae(fp.erf(1.0), 0.84270079294971486934) + assert ae(fp.erf(-1.0), -0.84270079294971486934) + assert ae(fp.erf(1.1), 0.88020506957408172966) + assert ae(fp.erf(-1.1), -0.88020506957408172966) + assert ae(fp.erf(8.5), 1.0) + assert ae(fp.erf(-8.5), -1.0) + assert ae(fp.erf(9.1), 1.0) + assert ae(fp.erf(-9.1), -1.0) + assert ae(fp.erf(20.0), 1.0) + assert ae(fp.erf(-20.0), -1.0) + assert ae(fp.erf(10000.0), 1.0) + assert ae(fp.erf(-10000.0), -1.0) + assert ae(fp.erf(1e+50), 1.0) + assert ae(fp.erf(-1e+50), -1.0) + assert ae(fp.erf(1j), 1.650425758797542876j) + assert ae(fp.erf(-1j), -1.650425758797542876j) + assert ae(fp.erf((2+3j)), (-20.829461427614568389 + 8.6873182714701631444j)) + assert ae(fp.erf(-(2+3j)), -(-20.829461427614568389 + 8.6873182714701631444j)) + assert ae(fp.erf((8+9j)), (-1072004.2525062051158 + 364149.91954310255423j)) + assert ae(fp.erf(-(8+9j)), -(-1072004.2525062051158 + 364149.91954310255423j)) + assert fp.erfc(fp.inf) == 0.0 + assert fp.erfc(fp.ninf) == 2.0 + assert fp.erfc(0) == 1 + assert fp.erfc(-0.0) == 1 + assert fp.erfc(0+0j) == 1 + assert ae(fp.erfc(0.3), 0.67137324054087258381) + assert ae(fp.erfc(-0.3), 1.3286267594591274162) + assert ae(fp.erfc(0.9), 0.20309178757716786034) + assert ae(fp.erfc(-0.9), 1.7969082124228321397) + assert ae(fp.erfc(1.0), 0.15729920705028513066) + assert ae(fp.erfc(-1.0), 1.8427007929497148693) + assert ae(fp.erfc(1.1), 0.11979493042591827034) + assert ae(fp.erfc(-1.1), 1.8802050695740817297) + assert ae(fp.erfc(8.5), 2.7623240713337714461e-33) + assert ae(fp.erfc(-8.5), 2.0) + assert ae(fp.erfc(9.1), 6.6969004279886077452e-38) + assert ae(fp.erfc(-9.1), 2.0) + assert ae(fp.erfc(20.0), 5.3958656116079009289e-176) + assert ae(fp.erfc(-20.0), 2.0) + assert ae(fp.erfc(10000.0), 0.0) + assert ae(fp.erfc(-10000.0), 2.0) + assert ae(fp.erfc(1e+50), 0.0) + assert ae(fp.erfc(-1e+50), 2.0) + assert ae(fp.erfc(1j), (1.0 - 1.650425758797542876j)) + assert ae(fp.erfc(-1j), (1.0 + 1.650425758797542876j)) + assert ae(fp.erfc((2+3j)), (21.829461427614568389 - 8.6873182714701631444j), 1e-13) + assert ae(fp.erfc(-(2+3j)), (-19.829461427614568389 + 8.6873182714701631444j), 1e-13) + assert ae(fp.erfc((8+9j)), (1072005.2525062051158 - 364149.91954310255423j)) + assert ae(fp.erfc(-(8+9j)), (-1072003.2525062051158 + 364149.91954310255423j)) + assert ae(fp.erfc(20+0j), (5.3958656116079009289e-176 + 0.0j)) + +def test_fp_lambertw(): + assert ae(fp.lambertw(0.0), 0.0) + assert ae(fp.lambertw(1.0), 0.567143290409783873) + assert ae(fp.lambertw(7.5), 1.5662309537823875394) + assert ae(fp.lambertw(-0.25), -0.35740295618138890307) + assert ae(fp.lambertw(-10.0), (1.3699809685212708156 + 2.140194527074713196j)) + assert ae(fp.lambertw(0+0j), (0.0 + 0.0j)) + assert ae(fp.lambertw(4+0j), (1.2021678731970429392 + 0.0j)) + assert ae(fp.lambertw(1000.5), 5.2500227450408980127) + assert ae(fp.lambertw(1e100), 224.84310644511850156) + assert ae(fp.lambertw(-1000.0), (5.1501630246362515223 + 2.6641981432905204596j)) + assert ae(fp.lambertw(1e-10), 9.9999999990000003645e-11) + assert ae(fp.lambertw(1e-10j), (1.0000000000000000728e-20 + 1.0000000000000000364e-10j)) + assert ae(fp.lambertw(3+4j), (1.2815618061237758782 + 0.53309522202097107131j)) + assert ae(fp.lambertw(-3-4j), (1.0750730665692549276 - 1.3251023817343588823j)) + assert ae(fp.lambertw(10000+1000j), (7.2361526563371602186 + 0.087567810943839352034j)) + assert ae(fp.lambertw(0.0, -1), -fp.inf) + assert ae(fp.lambertw(1.0, -1), (-1.5339133197935745079 - 4.3751851530618983855j)) + assert ae(fp.lambertw(7.5, -1), (0.44125668415098614999 - 4.8039842008452390179j)) + assert ae(fp.lambertw(-0.25, -1), -2.1532923641103496492) + assert ae(fp.lambertw(-10.0, -1), (1.3699809685212708156 - 2.140194527074713196j)) + assert ae(fp.lambertw(0+0j, -1), -fp.inf) + assert ae(fp.lambertw(4+0j, -1), (-0.15730793189620765317 - 4.6787800704666656212j)) + assert ae(fp.lambertw(1000.5, -1), (4.9153765415404024736 - 5.4465682700815159569j)) + assert ae(fp.lambertw(1e100, -1), (224.84272130101601052 - 6.2553713838167244141j)) + assert ae(fp.lambertw(-1000.0, -1), (5.1501630246362515223 - 2.6641981432905204596j)) + assert ae(fp.lambertw(1e-10, -1), (-26.303186778379041521 - 3.2650939117038283975j)) + assert ae(fp.lambertw(1e-10j, -1), (-26.297238779529035028 - 1.6328071613455765135j)) + assert ae(fp.lambertw(3+4j, -1), (0.25856740686699741676 - 3.8521166861614355895j)) + assert ae(fp.lambertw(-3-4j, -1), (-0.32028750204310768396 - 6.8801677192091972343j)) + assert ae(fp.lambertw(10000+1000j, -1), (7.0255308742285435567 - 5.5177506835734067601j)) + assert ae(fp.lambertw(0.0, 2), -fp.inf) + assert ae(fp.lambertw(1.0, 2), (-2.4015851048680028842 + 10.776299516115070898j)) + assert ae(fp.lambertw(7.5, 2), (-0.38003357962843791529 + 10.960916473368746184j)) + assert ae(fp.lambertw(-0.25, 2), (-4.0558735269061511898 + 13.852334658567271386j)) + assert ae(fp.lambertw(-10.0, 2), (-0.34479123764318858696 + 14.112740596763592363j)) + assert ae(fp.lambertw(0+0j, 2), -fp.inf) + assert ae(fp.lambertw(4+0j, 2), (-1.0070343323804262788 + 10.903476551861683082j)) + assert ae(fp.lambertw(1000.5, 2), (4.4076185165459395295 + 11.365524591091402177j)) + assert ae(fp.lambertw(1e100, 2), (224.84156762724875878 + 12.510785262632255672j)) + assert ae(fp.lambertw(-1000.0, 2), (4.1984245610246530756 + 14.420478573754313845j)) + assert ae(fp.lambertw(1e-10, 2), (-26.362258095445866488 + 9.7800247407031482519j)) + assert ae(fp.lambertw(1e-10j, 2), (-26.384250801683084252 + 11.403535950607739763j)) + assert ae(fp.lambertw(3+4j, 2), (-0.86554679943333993562 + 11.849956798331992027j)) + assert ae(fp.lambertw(-3-4j, 2), (-0.55792273874679112639 + 8.7173627024159324811j)) + assert ae(fp.lambertw(10000+1000j, 2), (6.6223802254585662734 + 11.61348646825020766j)) + +def test_fp_stress_ei_e1(): + # Can be tightened on recent Pythons with more accurate math/cmath + ATOL = 1e-13 + PTOL = 1e-12 + v = fp.e1(1.1641532182693481445e-10) + assert ae(v, 22.296641293693077672, tol=ATOL) + assert type(v) is float + v = fp.e1(0.25) + assert ae(v, 1.0442826344437381945, tol=ATOL) + assert type(v) is float + v = fp.e1(1.0) + assert ae(v, 0.21938393439552027368, tol=ATOL) + assert type(v) is float + v = fp.e1(2.0) + assert ae(v, 0.048900510708061119567, tol=ATOL) + assert type(v) is float + v = fp.e1(5.0) + assert ae(v, 0.0011482955912753257973, tol=ATOL) + assert type(v) is float + v = fp.e1(20.0) + assert ae(v, 9.8355252906498816904e-11, tol=ATOL) + assert type(v) is float + v = fp.e1(30.0) + assert ae(v, 3.0215520106888125448e-15, tol=ATOL) + assert type(v) is float + v = fp.e1(40.0) + assert ae(v, 1.0367732614516569722e-19, tol=ATOL) + assert type(v) is float + v = fp.e1(50.0) + assert ae(v, 3.7832640295504590187e-24, tol=ATOL) + assert type(v) is float + v = fp.e1(80.0) + assert ae(v, 2.2285432586884729112e-37, tol=ATOL) + assert type(v) is float + v = fp.e1((1.1641532182693481445e-10 + 0.0j)) + assert ae(v, (22.296641293693077672 + 0.0j), tol=ATOL) + assert ae(v.real, 22.296641293693077672, tol=PTOL) + assert v.imag == 0 + v = fp.e1((0.25 + 0.0j)) + assert ae(v, (1.0442826344437381945 + 0.0j), tol=ATOL) + assert ae(v.real, 1.0442826344437381945, tol=PTOL) + assert v.imag == 0 + v = fp.e1((1.0 + 0.0j)) + assert ae(v, (0.21938393439552027368 + 0.0j), tol=ATOL) + assert ae(v.real, 0.21938393439552027368, tol=PTOL) + assert v.imag == 0 + v = fp.e1((2.0 + 0.0j)) + assert ae(v, (0.048900510708061119567 + 0.0j), tol=ATOL) + assert ae(v.real, 0.048900510708061119567, tol=PTOL) + assert v.imag == 0 + v = fp.e1((5.0 + 0.0j)) + assert ae(v, (0.0011482955912753257973 + 0.0j), tol=ATOL) + assert ae(v.real, 0.0011482955912753257973, tol=PTOL) + assert v.imag == 0 + v = fp.e1((20.0 + 0.0j)) + assert ae(v, (9.8355252906498816904e-11 + 0.0j), tol=ATOL) + assert ae(v.real, 9.8355252906498816904e-11, tol=PTOL) + assert v.imag == 0 + v = fp.e1((30.0 + 0.0j)) + assert ae(v, (3.0215520106888125448e-15 + 0.0j), tol=ATOL) + assert ae(v.real, 3.0215520106888125448e-15, tol=PTOL) + assert v.imag == 0 + v = fp.e1((40.0 + 0.0j)) + assert ae(v, (1.0367732614516569722e-19 + 0.0j), tol=ATOL) + assert ae(v.real, 1.0367732614516569722e-19, tol=PTOL) + assert v.imag == 0 + v = fp.e1((50.0 + 0.0j)) + assert ae(v, (3.7832640295504590187e-24 + 0.0j), tol=ATOL) + assert ae(v.real, 3.7832640295504590187e-24, tol=PTOL) + assert v.imag == 0 + v = fp.e1((80.0 + 0.0j)) + assert ae(v, (2.2285432586884729112e-37 + 0.0j), tol=ATOL) + assert ae(v.real, 2.2285432586884729112e-37, tol=PTOL) + assert v.imag == 0 + v = fp.e1((4.6566128730773925781e-10 + 1.1641532182693481445e-10j)) + assert ae(v, (20.880034622014215597 - 0.24497866301044883237j), tol=ATOL) + assert ae(v.real, 20.880034622014215597, tol=PTOL) + assert ae(v.imag, -0.24497866301044883237, tol=PTOL) + v = fp.e1((1.0 + 0.25j)) + assert ae(v, (0.19731063945004229095 - 0.087366045774299963672j), tol=ATOL) + assert ae(v.real, 0.19731063945004229095, tol=PTOL) + assert ae(v.imag, -0.087366045774299963672, tol=PTOL) + v = fp.e1((4.0 + 1.0j)) + assert ae(v, (0.0013106173980145506944 - 0.0034542480199350626699j), tol=ATOL) + assert ae(v.real, 0.0013106173980145506944, tol=PTOL) + assert ae(v.imag, -0.0034542480199350626699, tol=PTOL) + v = fp.e1((8.0 + 2.0j)) + assert ae(v, (-0.000022278049065270225945 - 0.000029191940456521555288j), tol=ATOL) + assert ae(v.real, -0.000022278049065270225945, tol=PTOL) + assert ae(v.imag, -0.000029191940456521555288, tol=PTOL) + v = fp.e1((20.0 + 5.0j)) + assert ae(v, (4.7711374515765346894e-11 + 8.2902652405126947359e-11j), tol=ATOL) + assert ae(v.real, 4.7711374515765346894e-11, tol=PTOL) + assert ae(v.imag, 8.2902652405126947359e-11, tol=PTOL) + v = fp.e1((80.0 + 20.0j)) + assert ae(v, (3.8353473865788235787e-38 - 2.129247592349605139e-37j), tol=ATOL) + assert ae(v.real, 3.8353473865788235787e-38, tol=PTOL) + assert ae(v.imag, -2.129247592349605139e-37, tol=PTOL) + v = fp.e1((120.0 + 30.0j)) + assert ae(v, (2.3836002337480334716e-55 + 5.6704043587126198306e-55j), tol=ATOL) + assert ae(v.real, 2.3836002337480334716e-55, tol=PTOL) + assert ae(v.imag, 5.6704043587126198306e-55, tol=PTOL) + v = fp.e1((160.0 + 40.0j)) + assert ae(v, (-1.6238022898654510661e-72 - 1.104172355572287367e-72j), tol=ATOL) + assert ae(v.real, -1.6238022898654510661e-72, tol=PTOL) + assert ae(v.imag, -1.104172355572287367e-72, tol=PTOL) + v = fp.e1((200.0 + 50.0j)) + assert ae(v, (6.6800061461666228487e-90 + 1.4473816083541016115e-91j), tol=ATOL) + assert ae(v.real, 6.6800061461666228487e-90, tol=PTOL) + assert ae(v.imag, 1.4473816083541016115e-91, tol=PTOL) + v = fp.e1((320.0 + 80.0j)) + assert ae(v, (4.2737871527778786157e-143 + 3.1789935525785660314e-142j), tol=ATOL) + assert ae(v.real, 4.2737871527778786157e-143, tol=PTOL) + assert ae(v.imag, 3.1789935525785660314e-142, tol=PTOL) + v = fp.e1((1.1641532182693481445e-10 + 1.1641532182693481445e-10j)) + assert ae(v, (21.950067703413105017 - 0.7853981632810329878j), tol=ATOL) + assert ae(v.real, 21.950067703413105017, tol=PTOL) + assert ae(v.imag, -0.7853981632810329878, tol=PTOL) + v = fp.e1((0.25 + 0.25j)) + assert ae(v, (0.71092525792923287894 - 0.56491812441304194711j), tol=ATOL) + assert ae(v.real, 0.71092525792923287894, tol=PTOL) + assert ae(v.imag, -0.56491812441304194711, tol=PTOL) + v = fp.e1((1.0 + 1.0j)) + assert ae(v, (0.00028162445198141832551 - 0.17932453503935894015j), tol=ATOL) + assert ae(v.real, 0.00028162445198141832551, tol=PTOL) + assert ae(v.imag, -0.17932453503935894015, tol=PTOL) + v = fp.e1((2.0 + 2.0j)) + assert ae(v, (-0.033767089606562004246 - 0.018599414169750541925j), tol=ATOL) + assert ae(v.real, -0.033767089606562004246, tol=PTOL) + assert ae(v.imag, -0.018599414169750541925, tol=PTOL) + v = fp.e1((5.0 + 5.0j)) + assert ae(v, (0.0007266506660356393891 + 0.00047102780163522245054j), tol=ATOL) + assert ae(v.real, 0.0007266506660356393891, tol=PTOL) + assert ae(v.imag, 0.00047102780163522245054, tol=PTOL) + v = fp.e1((20.0 + 20.0j)) + assert ae(v, (-2.3824537449367396579e-11 - 6.6969873156525615158e-11j), tol=ATOL) + assert ae(v.real, -2.3824537449367396579e-11, tol=PTOL) + assert ae(v.imag, -6.6969873156525615158e-11, tol=PTOL) + v = fp.e1((30.0 + 30.0j)) + assert ae(v, (1.7316045841744061617e-15 + 1.3065678019487308689e-15j), tol=ATOL) + assert ae(v.real, 1.7316045841744061617e-15, tol=PTOL) + assert ae(v.imag, 1.3065678019487308689e-15, tol=PTOL) + v = fp.e1((40.0 + 40.0j)) + assert ae(v, (-7.4001043002899232182e-20 - 4.991847855336816304e-21j), tol=ATOL) + assert ae(v.real, -7.4001043002899232182e-20, tol=PTOL) + assert ae(v.imag, -4.991847855336816304e-21, tol=PTOL) + v = fp.e1((50.0 + 50.0j)) + assert ae(v, (2.3566128324644641219e-24 - 1.3188326726201614778e-24j), tol=ATOL) + assert ae(v.real, 2.3566128324644641219e-24, tol=PTOL) + assert ae(v.imag, -1.3188326726201614778e-24, tol=PTOL) + v = fp.e1((80.0 + 80.0j)) + assert ae(v, (9.8279750572186526673e-38 + 1.243952841288868831e-37j), tol=ATOL) + assert ae(v.real, 9.8279750572186526673e-38, tol=PTOL) + assert ae(v.imag, 1.243952841288868831e-37, tol=PTOL) + v = fp.e1((1.1641532182693481445e-10 + 4.6566128730773925781e-10j)) + assert ae(v, (20.880034621664969632 - 1.3258176632023711778j), tol=ATOL) + assert ae(v.real, 20.880034621664969632, tol=PTOL) + assert ae(v.imag, -1.3258176632023711778, tol=PTOL) + v = fp.e1((0.25 + 1.0j)) + assert ae(v, (-0.16868306393667788761 - 0.4858011885947426971j), tol=ATOL) + assert ae(v.real, -0.16868306393667788761, tol=PTOL) + assert ae(v.imag, -0.4858011885947426971, tol=PTOL) + v = fp.e1((1.0 + 4.0j)) + assert ae(v, (0.03373591813926547318 + 0.073523452241083821877j), tol=ATOL) + assert ae(v.real, 0.03373591813926547318, tol=PTOL) + assert ae(v.imag, 0.073523452241083821877, tol=PTOL) + v = fp.e1((2.0 + 8.0j)) + assert ae(v, (-0.015392833434733785143 - 0.0031747121557605415914j), tol=ATOL) + assert ae(v.real, -0.015392833434733785143, tol=PTOL) + assert ae(v.imag, -0.0031747121557605415914, tol=PTOL) + v = fp.e1((5.0 + 20.0j)) + assert ae(v, (-0.00024419662286542966525 - 0.00021008322966152755674j), tol=ATOL) + assert ae(v.real, -0.00024419662286542966525, tol=PTOL) + assert ae(v.imag, -0.00021008322966152755674, tol=PTOL) + v = fp.e1((20.0 + 80.0j)) + assert ae(v, (2.3255552781051330088e-11 + 8.9463918891349438007e-12j), tol=ATOL) + assert ae(v.real, 2.3255552781051330088e-11, tol=PTOL) + assert ae(v.imag, 8.9463918891349438007e-12, tol=PTOL) + v = fp.e1((30.0 + 120.0j)) + assert ae(v, (-2.7068919097124652332e-16 - 7.0477762411705130239e-16j), tol=ATOL) + assert ae(v.real, -2.7068919097124652332e-16, tol=PTOL) + assert ae(v.imag, -7.0477762411705130239e-16, tol=PTOL) + v = fp.e1((40.0 + 160.0j)) + assert ae(v, (-1.1695597827678024687e-20 + 2.2907401455645736661e-20j), tol=ATOL) + assert ae(v.real, -1.1695597827678024687e-20, tol=PTOL) + assert ae(v.imag, 2.2907401455645736661e-20, tol=PTOL) + v = fp.e1((50.0 + 200.0j)) + assert ae(v, (9.0323746914410162531e-25 - 2.3950601790033530935e-25j), tol=ATOL) + assert ae(v.real, 9.0323746914410162531e-25, tol=PTOL) + assert ae(v.imag, -2.3950601790033530935e-25, tol=PTOL) + v = fp.e1((80.0 + 320.0j)) + assert ae(v, (3.4819106748728063576e-38 - 4.215653005615772724e-38j), tol=ATOL) + assert ae(v.real, 3.4819106748728063576e-38, tol=PTOL) + assert ae(v.imag, -4.215653005615772724e-38, tol=PTOL) + v = fp.e1((0.0 + 1.1641532182693481445e-10j)) + assert ae(v, (22.29664129357666235 - 1.5707963266784812974j), tol=ATOL) + assert ae(v.real, 22.29664129357666235, tol=PTOL) + assert ae(v.imag, -1.5707963266784812974, tol=PTOL) + v = fp.e1((0.0 + 0.25j)) + assert ae(v, (0.82466306258094565309 - 1.3216627564751394551j), tol=ATOL) + assert ae(v.real, 0.82466306258094565309, tol=PTOL) + assert ae(v.imag, -1.3216627564751394551, tol=PTOL) + v = fp.e1((0.0 + 1.0j)) + assert ae(v, (-0.33740392290096813466 - 0.62471325642771360429j), tol=ATOL) + assert ae(v.real, -0.33740392290096813466, tol=PTOL) + assert ae(v.imag, -0.62471325642771360429, tol=PTOL) + v = fp.e1((0.0 + 2.0j)) + assert ae(v, (-0.4229808287748649957 + 0.034616650007798229345j), tol=ATOL) + assert ae(v.real, -0.4229808287748649957, tol=PTOL) + assert ae(v.imag, 0.034616650007798229345, tol=PTOL) + v = fp.e1((0.0 + 5.0j)) + assert ae(v, (0.19002974965664387862 - 0.020865081850222481957j), tol=ATOL) + assert ae(v.real, 0.19002974965664387862, tol=PTOL) + assert ae(v.imag, -0.020865081850222481957, tol=PTOL) + v = fp.e1((0.0 + 20.0j)) + assert ae(v, (-0.04441982084535331654 - 0.022554625751456779068j), tol=ATOL) + assert ae(v.real, -0.04441982084535331654, tol=PTOL) + assert ae(v.imag, -0.022554625751456779068, tol=PTOL) + v = fp.e1((0.0 + 30.0j)) + assert ae(v, (0.033032417282071143779 - 0.0040397867645455082476j), tol=ATOL) + assert ae(v.real, 0.033032417282071143779, tol=PTOL) + assert ae(v.imag, -0.0040397867645455082476, tol=PTOL) + v = fp.e1((0.0 + 40.0j)) + assert ae(v, (-0.019020007896208766962 + 0.016188792559887887544j), tol=ATOL) + assert ae(v.real, -0.019020007896208766962, tol=PTOL) + assert ae(v.imag, 0.016188792559887887544, tol=PTOL) + v = fp.e1((0.0 + 50.0j)) + assert ae(v, (0.0056283863241163054402 - 0.019179254308960724503j), tol=ATOL) + assert ae(v.real, 0.0056283863241163054402, tol=PTOL) + assert ae(v.imag, -0.019179254308960724503, tol=PTOL) + v = fp.e1((0.0 + 80.0j)) + assert ae(v, (0.012402501155070958192 + 0.0015345601175906961199j), tol=ATOL) + assert ae(v.real, 0.012402501155070958192, tol=PTOL) + assert ae(v.imag, 0.0015345601175906961199, tol=PTOL) + v = fp.e1((-1.1641532182693481445e-10 + 4.6566128730773925781e-10j)) + assert ae(v, (20.880034621432138988 - 1.8157749894560994861j), tol=ATOL) + assert ae(v.real, 20.880034621432138988, tol=PTOL) + assert ae(v.imag, -1.8157749894560994861, tol=PTOL) + v = fp.e1((-0.25 + 1.0j)) + assert ae(v, (-0.59066621214766308594 - 0.74474454765205036972j), tol=ATOL) + assert ae(v.real, -0.59066621214766308594, tol=PTOL) + assert ae(v.imag, -0.74474454765205036972, tol=PTOL) + v = fp.e1((-1.0 + 4.0j)) + assert ae(v, (0.49739047283060471093 + 0.41543605404038863174j), tol=ATOL) + assert ae(v.real, 0.49739047283060471093, tol=PTOL) + assert ae(v.imag, 0.41543605404038863174, tol=PTOL) + v = fp.e1((-2.0 + 8.0j)) + assert ae(v, (-0.8705211147733730969 + 0.24099328498605539667j), tol=ATOL) + assert ae(v.real, -0.8705211147733730969, tol=PTOL) + assert ae(v.imag, 0.24099328498605539667, tol=PTOL) + v = fp.e1((-5.0 + 20.0j)) + assert ae(v, (-7.0789514293925893007 - 1.6102177171960790536j), tol=ATOL) + assert ae(v.real, -7.0789514293925893007, tol=PTOL) + assert ae(v.imag, -1.6102177171960790536, tol=PTOL) + v = fp.e1((-20.0 + 80.0j)) + assert ae(v, (5855431.4907298084434 - 720920.93315409165707j), tol=ATOL) + assert ae(v.real, 5855431.4907298084434, tol=PTOL) + assert ae(v.imag, -720920.93315409165707, tol=PTOL) + v = fp.e1((-30.0 + 120.0j)) + assert ae(v, (-65402491644.703470747 - 56697658399.657460294j), tol=ATOL) + assert ae(v.real, -65402491644.703470747, tol=PTOL) + assert ae(v.imag, -56697658399.657460294, tol=PTOL) + v = fp.e1((-40.0 + 160.0j)) + assert ae(v, (25504929379604.776769 + 1429035198630573.2463j), tol=ATOL) + assert ae(v.real, 25504929379604.776769, tol=PTOL) + assert ae(v.imag, 1429035198630573.2463, tol=PTOL) + v = fp.e1((-50.0 + 200.0j)) + assert ae(v, (18437746526988116954.0 - 17146362239046152345.0j), tol=ATOL) + assert ae(v.real, 18437746526988116954.0, tol=PTOL) + assert ae(v.imag, -17146362239046152345.0, tol=PTOL) + v = fp.e1((-80.0 + 320.0j)) + assert ae(v, (3.3464697299634526706e+31 - 1.6473152633843023919e+32j), tol=ATOL) + assert ae(v.real, 3.3464697299634526706e+31, tol=PTOL) + assert ae(v.imag, -1.6473152633843023919e+32, tol=PTOL) + v = fp.e1((-4.6566128730773925781e-10 + 1.1641532182693481445e-10j)) + assert ae(v, (20.880034621082893023 - 2.8966139903465137624j), tol=ATOL) + assert ae(v.real, 20.880034621082893023, tol=PTOL) + assert ae(v.imag, -2.8966139903465137624, tol=PTOL) + v = fp.e1((-1.0 + 0.25j)) + assert ae(v, (-1.8942716983721074932 - 2.4689102827070540799j), tol=ATOL) + assert ae(v.real, -1.8942716983721074932, tol=PTOL) + assert ae(v.imag, -2.4689102827070540799, tol=PTOL) + v = fp.e1((-4.0 + 1.0j)) + assert ae(v, (-14.806699492675420438 + 9.1384225230837893776j), tol=ATOL) + assert ae(v.real, -14.806699492675420438, tol=PTOL) + assert ae(v.imag, 9.1384225230837893776, tol=PTOL) + v = fp.e1((-8.0 + 2.0j)) + assert ae(v, (54.633252667426386294 + 413.20318163814670688j), tol=ATOL) + assert ae(v.real, 54.633252667426386294, tol=PTOL) + assert ae(v.imag, 413.20318163814670688, tol=PTOL) + v = fp.e1((-20.0 + 5.0j)) + assert ae(v, (-711836.97165402624643 - 24745250.939695900956j), tol=ATOL) + assert ae(v.real, -711836.97165402624643, tol=PTOL) + assert ae(v.imag, -24745250.939695900956, tol=PTOL) + v = fp.e1((-80.0 + 20.0j)) + assert ae(v, (-4.2139911108612653091e+32 + 5.3367124741918251637e+32j), tol=ATOL) + assert ae(v.real, -4.2139911108612653091e+32, tol=PTOL) + assert ae(v.imag, 5.3367124741918251637e+32, tol=PTOL) + v = fp.e1((-120.0 + 30.0j)) + assert ae(v, (9.7760616203707508892e+48 - 1.058257682317195792e+50j), tol=ATOL) + assert ae(v.real, 9.7760616203707508892e+48, tol=PTOL) + assert ae(v.imag, -1.058257682317195792e+50, tol=PTOL) + v = fp.e1((-160.0 + 40.0j)) + assert ae(v, (8.7065541466623638861e+66 + 1.6577106725141739889e+67j), tol=ATOL) + assert ae(v.real, 8.7065541466623638861e+66, tol=PTOL) + assert ae(v.imag, 1.6577106725141739889e+67, tol=PTOL) + v = fp.e1((-200.0 + 50.0j)) + assert ae(v, (-3.070744996327018106e+84 - 1.7243244846769415903e+84j), tol=ATOL) + assert ae(v.real, -3.070744996327018106e+84, tol=PTOL) + assert ae(v.imag, -1.7243244846769415903e+84, tol=PTOL) + v = fp.e1((-320.0 + 80.0j)) + assert ae(v, (9.9960598637998647276e+135 - 2.6855081527595608863e+136j), tol=ATOL) + assert ae(v.real, 9.9960598637998647276e+135, tol=PTOL) + assert ae(v.imag, -2.6855081527595608863e+136, tol=PTOL) + v = fp.e1(-1.1641532182693481445e-10) + assert ae(v, (22.296641293460247028 - 3.1415926535897932385j), tol=ATOL) + assert ae(v.real, 22.296641293460247028, tol=PTOL) + assert ae(v.imag, -3.1415926535897932385, tol=PTOL) + v = fp.e1(-0.25) + assert ae(v, (0.54254326466191372953 - 3.1415926535897932385j), tol=ATOL) + assert ae(v.real, 0.54254326466191372953, tol=PTOL) + assert ae(v.imag, -3.1415926535897932385, tol=PTOL) + v = fp.e1(-1.0) + assert ae(v, (-1.8951178163559367555 - 3.1415926535897932385j), tol=ATOL) + assert ae(v.real, -1.8951178163559367555, tol=PTOL) + assert ae(v.imag, -3.1415926535897932385, tol=PTOL) + v = fp.e1(-2.0) + assert ae(v, (-4.9542343560018901634 - 3.1415926535897932385j), tol=ATOL) + assert ae(v.real, -4.9542343560018901634, tol=PTOL) + assert ae(v.imag, -3.1415926535897932385, tol=PTOL) + v = fp.e1(-5.0) + assert ae(v, (-40.185275355803177455 - 3.1415926535897932385j), tol=ATOL) + assert ae(v.real, -40.185275355803177455, tol=PTOL) + assert ae(v.imag, -3.1415926535897932385, tol=PTOL) + v = fp.e1(-20.0) + assert ae(v, (-25615652.66405658882 - 3.1415926535897932385j), tol=ATOL) + assert ae(v.real, -25615652.66405658882, tol=PTOL) + assert ae(v.imag, -3.1415926535897932385, tol=PTOL) + v = fp.e1(-30.0) + assert ae(v, (-368973209407.27419706 - 3.1415926535897932385j), tol=ATOL) + assert ae(v.real, -368973209407.27419706, tol=PTOL) + assert ae(v.imag, -3.1415926535897932385, tol=PTOL) + v = fp.e1(-40.0) + assert ae(v, (-6039718263611241.5784 - 3.1415926535897932385j), tol=ATOL) + assert ae(v.real, -6039718263611241.5784, tol=PTOL) + assert ae(v.imag, -3.1415926535897932385, tol=PTOL) + v = fp.e1(-50.0) + assert ae(v, (-1.0585636897131690963e+20 - 3.1415926535897932385j), tol=ATOL) + assert ae(v.real, -1.0585636897131690963e+20, tol=PTOL) + assert ae(v.imag, -3.1415926535897932385, tol=PTOL) + v = fp.e1(-80.0) + assert ae(v, (-7.0146000049047999696e+32 - 3.1415926535897932385j), tol=ATOL) + assert ae(v.real, -7.0146000049047999696e+32, tol=PTOL) + assert ae(v.imag, -3.1415926535897932385, tol=PTOL) + v = fp.e1((-1.1641532182693481445e-10 + 0.0j)) + assert ae(v, (22.296641293460247028 - 3.1415926535897932385j), tol=ATOL) + assert ae(v.real, 22.296641293460247028, tol=PTOL) + assert ae(v.imag, -3.1415926535897932385, tol=PTOL) + v = fp.e1((-0.25 + 0.0j)) + assert ae(v, (0.54254326466191372953 - 3.1415926535897932385j), tol=ATOL) + assert ae(v.real, 0.54254326466191372953, tol=PTOL) + assert ae(v.imag, -3.1415926535897932385, tol=PTOL) + v = fp.e1((-1.0 + 0.0j)) + assert ae(v, (-1.8951178163559367555 - 3.1415926535897932385j), tol=ATOL) + assert ae(v.real, -1.8951178163559367555, tol=PTOL) + assert ae(v.imag, -3.1415926535897932385, tol=PTOL) + v = fp.e1((-2.0 + 0.0j)) + assert ae(v, (-4.9542343560018901634 - 3.1415926535897932385j), tol=ATOL) + assert ae(v.real, -4.9542343560018901634, tol=PTOL) + assert ae(v.imag, -3.1415926535897932385, tol=PTOL) + v = fp.e1((-5.0 + 0.0j)) + assert ae(v, (-40.185275355803177455 - 3.1415926535897932385j), tol=ATOL) + assert ae(v.real, -40.185275355803177455, tol=PTOL) + assert ae(v.imag, -3.1415926535897932385, tol=PTOL) + v = fp.e1((-20.0 + 0.0j)) + assert ae(v, (-25615652.66405658882 - 3.1415926535897932385j), tol=ATOL) + assert ae(v.real, -25615652.66405658882, tol=PTOL) + assert ae(v.imag, -3.1415926535897932385, tol=PTOL) + v = fp.e1((-30.0 + 0.0j)) + assert ae(v, (-368973209407.27419706 - 3.1415926535897932385j), tol=ATOL) + assert ae(v.real, -368973209407.27419706, tol=PTOL) + assert ae(v.imag, -3.1415926535897932385, tol=PTOL) + v = fp.e1((-40.0 + 0.0j)) + assert ae(v, (-6039718263611241.5784 - 3.1415926535897932385j), tol=ATOL) + assert ae(v.real, -6039718263611241.5784, tol=PTOL) + assert ae(v.imag, -3.1415926535897932385, tol=PTOL) + v = fp.e1((-50.0 + 0.0j)) + assert ae(v, (-1.0585636897131690963e+20 - 3.1415926535897932385j), tol=ATOL) + assert ae(v.real, -1.0585636897131690963e+20, tol=PTOL) + assert ae(v.imag, -3.1415926535897932385, tol=PTOL) + v = fp.e1((-80.0 + 0.0j)) + assert ae(v, (-7.0146000049047999696e+32 - 3.1415926535897932385j), tol=ATOL) + assert ae(v.real, -7.0146000049047999696e+32, tol=PTOL) + assert ae(v.imag, -3.1415926535897932385, tol=PTOL) + v = fp.e1((-4.6566128730773925781e-10 - 1.1641532182693481445e-10j)) + assert ae(v, (20.880034621082893023 + 2.8966139903465137624j), tol=ATOL) + assert ae(v.real, 20.880034621082893023, tol=PTOL) + assert ae(v.imag, 2.8966139903465137624, tol=PTOL) + v = fp.e1((-1.0 - 0.25j)) + assert ae(v, (-1.8942716983721074932 + 2.4689102827070540799j), tol=ATOL) + assert ae(v.real, -1.8942716983721074932, tol=PTOL) + assert ae(v.imag, 2.4689102827070540799, tol=PTOL) + v = fp.e1((-4.0 - 1.0j)) + assert ae(v, (-14.806699492675420438 - 9.1384225230837893776j), tol=ATOL) + assert ae(v.real, -14.806699492675420438, tol=PTOL) + assert ae(v.imag, -9.1384225230837893776, tol=PTOL) + v = fp.e1((-8.0 - 2.0j)) + assert ae(v, (54.633252667426386294 - 413.20318163814670688j), tol=ATOL) + assert ae(v.real, 54.633252667426386294, tol=PTOL) + assert ae(v.imag, -413.20318163814670688, tol=PTOL) + v = fp.e1((-20.0 - 5.0j)) + assert ae(v, (-711836.97165402624643 + 24745250.939695900956j), tol=ATOL) + assert ae(v.real, -711836.97165402624643, tol=PTOL) + assert ae(v.imag, 24745250.939695900956, tol=PTOL) + v = fp.e1((-80.0 - 20.0j)) + assert ae(v, (-4.2139911108612653091e+32 - 5.3367124741918251637e+32j), tol=ATOL) + assert ae(v.real, -4.2139911108612653091e+32, tol=PTOL) + assert ae(v.imag, -5.3367124741918251637e+32, tol=PTOL) + v = fp.e1((-120.0 - 30.0j)) + assert ae(v, (9.7760616203707508892e+48 + 1.058257682317195792e+50j), tol=ATOL) + assert ae(v.real, 9.7760616203707508892e+48, tol=PTOL) + assert ae(v.imag, 1.058257682317195792e+50, tol=PTOL) + v = fp.e1((-160.0 - 40.0j)) + assert ae(v, (8.7065541466623638861e+66 - 1.6577106725141739889e+67j), tol=ATOL) + assert ae(v.real, 8.7065541466623638861e+66, tol=PTOL) + assert ae(v.imag, -1.6577106725141739889e+67, tol=PTOL) + v = fp.e1((-200.0 - 50.0j)) + assert ae(v, (-3.070744996327018106e+84 + 1.7243244846769415903e+84j), tol=ATOL) + assert ae(v.real, -3.070744996327018106e+84, tol=PTOL) + assert ae(v.imag, 1.7243244846769415903e+84, tol=PTOL) + v = fp.e1((-320.0 - 80.0j)) + assert ae(v, (9.9960598637998647276e+135 + 2.6855081527595608863e+136j), tol=ATOL) + assert ae(v.real, 9.9960598637998647276e+135, tol=PTOL) + assert ae(v.imag, 2.6855081527595608863e+136, tol=PTOL) + v = fp.e1((-1.1641532182693481445e-10 - 1.1641532182693481445e-10j)) + assert ae(v, (21.950067703180274374 + 2.356194490075929607j), tol=ATOL) + assert ae(v.real, 21.950067703180274374, tol=PTOL) + assert ae(v.imag, 2.356194490075929607, tol=PTOL) + v = fp.e1((-0.25 - 0.25j)) + assert ae(v, (0.21441047326710323254 + 2.0732153554307936389j), tol=ATOL) + assert ae(v.real, 0.21441047326710323254, tol=PTOL) + assert ae(v.imag, 2.0732153554307936389, tol=PTOL) + v = fp.e1((-1.0 - 1.0j)) + assert ae(v, (-1.7646259855638540684 + 0.7538228020792708192j), tol=ATOL) + assert ae(v.real, -1.7646259855638540684, tol=PTOL) + assert ae(v.imag, 0.7538228020792708192, tol=PTOL) + v = fp.e1((-2.0 - 2.0j)) + assert ae(v, (-1.8920781621855474089 - 2.1753697842428647236j), tol=ATOL) + assert ae(v.real, -1.8920781621855474089, tol=PTOL) + assert ae(v.imag, -2.1753697842428647236, tol=PTOL) + v = fp.e1((-5.0 - 5.0j)) + assert ae(v, (13.470936071475245856 + 18.464085049321024206j), tol=ATOL) + assert ae(v.real, 13.470936071475245856, tol=PTOL) + assert ae(v.imag, 18.464085049321024206, tol=PTOL) + v = fp.e1((-20.0 - 20.0j)) + assert ae(v, (-16589317.398788971896 - 5831702.3296441771206j), tol=ATOL) + assert ae(v.real, -16589317.398788971896, tol=PTOL) + assert ae(v.imag, -5831702.3296441771206, tol=PTOL) + v = fp.e1((-30.0 - 30.0j)) + assert ae(v, (154596484273.69322527 + 204179357837.41389696j), tol=ATOL) + assert ae(v.real, 154596484273.69322527, tol=PTOL) + assert ae(v.imag, 204179357837.41389696, tol=PTOL) + v = fp.e1((-40.0 - 40.0j)) + assert ae(v, (-287512180321448.45408 - 4203502407932314.974j), tol=ATOL) + assert ae(v.real, -287512180321448.45408, tol=PTOL) + assert ae(v.imag, -4203502407932314.974, tol=PTOL) + v = fp.e1((-50.0 - 50.0j)) + assert ae(v, (-36128528616649268826.0 + 64648801861338741963.0j), tol=ATOL) + assert ae(v.real, -36128528616649268826.0, tol=PTOL) + assert ae(v.imag, 64648801861338741963.0, tol=PTOL) + v = fp.e1((-80.0 - 80.0j)) + assert ae(v, (3.8674816337930010217e+32 + 3.0540709639658071041e+32j), tol=ATOL) + assert ae(v.real, 3.8674816337930010217e+32, tol=PTOL) + assert ae(v.imag, 3.0540709639658071041e+32, tol=PTOL) + v = fp.e1((-1.1641532182693481445e-10 - 4.6566128730773925781e-10j)) + assert ae(v, (20.880034621432138988 + 1.8157749894560994861j), tol=ATOL) + assert ae(v.real, 20.880034621432138988, tol=PTOL) + assert ae(v.imag, 1.8157749894560994861, tol=PTOL) + v = fp.e1((-0.25 - 1.0j)) + assert ae(v, (-0.59066621214766308594 + 0.74474454765205036972j), tol=ATOL) + assert ae(v.real, -0.59066621214766308594, tol=PTOL) + assert ae(v.imag, 0.74474454765205036972, tol=PTOL) + v = fp.e1((-1.0 - 4.0j)) + assert ae(v, (0.49739047283060471093 - 0.41543605404038863174j), tol=ATOL) + assert ae(v.real, 0.49739047283060471093, tol=PTOL) + assert ae(v.imag, -0.41543605404038863174, tol=PTOL) + v = fp.e1((-2.0 - 8.0j)) + assert ae(v, (-0.8705211147733730969 - 0.24099328498605539667j), tol=ATOL) + assert ae(v.real, -0.8705211147733730969, tol=PTOL) + assert ae(v.imag, -0.24099328498605539667, tol=PTOL) + v = fp.e1((-5.0 - 20.0j)) + assert ae(v, (-7.0789514293925893007 + 1.6102177171960790536j), tol=ATOL) + assert ae(v.real, -7.0789514293925893007, tol=PTOL) + assert ae(v.imag, 1.6102177171960790536, tol=PTOL) + v = fp.e1((-20.0 - 80.0j)) + assert ae(v, (5855431.4907298084434 + 720920.93315409165707j), tol=ATOL) + assert ae(v.real, 5855431.4907298084434, tol=PTOL) + assert ae(v.imag, 720920.93315409165707, tol=PTOL) + v = fp.e1((-30.0 - 120.0j)) + assert ae(v, (-65402491644.703470747 + 56697658399.657460294j), tol=ATOL) + assert ae(v.real, -65402491644.703470747, tol=PTOL) + assert ae(v.imag, 56697658399.657460294, tol=PTOL) + v = fp.e1((-40.0 - 160.0j)) + assert ae(v, (25504929379604.776769 - 1429035198630573.2463j), tol=ATOL) + assert ae(v.real, 25504929379604.776769, tol=PTOL) + assert ae(v.imag, -1429035198630573.2463, tol=PTOL) + v = fp.e1((-50.0 - 200.0j)) + assert ae(v, (18437746526988116954.0 + 17146362239046152345.0j), tol=ATOL) + assert ae(v.real, 18437746526988116954.0, tol=PTOL) + assert ae(v.imag, 17146362239046152345.0, tol=PTOL) + v = fp.e1((-80.0 - 320.0j)) + assert ae(v, (3.3464697299634526706e+31 + 1.6473152633843023919e+32j), tol=ATOL) + assert ae(v.real, 3.3464697299634526706e+31, tol=PTOL) + assert ae(v.imag, 1.6473152633843023919e+32, tol=PTOL) + v = fp.e1((0.0 - 1.1641532182693481445e-10j)) + assert ae(v, (22.29664129357666235 + 1.5707963266784812974j), tol=ATOL) + assert ae(v.real, 22.29664129357666235, tol=PTOL) + assert ae(v.imag, 1.5707963266784812974, tol=PTOL) + v = fp.e1((0.0 - 0.25j)) + assert ae(v, (0.82466306258094565309 + 1.3216627564751394551j), tol=ATOL) + assert ae(v.real, 0.82466306258094565309, tol=PTOL) + assert ae(v.imag, 1.3216627564751394551, tol=PTOL) + v = fp.e1((0.0 - 1.0j)) + assert ae(v, (-0.33740392290096813466 + 0.62471325642771360429j), tol=ATOL) + assert ae(v.real, -0.33740392290096813466, tol=PTOL) + assert ae(v.imag, 0.62471325642771360429, tol=PTOL) + v = fp.e1((0.0 - 2.0j)) + assert ae(v, (-0.4229808287748649957 - 0.034616650007798229345j), tol=ATOL) + assert ae(v.real, -0.4229808287748649957, tol=PTOL) + assert ae(v.imag, -0.034616650007798229345, tol=PTOL) + v = fp.e1((0.0 - 5.0j)) + assert ae(v, (0.19002974965664387862 + 0.020865081850222481957j), tol=ATOL) + assert ae(v.real, 0.19002974965664387862, tol=PTOL) + assert ae(v.imag, 0.020865081850222481957, tol=PTOL) + v = fp.e1((0.0 - 20.0j)) + assert ae(v, (-0.04441982084535331654 + 0.022554625751456779068j), tol=ATOL) + assert ae(v.real, -0.04441982084535331654, tol=PTOL) + assert ae(v.imag, 0.022554625751456779068, tol=PTOL) + v = fp.e1((0.0 - 30.0j)) + assert ae(v, (0.033032417282071143779 + 0.0040397867645455082476j), tol=ATOL) + assert ae(v.real, 0.033032417282071143779, tol=PTOL) + assert ae(v.imag, 0.0040397867645455082476, tol=PTOL) + v = fp.e1((0.0 - 40.0j)) + assert ae(v, (-0.019020007896208766962 - 0.016188792559887887544j), tol=ATOL) + assert ae(v.real, -0.019020007896208766962, tol=PTOL) + assert ae(v.imag, -0.016188792559887887544, tol=PTOL) + v = fp.e1((0.0 - 50.0j)) + assert ae(v, (0.0056283863241163054402 + 0.019179254308960724503j), tol=ATOL) + assert ae(v.real, 0.0056283863241163054402, tol=PTOL) + assert ae(v.imag, 0.019179254308960724503, tol=PTOL) + v = fp.e1((0.0 - 80.0j)) + assert ae(v, (0.012402501155070958192 - 0.0015345601175906961199j), tol=ATOL) + assert ae(v.real, 0.012402501155070958192, tol=PTOL) + assert ae(v.imag, -0.0015345601175906961199, tol=PTOL) + v = fp.e1((1.1641532182693481445e-10 - 4.6566128730773925781e-10j)) + assert ae(v, (20.880034621664969632 + 1.3258176632023711778j), tol=ATOL) + assert ae(v.real, 20.880034621664969632, tol=PTOL) + assert ae(v.imag, 1.3258176632023711778, tol=PTOL) + v = fp.e1((0.25 - 1.0j)) + assert ae(v, (-0.16868306393667788761 + 0.4858011885947426971j), tol=ATOL) + assert ae(v.real, -0.16868306393667788761, tol=PTOL) + assert ae(v.imag, 0.4858011885947426971, tol=PTOL) + v = fp.e1((1.0 - 4.0j)) + assert ae(v, (0.03373591813926547318 - 0.073523452241083821877j), tol=ATOL) + assert ae(v.real, 0.03373591813926547318, tol=PTOL) + assert ae(v.imag, -0.073523452241083821877, tol=PTOL) + v = fp.e1((2.0 - 8.0j)) + assert ae(v, (-0.015392833434733785143 + 0.0031747121557605415914j), tol=ATOL) + assert ae(v.real, -0.015392833434733785143, tol=PTOL) + assert ae(v.imag, 0.0031747121557605415914, tol=PTOL) + v = fp.e1((5.0 - 20.0j)) + assert ae(v, (-0.00024419662286542966525 + 0.00021008322966152755674j), tol=ATOL) + assert ae(v.real, -0.00024419662286542966525, tol=PTOL) + assert ae(v.imag, 0.00021008322966152755674, tol=PTOL) + v = fp.e1((20.0 - 80.0j)) + assert ae(v, (2.3255552781051330088e-11 - 8.9463918891349438007e-12j), tol=ATOL) + assert ae(v.real, 2.3255552781051330088e-11, tol=PTOL) + assert ae(v.imag, -8.9463918891349438007e-12, tol=PTOL) + v = fp.e1((30.0 - 120.0j)) + assert ae(v, (-2.7068919097124652332e-16 + 7.0477762411705130239e-16j), tol=ATOL) + assert ae(v.real, -2.7068919097124652332e-16, tol=PTOL) + assert ae(v.imag, 7.0477762411705130239e-16, tol=PTOL) + v = fp.e1((40.0 - 160.0j)) + assert ae(v, (-1.1695597827678024687e-20 - 2.2907401455645736661e-20j), tol=ATOL) + assert ae(v.real, -1.1695597827678024687e-20, tol=PTOL) + assert ae(v.imag, -2.2907401455645736661e-20, tol=PTOL) + v = fp.e1((50.0 - 200.0j)) + assert ae(v, (9.0323746914410162531e-25 + 2.3950601790033530935e-25j), tol=ATOL) + assert ae(v.real, 9.0323746914410162531e-25, tol=PTOL) + assert ae(v.imag, 2.3950601790033530935e-25, tol=PTOL) + v = fp.e1((80.0 - 320.0j)) + assert ae(v, (3.4819106748728063576e-38 + 4.215653005615772724e-38j), tol=ATOL) + assert ae(v.real, 3.4819106748728063576e-38, tol=PTOL) + assert ae(v.imag, 4.215653005615772724e-38, tol=PTOL) + v = fp.e1((1.1641532182693481445e-10 - 1.1641532182693481445e-10j)) + assert ae(v, (21.950067703413105017 + 0.7853981632810329878j), tol=ATOL) + assert ae(v.real, 21.950067703413105017, tol=PTOL) + assert ae(v.imag, 0.7853981632810329878, tol=PTOL) + v = fp.e1((0.25 - 0.25j)) + assert ae(v, (0.71092525792923287894 + 0.56491812441304194711j), tol=ATOL) + assert ae(v.real, 0.71092525792923287894, tol=PTOL) + assert ae(v.imag, 0.56491812441304194711, tol=PTOL) + v = fp.e1((1.0 - 1.0j)) + assert ae(v, (0.00028162445198141832551 + 0.17932453503935894015j), tol=ATOL) + assert ae(v.real, 0.00028162445198141832551, tol=PTOL) + assert ae(v.imag, 0.17932453503935894015, tol=PTOL) + v = fp.e1((2.0 - 2.0j)) + assert ae(v, (-0.033767089606562004246 + 0.018599414169750541925j), tol=ATOL) + assert ae(v.real, -0.033767089606562004246, tol=PTOL) + assert ae(v.imag, 0.018599414169750541925, tol=PTOL) + v = fp.e1((5.0 - 5.0j)) + assert ae(v, (0.0007266506660356393891 - 0.00047102780163522245054j), tol=ATOL) + assert ae(v.real, 0.0007266506660356393891, tol=PTOL) + assert ae(v.imag, -0.00047102780163522245054, tol=PTOL) + v = fp.e1((20.0 - 20.0j)) + assert ae(v, (-2.3824537449367396579e-11 + 6.6969873156525615158e-11j), tol=ATOL) + assert ae(v.real, -2.3824537449367396579e-11, tol=PTOL) + assert ae(v.imag, 6.6969873156525615158e-11, tol=PTOL) + v = fp.e1((30.0 - 30.0j)) + assert ae(v, (1.7316045841744061617e-15 - 1.3065678019487308689e-15j), tol=ATOL) + assert ae(v.real, 1.7316045841744061617e-15, tol=PTOL) + assert ae(v.imag, -1.3065678019487308689e-15, tol=PTOL) + v = fp.e1((40.0 - 40.0j)) + assert ae(v, (-7.4001043002899232182e-20 + 4.991847855336816304e-21j), tol=ATOL) + assert ae(v.real, -7.4001043002899232182e-20, tol=PTOL) + assert ae(v.imag, 4.991847855336816304e-21, tol=PTOL) + v = fp.e1((50.0 - 50.0j)) + assert ae(v, (2.3566128324644641219e-24 + 1.3188326726201614778e-24j), tol=ATOL) + assert ae(v.real, 2.3566128324644641219e-24, tol=PTOL) + assert ae(v.imag, 1.3188326726201614778e-24, tol=PTOL) + v = fp.e1((80.0 - 80.0j)) + assert ae(v, (9.8279750572186526673e-38 - 1.243952841288868831e-37j), tol=ATOL) + assert ae(v.real, 9.8279750572186526673e-38, tol=PTOL) + assert ae(v.imag, -1.243952841288868831e-37, tol=PTOL) + v = fp.e1((4.6566128730773925781e-10 - 1.1641532182693481445e-10j)) + assert ae(v, (20.880034622014215597 + 0.24497866301044883237j), tol=ATOL) + assert ae(v.real, 20.880034622014215597, tol=PTOL) + assert ae(v.imag, 0.24497866301044883237, tol=PTOL) + v = fp.e1((1.0 - 0.25j)) + assert ae(v, (0.19731063945004229095 + 0.087366045774299963672j), tol=ATOL) + assert ae(v.real, 0.19731063945004229095, tol=PTOL) + assert ae(v.imag, 0.087366045774299963672, tol=PTOL) + v = fp.e1((4.0 - 1.0j)) + assert ae(v, (0.0013106173980145506944 + 0.0034542480199350626699j), tol=ATOL) + assert ae(v.real, 0.0013106173980145506944, tol=PTOL) + assert ae(v.imag, 0.0034542480199350626699, tol=PTOL) + v = fp.e1((8.0 - 2.0j)) + assert ae(v, (-0.000022278049065270225945 + 0.000029191940456521555288j), tol=ATOL) + assert ae(v.real, -0.000022278049065270225945, tol=PTOL) + assert ae(v.imag, 0.000029191940456521555288, tol=PTOL) + v = fp.e1((20.0 - 5.0j)) + assert ae(v, (4.7711374515765346894e-11 - 8.2902652405126947359e-11j), tol=ATOL) + assert ae(v.real, 4.7711374515765346894e-11, tol=PTOL) + assert ae(v.imag, -8.2902652405126947359e-11, tol=PTOL) + v = fp.e1((80.0 - 20.0j)) + assert ae(v, (3.8353473865788235787e-38 + 2.129247592349605139e-37j), tol=ATOL) + assert ae(v.real, 3.8353473865788235787e-38, tol=PTOL) + assert ae(v.imag, 2.129247592349605139e-37, tol=PTOL) + v = fp.e1((120.0 - 30.0j)) + assert ae(v, (2.3836002337480334716e-55 - 5.6704043587126198306e-55j), tol=ATOL) + assert ae(v.real, 2.3836002337480334716e-55, tol=PTOL) + assert ae(v.imag, -5.6704043587126198306e-55, tol=PTOL) + v = fp.e1((160.0 - 40.0j)) + assert ae(v, (-1.6238022898654510661e-72 + 1.104172355572287367e-72j), tol=ATOL) + assert ae(v.real, -1.6238022898654510661e-72, tol=PTOL) + assert ae(v.imag, 1.104172355572287367e-72, tol=PTOL) + v = fp.e1((200.0 - 50.0j)) + assert ae(v, (6.6800061461666228487e-90 - 1.4473816083541016115e-91j), tol=ATOL) + assert ae(v.real, 6.6800061461666228487e-90, tol=PTOL) + assert ae(v.imag, -1.4473816083541016115e-91, tol=PTOL) + v = fp.e1((320.0 - 80.0j)) + assert ae(v, (4.2737871527778786157e-143 - 3.1789935525785660314e-142j), tol=ATOL) + assert ae(v.real, 4.2737871527778786157e-143, tol=PTOL) + assert ae(v.imag, -3.1789935525785660314e-142, tol=PTOL) + v = fp.ei(1.1641532182693481445e-10) + assert ae(v, -22.296641293460247028, tol=ATOL) + assert type(v) is float + v = fp.ei(0.25) + assert ae(v, -0.54254326466191372953, tol=ATOL) + assert type(v) is float + v = fp.ei(1.0) + assert ae(v, 1.8951178163559367555, tol=ATOL) + assert type(v) is float + v = fp.ei(2.0) + assert ae(v, 4.9542343560018901634, tol=ATOL) + assert type(v) is float + v = fp.ei(5.0) + assert ae(v, 40.185275355803177455, tol=ATOL) + assert type(v) is float + v = fp.ei(20.0) + assert ae(v, 25615652.66405658882, tol=ATOL) + assert type(v) is float + v = fp.ei(30.0) + assert ae(v, 368973209407.27419706, tol=ATOL) + assert type(v) is float + v = fp.ei(40.0) + assert ae(v, 6039718263611241.5784, tol=ATOL) + assert type(v) is float + v = fp.ei(50.0) + assert ae(v, 1.0585636897131690963e+20, tol=ATOL) + assert type(v) is float + v = fp.ei(80.0) + assert ae(v, 7.0146000049047999696e+32, tol=ATOL) + assert type(v) is float + v = fp.ei((1.1641532182693481445e-10 + 0.0j)) + assert ae(v, (-22.296641293460247028 + 0.0j), tol=ATOL) + assert ae(v.real, -22.296641293460247028, tol=PTOL) + assert v.imag == 0 + v = fp.ei((0.25 + 0.0j)) + assert ae(v, (-0.54254326466191372953 + 0.0j), tol=ATOL) + assert ae(v.real, -0.54254326466191372953, tol=PTOL) + assert v.imag == 0 + v = fp.ei((1.0 + 0.0j)) + assert ae(v, (1.8951178163559367555 + 0.0j), tol=ATOL) + assert ae(v.real, 1.8951178163559367555, tol=PTOL) + assert v.imag == 0 + v = fp.ei((2.0 + 0.0j)) + assert ae(v, (4.9542343560018901634 + 0.0j), tol=ATOL) + assert ae(v.real, 4.9542343560018901634, tol=PTOL) + assert v.imag == 0 + v = fp.ei((5.0 + 0.0j)) + assert ae(v, (40.185275355803177455 + 0.0j), tol=ATOL) + assert ae(v.real, 40.185275355803177455, tol=PTOL) + assert v.imag == 0 + v = fp.ei((20.0 + 0.0j)) + assert ae(v, (25615652.66405658882 + 0.0j), tol=ATOL) + assert ae(v.real, 25615652.66405658882, tol=PTOL) + assert v.imag == 0 + v = fp.ei((30.0 + 0.0j)) + assert ae(v, (368973209407.27419706 + 0.0j), tol=ATOL) + assert ae(v.real, 368973209407.27419706, tol=PTOL) + assert v.imag == 0 + v = fp.ei((40.0 + 0.0j)) + assert ae(v, (6039718263611241.5784 + 0.0j), tol=ATOL) + assert ae(v.real, 6039718263611241.5784, tol=PTOL) + assert v.imag == 0 + v = fp.ei((50.0 + 0.0j)) + assert ae(v, (1.0585636897131690963e+20 + 0.0j), tol=ATOL) + assert ae(v.real, 1.0585636897131690963e+20, tol=PTOL) + assert v.imag == 0 + v = fp.ei((80.0 + 0.0j)) + assert ae(v, (7.0146000049047999696e+32 + 0.0j), tol=ATOL) + assert ae(v.real, 7.0146000049047999696e+32, tol=PTOL) + assert v.imag == 0 + v = fp.ei((4.6566128730773925781e-10 + 1.1641532182693481445e-10j)) + assert ae(v, (-20.880034621082893023 + 0.24497866324327947603j), tol=ATOL) + assert ae(v.real, -20.880034621082893023, tol=PTOL) + assert ae(v.imag, 0.24497866324327947603, tol=PTOL) + v = fp.ei((1.0 + 0.25j)) + assert ae(v, (1.8942716983721074932 + 0.67268237088273915854j), tol=ATOL) + assert ae(v.real, 1.8942716983721074932, tol=PTOL) + assert ae(v.imag, 0.67268237088273915854, tol=PTOL) + v = fp.ei((4.0 + 1.0j)) + assert ae(v, (14.806699492675420438 + 12.280015176673582616j), tol=ATOL) + assert ae(v.real, 14.806699492675420438, tol=PTOL) + assert ae(v.imag, 12.280015176673582616, tol=PTOL) + v = fp.ei((8.0 + 2.0j)) + assert ae(v, (-54.633252667426386294 + 416.34477429173650012j), tol=ATOL) + assert ae(v.real, -54.633252667426386294, tol=PTOL) + assert ae(v.imag, 416.34477429173650012, tol=PTOL) + v = fp.ei((20.0 + 5.0j)) + assert ae(v, (711836.97165402624643 - 24745247.798103247366j), tol=ATOL) + assert ae(v.real, 711836.97165402624643, tol=PTOL) + assert ae(v.imag, -24745247.798103247366, tol=PTOL) + v = fp.ei((80.0 + 20.0j)) + assert ae(v, (4.2139911108612653091e+32 + 5.3367124741918251637e+32j), tol=ATOL) + assert ae(v.real, 4.2139911108612653091e+32, tol=PTOL) + assert ae(v.imag, 5.3367124741918251637e+32, tol=PTOL) + v = fp.ei((120.0 + 30.0j)) + assert ae(v, (-9.7760616203707508892e+48 - 1.058257682317195792e+50j), tol=ATOL) + assert ae(v.real, -9.7760616203707508892e+48, tol=PTOL) + assert ae(v.imag, -1.058257682317195792e+50, tol=PTOL) + v = fp.ei((160.0 + 40.0j)) + assert ae(v, (-8.7065541466623638861e+66 + 1.6577106725141739889e+67j), tol=ATOL) + assert ae(v.real, -8.7065541466623638861e+66, tol=PTOL) + assert ae(v.imag, 1.6577106725141739889e+67, tol=PTOL) + v = fp.ei((200.0 + 50.0j)) + assert ae(v, (3.070744996327018106e+84 - 1.7243244846769415903e+84j), tol=ATOL) + assert ae(v.real, 3.070744996327018106e+84, tol=PTOL) + assert ae(v.imag, -1.7243244846769415903e+84, tol=PTOL) + v = fp.ei((320.0 + 80.0j)) + assert ae(v, (-9.9960598637998647276e+135 - 2.6855081527595608863e+136j), tol=ATOL) + assert ae(v.real, -9.9960598637998647276e+135, tol=PTOL) + assert ae(v.imag, -2.6855081527595608863e+136, tol=PTOL) + v = fp.ei((1.1641532182693481445e-10 + 1.1641532182693481445e-10j)) + assert ae(v, (-21.950067703180274374 + 0.78539816351386363145j), tol=ATOL) + assert ae(v.real, -21.950067703180274374, tol=PTOL) + assert ae(v.imag, 0.78539816351386363145, tol=PTOL) + v = fp.ei((0.25 + 0.25j)) + assert ae(v, (-0.21441047326710323254 + 1.0683772981589995996j), tol=ATOL) + assert ae(v.real, -0.21441047326710323254, tol=PTOL) + assert ae(v.imag, 1.0683772981589995996, tol=PTOL) + v = fp.ei((1.0 + 1.0j)) + assert ae(v, (1.7646259855638540684 + 2.3877698515105224193j), tol=ATOL) + assert ae(v.real, 1.7646259855638540684, tol=PTOL) + assert ae(v.imag, 2.3877698515105224193, tol=PTOL) + v = fp.ei((2.0 + 2.0j)) + assert ae(v, (1.8920781621855474089 + 5.3169624378326579621j), tol=ATOL) + assert ae(v.real, 1.8920781621855474089, tol=PTOL) + assert ae(v.imag, 5.3169624378326579621, tol=PTOL) + v = fp.ei((5.0 + 5.0j)) + assert ae(v, (-13.470936071475245856 - 15.322492395731230968j), tol=ATOL) + assert ae(v.real, -13.470936071475245856, tol=PTOL) + assert ae(v.imag, -15.322492395731230968, tol=PTOL) + v = fp.ei((20.0 + 20.0j)) + assert ae(v, (16589317.398788971896 + 5831705.4712368307104j), tol=ATOL) + assert ae(v.real, 16589317.398788971896, tol=PTOL) + assert ae(v.imag, 5831705.4712368307104, tol=PTOL) + v = fp.ei((30.0 + 30.0j)) + assert ae(v, (-154596484273.69322527 - 204179357834.2723043j), tol=ATOL) + assert ae(v.real, -154596484273.69322527, tol=PTOL) + assert ae(v.imag, -204179357834.2723043, tol=PTOL) + v = fp.ei((40.0 + 40.0j)) + assert ae(v, (287512180321448.45408 + 4203502407932318.1156j), tol=ATOL) + assert ae(v.real, 287512180321448.45408, tol=PTOL) + assert ae(v.imag, 4203502407932318.1156, tol=PTOL) + v = fp.ei((50.0 + 50.0j)) + assert ae(v, (36128528616649268826.0 - 64648801861338741960.0j), tol=ATOL) + assert ae(v.real, 36128528616649268826.0, tol=PTOL) + assert ae(v.imag, -64648801861338741960.0, tol=PTOL) + v = fp.ei((80.0 + 80.0j)) + assert ae(v, (-3.8674816337930010217e+32 - 3.0540709639658071041e+32j), tol=ATOL) + assert ae(v.real, -3.8674816337930010217e+32, tol=PTOL) + assert ae(v.imag, -3.0540709639658071041e+32, tol=PTOL) + v = fp.ei((1.1641532182693481445e-10 + 4.6566128730773925781e-10j)) + assert ae(v, (-20.880034621432138988 + 1.3258176641336937524j), tol=ATOL) + assert ae(v.real, -20.880034621432138988, tol=PTOL) + assert ae(v.imag, 1.3258176641336937524, tol=PTOL) + v = fp.ei((0.25 + 1.0j)) + assert ae(v, (0.59066621214766308594 + 2.3968481059377428687j), tol=ATOL) + assert ae(v.real, 0.59066621214766308594, tol=PTOL) + assert ae(v.imag, 2.3968481059377428687, tol=PTOL) + v = fp.ei((1.0 + 4.0j)) + assert ae(v, (-0.49739047283060471093 + 3.5570287076301818702j), tol=ATOL) + assert ae(v.real, -0.49739047283060471093, tol=PTOL) + assert ae(v.imag, 3.5570287076301818702, tol=PTOL) + v = fp.ei((2.0 + 8.0j)) + assert ae(v, (0.8705211147733730969 + 3.3825859385758486351j), tol=ATOL) + assert ae(v.real, 0.8705211147733730969, tol=PTOL) + assert ae(v.imag, 3.3825859385758486351, tol=PTOL) + v = fp.ei((5.0 + 20.0j)) + assert ae(v, (7.0789514293925893007 + 1.5313749363937141849j), tol=ATOL) + assert ae(v.real, 7.0789514293925893007, tol=PTOL) + assert ae(v.imag, 1.5313749363937141849, tol=PTOL) + v = fp.ei((20.0 + 80.0j)) + assert ae(v, (-5855431.4907298084434 - 720917.79156143806727j), tol=ATOL) + assert ae(v.real, -5855431.4907298084434, tol=PTOL) + assert ae(v.imag, -720917.79156143806727, tol=PTOL) + v = fp.ei((30.0 + 120.0j)) + assert ae(v, (65402491644.703470747 - 56697658396.51586764j), tol=ATOL) + assert ae(v.real, 65402491644.703470747, tol=PTOL) + assert ae(v.imag, -56697658396.51586764, tol=PTOL) + v = fp.ei((40.0 + 160.0j)) + assert ae(v, (-25504929379604.776769 + 1429035198630576.3879j), tol=ATOL) + assert ae(v.real, -25504929379604.776769, tol=PTOL) + assert ae(v.imag, 1429035198630576.3879, tol=PTOL) + v = fp.ei((50.0 + 200.0j)) + assert ae(v, (-18437746526988116954.0 - 17146362239046152342.0j), tol=ATOL) + assert ae(v.real, -18437746526988116954.0, tol=PTOL) + assert ae(v.imag, -17146362239046152342.0, tol=PTOL) + v = fp.ei((80.0 + 320.0j)) + assert ae(v, (-3.3464697299634526706e+31 - 1.6473152633843023919e+32j), tol=ATOL) + assert ae(v.real, -3.3464697299634526706e+31, tol=PTOL) + assert ae(v.imag, -1.6473152633843023919e+32, tol=PTOL) + v = fp.ei((0.0 + 1.1641532182693481445e-10j)) + assert ae(v, (-22.29664129357666235 + 1.5707963269113119411j), tol=ATOL) + assert ae(v.real, -22.29664129357666235, tol=PTOL) + assert ae(v.imag, 1.5707963269113119411, tol=PTOL) + v = fp.ei((0.0 + 0.25j)) + assert ae(v, (-0.82466306258094565309 + 1.8199298971146537833j), tol=ATOL) + assert ae(v.real, -0.82466306258094565309, tol=PTOL) + assert ae(v.imag, 1.8199298971146537833, tol=PTOL) + v = fp.ei((0.0 + 1.0j)) + assert ae(v, (0.33740392290096813466 + 2.5168793971620796342j), tol=ATOL) + assert ae(v.real, 0.33740392290096813466, tol=PTOL) + assert ae(v.imag, 2.5168793971620796342, tol=PTOL) + v = fp.ei((0.0 + 2.0j)) + assert ae(v, (0.4229808287748649957 + 3.1762093035975914678j), tol=ATOL) + assert ae(v.real, 0.4229808287748649957, tol=PTOL) + assert ae(v.imag, 3.1762093035975914678, tol=PTOL) + v = fp.ei((0.0 + 5.0j)) + assert ae(v, (-0.19002974965664387862 + 3.1207275717395707565j), tol=ATOL) + assert ae(v.real, -0.19002974965664387862, tol=PTOL) + assert ae(v.imag, 3.1207275717395707565, tol=PTOL) + v = fp.ei((0.0 + 20.0j)) + assert ae(v, (0.04441982084535331654 + 3.1190380278383364594j), tol=ATOL) + assert ae(v.real, 0.04441982084535331654, tol=PTOL) + assert ae(v.imag, 3.1190380278383364594, tol=PTOL) + v = fp.ei((0.0 + 30.0j)) + assert ae(v, (-0.033032417282071143779 + 3.1375528668252477302j), tol=ATOL) + assert ae(v.real, -0.033032417282071143779, tol=PTOL) + assert ae(v.imag, 3.1375528668252477302, tol=PTOL) + v = fp.ei((0.0 + 40.0j)) + assert ae(v, (0.019020007896208766962 + 3.157781446149681126j), tol=ATOL) + assert ae(v.real, 0.019020007896208766962, tol=PTOL) + assert ae(v.imag, 3.157781446149681126, tol=PTOL) + v = fp.ei((0.0 + 50.0j)) + assert ae(v, (-0.0056283863241163054402 + 3.122413399280832514j), tol=ATOL) + assert ae(v.real, -0.0056283863241163054402, tol=PTOL) + assert ae(v.imag, 3.122413399280832514, tol=PTOL) + v = fp.ei((0.0 + 80.0j)) + assert ae(v, (-0.012402501155070958192 + 3.1431272137073839346j), tol=ATOL) + assert ae(v.real, -0.012402501155070958192, tol=PTOL) + assert ae(v.imag, 3.1431272137073839346, tol=PTOL) + v = fp.ei((-1.1641532182693481445e-10 + 4.6566128730773925781e-10j)) + assert ae(v, (-20.880034621664969632 + 1.8157749903874220607j), tol=ATOL) + assert ae(v.real, -20.880034621664969632, tol=PTOL) + assert ae(v.imag, 1.8157749903874220607, tol=PTOL) + v = fp.ei((-0.25 + 1.0j)) + assert ae(v, (0.16868306393667788761 + 2.6557914649950505414j), tol=ATOL) + assert ae(v.real, 0.16868306393667788761, tol=PTOL) + assert ae(v.imag, 2.6557914649950505414, tol=PTOL) + v = fp.ei((-1.0 + 4.0j)) + assert ae(v, (-0.03373591813926547318 + 3.2151161058308770603j), tol=ATOL) + assert ae(v.real, -0.03373591813926547318, tol=PTOL) + assert ae(v.imag, 3.2151161058308770603, tol=PTOL) + v = fp.ei((-2.0 + 8.0j)) + assert ae(v, (0.015392833434733785143 + 3.1384179414340326969j), tol=ATOL) + assert ae(v.real, 0.015392833434733785143, tol=PTOL) + assert ae(v.imag, 3.1384179414340326969, tol=PTOL) + v = fp.ei((-5.0 + 20.0j)) + assert ae(v, (0.00024419662286542966525 + 3.1413825703601317109j), tol=ATOL) + assert ae(v.real, 0.00024419662286542966525, tol=PTOL) + assert ae(v.imag, 3.1413825703601317109, tol=PTOL) + v = fp.ei((-20.0 + 80.0j)) + assert ae(v, (-2.3255552781051330088e-11 + 3.1415926535987396304j), tol=ATOL) + assert ae(v.real, -2.3255552781051330088e-11, tol=PTOL) + assert ae(v.imag, 3.1415926535987396304, tol=PTOL) + v = fp.ei((-30.0 + 120.0j)) + assert ae(v, (2.7068919097124652332e-16 + 3.1415926535897925337j), tol=ATOL) + assert ae(v.real, 2.7068919097124652332e-16, tol=PTOL) + assert ae(v.imag, 3.1415926535897925337, tol=PTOL) + v = fp.ei((-40.0 + 160.0j)) + assert ae(v, (1.1695597827678024687e-20 + 3.1415926535897932385j), tol=ATOL) + assert ae(v.real, 1.1695597827678024687e-20, tol=PTOL) + assert ae(v.imag, 3.1415926535897932385, tol=PTOL) + v = fp.ei((-50.0 + 200.0j)) + assert ae(v, (-9.0323746914410162531e-25 + 3.1415926535897932385j), tol=ATOL) + assert ae(v.real, -9.0323746914410162531e-25, tol=PTOL) + assert ae(v.imag, 3.1415926535897932385, tol=PTOL) + v = fp.ei((-80.0 + 320.0j)) + assert ae(v, (-3.4819106748728063576e-38 + 3.1415926535897932385j), tol=ATOL) + assert ae(v.real, -3.4819106748728063576e-38, tol=PTOL) + assert ae(v.imag, 3.1415926535897932385, tol=PTOL) + v = fp.ei((-4.6566128730773925781e-10 + 1.1641532182693481445e-10j)) + assert ae(v, (-20.880034622014215597 + 2.8966139905793444061j), tol=ATOL) + assert ae(v.real, -20.880034622014215597, tol=PTOL) + assert ae(v.imag, 2.8966139905793444061, tol=PTOL) + v = fp.ei((-1.0 + 0.25j)) + assert ae(v, (-0.19731063945004229095 + 3.0542266078154932748j), tol=ATOL) + assert ae(v.real, -0.19731063945004229095, tol=PTOL) + assert ae(v.imag, 3.0542266078154932748, tol=PTOL) + v = fp.ei((-4.0 + 1.0j)) + assert ae(v, (-0.0013106173980145506944 + 3.1381384055698581758j), tol=ATOL) + assert ae(v.real, -0.0013106173980145506944, tol=PTOL) + assert ae(v.imag, 3.1381384055698581758, tol=PTOL) + v = fp.ei((-8.0 + 2.0j)) + assert ae(v, (0.000022278049065270225945 + 3.1415634616493367169j), tol=ATOL) + assert ae(v.real, 0.000022278049065270225945, tol=PTOL) + assert ae(v.imag, 3.1415634616493367169, tol=PTOL) + v = fp.ei((-20.0 + 5.0j)) + assert ae(v, (-4.7711374515765346894e-11 + 3.1415926536726958909j), tol=ATOL) + assert ae(v.real, -4.7711374515765346894e-11, tol=PTOL) + assert ae(v.imag, 3.1415926536726958909, tol=PTOL) + v = fp.ei((-80.0 + 20.0j)) + assert ae(v, (-3.8353473865788235787e-38 + 3.1415926535897932385j), tol=ATOL) + assert ae(v.real, -3.8353473865788235787e-38, tol=PTOL) + assert ae(v.imag, 3.1415926535897932385, tol=PTOL) + v = fp.ei((-120.0 + 30.0j)) + assert ae(v, (-2.3836002337480334716e-55 + 3.1415926535897932385j), tol=ATOL) + assert ae(v.real, -2.3836002337480334716e-55, tol=PTOL) + assert ae(v.imag, 3.1415926535897932385, tol=PTOL) + v = fp.ei((-160.0 + 40.0j)) + assert ae(v, (1.6238022898654510661e-72 + 3.1415926535897932385j), tol=ATOL) + assert ae(v.real, 1.6238022898654510661e-72, tol=PTOL) + assert ae(v.imag, 3.1415926535897932385, tol=PTOL) + v = fp.ei((-200.0 + 50.0j)) + assert ae(v, (-6.6800061461666228487e-90 + 3.1415926535897932385j), tol=ATOL) + assert ae(v.real, -6.6800061461666228487e-90, tol=PTOL) + assert ae(v.imag, 3.1415926535897932385, tol=PTOL) + v = fp.ei((-320.0 + 80.0j)) + assert ae(v, (-4.2737871527778786157e-143 + 3.1415926535897932385j), tol=ATOL) + assert ae(v.real, -4.2737871527778786157e-143, tol=PTOL) + assert ae(v.imag, 3.1415926535897932385, tol=PTOL) + v = fp.ei(-1.1641532182693481445e-10) + assert ae(v, -22.296641293693077672, tol=ATOL) + assert type(v) is float + v = fp.ei(-0.25) + assert ae(v, -1.0442826344437381945, tol=ATOL) + assert type(v) is float + v = fp.ei(-1.0) + assert ae(v, -0.21938393439552027368, tol=ATOL) + assert type(v) is float + v = fp.ei(-2.0) + assert ae(v, -0.048900510708061119567, tol=ATOL) + assert type(v) is float + v = fp.ei(-5.0) + assert ae(v, -0.0011482955912753257973, tol=ATOL) + assert type(v) is float + v = fp.ei(-20.0) + assert ae(v, -9.8355252906498816904e-11, tol=ATOL) + assert type(v) is float + v = fp.ei(-30.0) + assert ae(v, -3.0215520106888125448e-15, tol=ATOL) + assert type(v) is float + v = fp.ei(-40.0) + assert ae(v, -1.0367732614516569722e-19, tol=ATOL) + assert type(v) is float + v = fp.ei(-50.0) + assert ae(v, -3.7832640295504590187e-24, tol=ATOL) + assert type(v) is float + v = fp.ei(-80.0) + assert ae(v, -2.2285432586884729112e-37, tol=ATOL) + assert type(v) is float + v = fp.ei((-1.1641532182693481445e-10 + 0.0j)) + assert ae(v, (-22.296641293693077672 + 0.0j), tol=ATOL) + assert ae(v.real, -22.296641293693077672, tol=PTOL) + assert v.imag == 0 + v = fp.ei((-0.25 + 0.0j)) + assert ae(v, (-1.0442826344437381945 + 0.0j), tol=ATOL) + assert ae(v.real, -1.0442826344437381945, tol=PTOL) + assert v.imag == 0 + v = fp.ei((-1.0 + 0.0j)) + assert ae(v, (-0.21938393439552027368 + 0.0j), tol=ATOL) + assert ae(v.real, -0.21938393439552027368, tol=PTOL) + assert v.imag == 0 + v = fp.ei((-2.0 + 0.0j)) + assert ae(v, (-0.048900510708061119567 + 0.0j), tol=ATOL) + assert ae(v.real, -0.048900510708061119567, tol=PTOL) + assert v.imag == 0 + v = fp.ei((-5.0 + 0.0j)) + assert ae(v, (-0.0011482955912753257973 + 0.0j), tol=ATOL) + assert ae(v.real, -0.0011482955912753257973, tol=PTOL) + assert v.imag == 0 + v = fp.ei((-20.0 + 0.0j)) + assert ae(v, (-9.8355252906498816904e-11 + 0.0j), tol=ATOL) + assert ae(v.real, -9.8355252906498816904e-11, tol=PTOL) + assert v.imag == 0 + v = fp.ei((-30.0 + 0.0j)) + assert ae(v, (-3.0215520106888125448e-15 + 0.0j), tol=ATOL) + assert ae(v.real, -3.0215520106888125448e-15, tol=PTOL) + assert v.imag == 0 + v = fp.ei((-40.0 + 0.0j)) + assert ae(v, (-1.0367732614516569722e-19 + 0.0j), tol=ATOL) + assert ae(v.real, -1.0367732614516569722e-19, tol=PTOL) + assert v.imag == 0 + v = fp.ei((-50.0 + 0.0j)) + assert ae(v, (-3.7832640295504590187e-24 + 0.0j), tol=ATOL) + assert ae(v.real, -3.7832640295504590187e-24, tol=PTOL) + assert v.imag == 0 + v = fp.ei((-80.0 + 0.0j)) + assert ae(v, (-2.2285432586884729112e-37 + 0.0j), tol=ATOL) + assert ae(v.real, -2.2285432586884729112e-37, tol=PTOL) + assert v.imag == 0 + v = fp.ei((-4.6566128730773925781e-10 - 1.1641532182693481445e-10j)) + assert ae(v, (-20.880034622014215597 - 2.8966139905793444061j), tol=ATOL) + assert ae(v.real, -20.880034622014215597, tol=PTOL) + assert ae(v.imag, -2.8966139905793444061, tol=PTOL) + v = fp.ei((-1.0 - 0.25j)) + assert ae(v, (-0.19731063945004229095 - 3.0542266078154932748j), tol=ATOL) + assert ae(v.real, -0.19731063945004229095, tol=PTOL) + assert ae(v.imag, -3.0542266078154932748, tol=PTOL) + v = fp.ei((-4.0 - 1.0j)) + assert ae(v, (-0.0013106173980145506944 - 3.1381384055698581758j), tol=ATOL) + assert ae(v.real, -0.0013106173980145506944, tol=PTOL) + assert ae(v.imag, -3.1381384055698581758, tol=PTOL) + v = fp.ei((-8.0 - 2.0j)) + assert ae(v, (0.000022278049065270225945 - 3.1415634616493367169j), tol=ATOL) + assert ae(v.real, 0.000022278049065270225945, tol=PTOL) + assert ae(v.imag, -3.1415634616493367169, tol=PTOL) + v = fp.ei((-20.0 - 5.0j)) + assert ae(v, (-4.7711374515765346894e-11 - 3.1415926536726958909j), tol=ATOL) + assert ae(v.real, -4.7711374515765346894e-11, tol=PTOL) + assert ae(v.imag, -3.1415926536726958909, tol=PTOL) + v = fp.ei((-80.0 - 20.0j)) + assert ae(v, (-3.8353473865788235787e-38 - 3.1415926535897932385j), tol=ATOL) + assert ae(v.real, -3.8353473865788235787e-38, tol=PTOL) + assert ae(v.imag, -3.1415926535897932385, tol=PTOL) + v = fp.ei((-120.0 - 30.0j)) + assert ae(v, (-2.3836002337480334716e-55 - 3.1415926535897932385j), tol=ATOL) + assert ae(v.real, -2.3836002337480334716e-55, tol=PTOL) + assert ae(v.imag, -3.1415926535897932385, tol=PTOL) + v = fp.ei((-160.0 - 40.0j)) + assert ae(v, (1.6238022898654510661e-72 - 3.1415926535897932385j), tol=ATOL) + assert ae(v.real, 1.6238022898654510661e-72, tol=PTOL) + assert ae(v.imag, -3.1415926535897932385, tol=PTOL) + v = fp.ei((-200.0 - 50.0j)) + assert ae(v, (-6.6800061461666228487e-90 - 3.1415926535897932385j), tol=ATOL) + assert ae(v.real, -6.6800061461666228487e-90, tol=PTOL) + assert ae(v.imag, -3.1415926535897932385, tol=PTOL) + v = fp.ei((-320.0 - 80.0j)) + assert ae(v, (-4.2737871527778786157e-143 - 3.1415926535897932385j), tol=ATOL) + assert ae(v.real, -4.2737871527778786157e-143, tol=PTOL) + assert ae(v.imag, -3.1415926535897932385, tol=PTOL) + v = fp.ei((-1.1641532182693481445e-10 - 1.1641532182693481445e-10j)) + assert ae(v, (-21.950067703413105017 - 2.3561944903087602507j), tol=ATOL) + assert ae(v.real, -21.950067703413105017, tol=PTOL) + assert ae(v.imag, -2.3561944903087602507, tol=PTOL) + v = fp.ei((-0.25 - 0.25j)) + assert ae(v, (-0.71092525792923287894 - 2.5766745291767512913j), tol=ATOL) + assert ae(v.real, -0.71092525792923287894, tol=PTOL) + assert ae(v.imag, -2.5766745291767512913, tol=PTOL) + v = fp.ei((-1.0 - 1.0j)) + assert ae(v, (-0.00028162445198141832551 - 2.9622681185504342983j), tol=ATOL) + assert ae(v.real, -0.00028162445198141832551, tol=PTOL) + assert ae(v.imag, -2.9622681185504342983, tol=PTOL) + v = fp.ei((-2.0 - 2.0j)) + assert ae(v, (0.033767089606562004246 - 3.1229932394200426965j), tol=ATOL) + assert ae(v.real, 0.033767089606562004246, tol=PTOL) + assert ae(v.imag, -3.1229932394200426965, tol=PTOL) + v = fp.ei((-5.0 - 5.0j)) + assert ae(v, (-0.0007266506660356393891 - 3.1420636813914284609j), tol=ATOL) + assert ae(v.real, -0.0007266506660356393891, tol=PTOL) + assert ae(v.imag, -3.1420636813914284609, tol=PTOL) + v = fp.ei((-20.0 - 20.0j)) + assert ae(v, (2.3824537449367396579e-11 - 3.1415926535228233653j), tol=ATOL) + assert ae(v.real, 2.3824537449367396579e-11, tol=PTOL) + assert ae(v.imag, -3.1415926535228233653, tol=PTOL) + v = fp.ei((-30.0 - 30.0j)) + assert ae(v, (-1.7316045841744061617e-15 - 3.141592653589794545j), tol=ATOL) + assert ae(v.real, -1.7316045841744061617e-15, tol=PTOL) + assert ae(v.imag, -3.141592653589794545, tol=PTOL) + v = fp.ei((-40.0 - 40.0j)) + assert ae(v, (7.4001043002899232182e-20 - 3.1415926535897932385j), tol=ATOL) + assert ae(v.real, 7.4001043002899232182e-20, tol=PTOL) + assert ae(v.imag, -3.1415926535897932385, tol=PTOL) + v = fp.ei((-50.0 - 50.0j)) + assert ae(v, (-2.3566128324644641219e-24 - 3.1415926535897932385j), tol=ATOL) + assert ae(v.real, -2.3566128324644641219e-24, tol=PTOL) + assert ae(v.imag, -3.1415926535897932385, tol=PTOL) + v = fp.ei((-80.0 - 80.0j)) + assert ae(v, (-9.8279750572186526673e-38 - 3.1415926535897932385j), tol=ATOL) + assert ae(v.real, -9.8279750572186526673e-38, tol=PTOL) + assert ae(v.imag, -3.1415926535897932385, tol=PTOL) + v = fp.ei((-1.1641532182693481445e-10 - 4.6566128730773925781e-10j)) + assert ae(v, (-20.880034621664969632 - 1.8157749903874220607j), tol=ATOL) + assert ae(v.real, -20.880034621664969632, tol=PTOL) + assert ae(v.imag, -1.8157749903874220607, tol=PTOL) + v = fp.ei((-0.25 - 1.0j)) + assert ae(v, (0.16868306393667788761 - 2.6557914649950505414j), tol=ATOL) + assert ae(v.real, 0.16868306393667788761, tol=PTOL) + assert ae(v.imag, -2.6557914649950505414, tol=PTOL) + v = fp.ei((-1.0 - 4.0j)) + assert ae(v, (-0.03373591813926547318 - 3.2151161058308770603j), tol=ATOL) + assert ae(v.real, -0.03373591813926547318, tol=PTOL) + assert ae(v.imag, -3.2151161058308770603, tol=PTOL) + v = fp.ei((-2.0 - 8.0j)) + assert ae(v, (0.015392833434733785143 - 3.1384179414340326969j), tol=ATOL) + assert ae(v.real, 0.015392833434733785143, tol=PTOL) + assert ae(v.imag, -3.1384179414340326969, tol=PTOL) + v = fp.ei((-5.0 - 20.0j)) + assert ae(v, (0.00024419662286542966525 - 3.1413825703601317109j), tol=ATOL) + assert ae(v.real, 0.00024419662286542966525, tol=PTOL) + assert ae(v.imag, -3.1413825703601317109, tol=PTOL) + v = fp.ei((-20.0 - 80.0j)) + assert ae(v, (-2.3255552781051330088e-11 - 3.1415926535987396304j), tol=ATOL) + assert ae(v.real, -2.3255552781051330088e-11, tol=PTOL) + assert ae(v.imag, -3.1415926535987396304, tol=PTOL) + v = fp.ei((-30.0 - 120.0j)) + assert ae(v, (2.7068919097124652332e-16 - 3.1415926535897925337j), tol=ATOL) + assert ae(v.real, 2.7068919097124652332e-16, tol=PTOL) + assert ae(v.imag, -3.1415926535897925337, tol=PTOL) + v = fp.ei((-40.0 - 160.0j)) + assert ae(v, (1.1695597827678024687e-20 - 3.1415926535897932385j), tol=ATOL) + assert ae(v.real, 1.1695597827678024687e-20, tol=PTOL) + assert ae(v.imag, -3.1415926535897932385, tol=PTOL) + v = fp.ei((-50.0 - 200.0j)) + assert ae(v, (-9.0323746914410162531e-25 - 3.1415926535897932385j), tol=ATOL) + assert ae(v.real, -9.0323746914410162531e-25, tol=PTOL) + assert ae(v.imag, -3.1415926535897932385, tol=PTOL) + v = fp.ei((-80.0 - 320.0j)) + assert ae(v, (-3.4819106748728063576e-38 - 3.1415926535897932385j), tol=ATOL) + assert ae(v.real, -3.4819106748728063576e-38, tol=PTOL) + assert ae(v.imag, -3.1415926535897932385, tol=PTOL) + v = fp.ei((0.0 - 1.1641532182693481445e-10j)) + assert ae(v, (-22.29664129357666235 - 1.5707963269113119411j), tol=ATOL) + assert ae(v.real, -22.29664129357666235, tol=PTOL) + assert ae(v.imag, -1.5707963269113119411, tol=PTOL) + v = fp.ei((0.0 - 0.25j)) + assert ae(v, (-0.82466306258094565309 - 1.8199298971146537833j), tol=ATOL) + assert ae(v.real, -0.82466306258094565309, tol=PTOL) + assert ae(v.imag, -1.8199298971146537833, tol=PTOL) + v = fp.ei((0.0 - 1.0j)) + assert ae(v, (0.33740392290096813466 - 2.5168793971620796342j), tol=ATOL) + assert ae(v.real, 0.33740392290096813466, tol=PTOL) + assert ae(v.imag, -2.5168793971620796342, tol=PTOL) + v = fp.ei((0.0 - 2.0j)) + assert ae(v, (0.4229808287748649957 - 3.1762093035975914678j), tol=ATOL) + assert ae(v.real, 0.4229808287748649957, tol=PTOL) + assert ae(v.imag, -3.1762093035975914678, tol=PTOL) + v = fp.ei((0.0 - 5.0j)) + assert ae(v, (-0.19002974965664387862 - 3.1207275717395707565j), tol=ATOL) + assert ae(v.real, -0.19002974965664387862, tol=PTOL) + assert ae(v.imag, -3.1207275717395707565, tol=PTOL) + v = fp.ei((0.0 - 20.0j)) + assert ae(v, (0.04441982084535331654 - 3.1190380278383364594j), tol=ATOL) + assert ae(v.real, 0.04441982084535331654, tol=PTOL) + assert ae(v.imag, -3.1190380278383364594, tol=PTOL) + v = fp.ei((0.0 - 30.0j)) + assert ae(v, (-0.033032417282071143779 - 3.1375528668252477302j), tol=ATOL) + assert ae(v.real, -0.033032417282071143779, tol=PTOL) + assert ae(v.imag, -3.1375528668252477302, tol=PTOL) + v = fp.ei((0.0 - 40.0j)) + assert ae(v, (0.019020007896208766962 - 3.157781446149681126j), tol=ATOL) + assert ae(v.real, 0.019020007896208766962, tol=PTOL) + assert ae(v.imag, -3.157781446149681126, tol=PTOL) + v = fp.ei((0.0 - 50.0j)) + assert ae(v, (-0.0056283863241163054402 - 3.122413399280832514j), tol=ATOL) + assert ae(v.real, -0.0056283863241163054402, tol=PTOL) + assert ae(v.imag, -3.122413399280832514, tol=PTOL) + v = fp.ei((0.0 - 80.0j)) + assert ae(v, (-0.012402501155070958192 - 3.1431272137073839346j), tol=ATOL) + assert ae(v.real, -0.012402501155070958192, tol=PTOL) + assert ae(v.imag, -3.1431272137073839346, tol=PTOL) + v = fp.ei((1.1641532182693481445e-10 - 4.6566128730773925781e-10j)) + assert ae(v, (-20.880034621432138988 - 1.3258176641336937524j), tol=ATOL) + assert ae(v.real, -20.880034621432138988, tol=PTOL) + assert ae(v.imag, -1.3258176641336937524, tol=PTOL) + v = fp.ei((0.25 - 1.0j)) + assert ae(v, (0.59066621214766308594 - 2.3968481059377428687j), tol=ATOL) + assert ae(v.real, 0.59066621214766308594, tol=PTOL) + assert ae(v.imag, -2.3968481059377428687, tol=PTOL) + v = fp.ei((1.0 - 4.0j)) + assert ae(v, (-0.49739047283060471093 - 3.5570287076301818702j), tol=ATOL) + assert ae(v.real, -0.49739047283060471093, tol=PTOL) + assert ae(v.imag, -3.5570287076301818702, tol=PTOL) + v = fp.ei((2.0 - 8.0j)) + assert ae(v, (0.8705211147733730969 - 3.3825859385758486351j), tol=ATOL) + assert ae(v.real, 0.8705211147733730969, tol=PTOL) + assert ae(v.imag, -3.3825859385758486351, tol=PTOL) + v = fp.ei((5.0 - 20.0j)) + assert ae(v, (7.0789514293925893007 - 1.5313749363937141849j), tol=ATOL) + assert ae(v.real, 7.0789514293925893007, tol=PTOL) + assert ae(v.imag, -1.5313749363937141849, tol=PTOL) + v = fp.ei((20.0 - 80.0j)) + assert ae(v, (-5855431.4907298084434 + 720917.79156143806727j), tol=ATOL) + assert ae(v.real, -5855431.4907298084434, tol=PTOL) + assert ae(v.imag, 720917.79156143806727, tol=PTOL) + v = fp.ei((30.0 - 120.0j)) + assert ae(v, (65402491644.703470747 + 56697658396.51586764j), tol=ATOL) + assert ae(v.real, 65402491644.703470747, tol=PTOL) + assert ae(v.imag, 56697658396.51586764, tol=PTOL) + v = fp.ei((40.0 - 160.0j)) + assert ae(v, (-25504929379604.776769 - 1429035198630576.3879j), tol=ATOL) + assert ae(v.real, -25504929379604.776769, tol=PTOL) + assert ae(v.imag, -1429035198630576.3879, tol=PTOL) + v = fp.ei((50.0 - 200.0j)) + assert ae(v, (-18437746526988116954.0 + 17146362239046152342.0j), tol=ATOL) + assert ae(v.real, -18437746526988116954.0, tol=PTOL) + assert ae(v.imag, 17146362239046152342.0, tol=PTOL) + v = fp.ei((80.0 - 320.0j)) + assert ae(v, (-3.3464697299634526706e+31 + 1.6473152633843023919e+32j), tol=ATOL) + assert ae(v.real, -3.3464697299634526706e+31, tol=PTOL) + assert ae(v.imag, 1.6473152633843023919e+32, tol=PTOL) + v = fp.ei((1.1641532182693481445e-10 - 1.1641532182693481445e-10j)) + assert ae(v, (-21.950067703180274374 - 0.78539816351386363145j), tol=ATOL) + assert ae(v.real, -21.950067703180274374, tol=PTOL) + assert ae(v.imag, -0.78539816351386363145, tol=PTOL) + v = fp.ei((0.25 - 0.25j)) + assert ae(v, (-0.21441047326710323254 - 1.0683772981589995996j), tol=ATOL) + assert ae(v.real, -0.21441047326710323254, tol=PTOL) + assert ae(v.imag, -1.0683772981589995996, tol=PTOL) + v = fp.ei((1.0 - 1.0j)) + assert ae(v, (1.7646259855638540684 - 2.3877698515105224193j), tol=ATOL) + assert ae(v.real, 1.7646259855638540684, tol=PTOL) + assert ae(v.imag, -2.3877698515105224193, tol=PTOL) + v = fp.ei((2.0 - 2.0j)) + assert ae(v, (1.8920781621855474089 - 5.3169624378326579621j), tol=ATOL) + assert ae(v.real, 1.8920781621855474089, tol=PTOL) + assert ae(v.imag, -5.3169624378326579621, tol=PTOL) + v = fp.ei((5.0 - 5.0j)) + assert ae(v, (-13.470936071475245856 + 15.322492395731230968j), tol=ATOL) + assert ae(v.real, -13.470936071475245856, tol=PTOL) + assert ae(v.imag, 15.322492395731230968, tol=PTOL) + v = fp.ei((20.0 - 20.0j)) + assert ae(v, (16589317.398788971896 - 5831705.4712368307104j), tol=ATOL) + assert ae(v.real, 16589317.398788971896, tol=PTOL) + assert ae(v.imag, -5831705.4712368307104, tol=PTOL) + v = fp.ei((30.0 - 30.0j)) + assert ae(v, (-154596484273.69322527 + 204179357834.2723043j), tol=ATOL) + assert ae(v.real, -154596484273.69322527, tol=PTOL) + assert ae(v.imag, 204179357834.2723043, tol=PTOL) + v = fp.ei((40.0 - 40.0j)) + assert ae(v, (287512180321448.45408 - 4203502407932318.1156j), tol=ATOL) + assert ae(v.real, 287512180321448.45408, tol=PTOL) + assert ae(v.imag, -4203502407932318.1156, tol=PTOL) + v = fp.ei((50.0 - 50.0j)) + assert ae(v, (36128528616649268826.0 + 64648801861338741960.0j), tol=ATOL) + assert ae(v.real, 36128528616649268826.0, tol=PTOL) + assert ae(v.imag, 64648801861338741960.0, tol=PTOL) + v = fp.ei((80.0 - 80.0j)) + assert ae(v, (-3.8674816337930010217e+32 + 3.0540709639658071041e+32j), tol=ATOL) + assert ae(v.real, -3.8674816337930010217e+32, tol=PTOL) + assert ae(v.imag, 3.0540709639658071041e+32, tol=PTOL) + v = fp.ei((4.6566128730773925781e-10 - 1.1641532182693481445e-10j)) + assert ae(v, (-20.880034621082893023 - 0.24497866324327947603j), tol=ATOL) + assert ae(v.real, -20.880034621082893023, tol=PTOL) + assert ae(v.imag, -0.24497866324327947603, tol=PTOL) + v = fp.ei((1.0 - 0.25j)) + assert ae(v, (1.8942716983721074932 - 0.67268237088273915854j), tol=ATOL) + assert ae(v.real, 1.8942716983721074932, tol=PTOL) + assert ae(v.imag, -0.67268237088273915854, tol=PTOL) + v = fp.ei((4.0 - 1.0j)) + assert ae(v, (14.806699492675420438 - 12.280015176673582616j), tol=ATOL) + assert ae(v.real, 14.806699492675420438, tol=PTOL) + assert ae(v.imag, -12.280015176673582616, tol=PTOL) + v = fp.ei((8.0 - 2.0j)) + assert ae(v, (-54.633252667426386294 - 416.34477429173650012j), tol=ATOL) + assert ae(v.real, -54.633252667426386294, tol=PTOL) + assert ae(v.imag, -416.34477429173650012, tol=PTOL) + v = fp.ei((20.0 - 5.0j)) + assert ae(v, (711836.97165402624643 + 24745247.798103247366j), tol=ATOL) + assert ae(v.real, 711836.97165402624643, tol=PTOL) + assert ae(v.imag, 24745247.798103247366, tol=PTOL) + v = fp.ei((80.0 - 20.0j)) + assert ae(v, (4.2139911108612653091e+32 - 5.3367124741918251637e+32j), tol=ATOL) + assert ae(v.real, 4.2139911108612653091e+32, tol=PTOL) + assert ae(v.imag, -5.3367124741918251637e+32, tol=PTOL) + v = fp.ei((120.0 - 30.0j)) + assert ae(v, (-9.7760616203707508892e+48 + 1.058257682317195792e+50j), tol=ATOL) + assert ae(v.real, -9.7760616203707508892e+48, tol=PTOL) + assert ae(v.imag, 1.058257682317195792e+50, tol=PTOL) + v = fp.ei((160.0 - 40.0j)) + assert ae(v, (-8.7065541466623638861e+66 - 1.6577106725141739889e+67j), tol=ATOL) + assert ae(v.real, -8.7065541466623638861e+66, tol=PTOL) + assert ae(v.imag, -1.6577106725141739889e+67, tol=PTOL) + v = fp.ei((200.0 - 50.0j)) + assert ae(v, (3.070744996327018106e+84 + 1.7243244846769415903e+84j), tol=ATOL) + assert ae(v.real, 3.070744996327018106e+84, tol=PTOL) + assert ae(v.imag, 1.7243244846769415903e+84, tol=PTOL) + v = fp.ei((320.0 - 80.0j)) + assert ae(v, (-9.9960598637998647276e+135 + 2.6855081527595608863e+136j), tol=ATOL) + assert ae(v.real, -9.9960598637998647276e+135, tol=PTOL) + assert ae(v.imag, 2.6855081527595608863e+136, tol=PTOL) diff --git a/lib/python3.12/site-packages/mpmath/tests/test_functions.py b/lib/python3.12/site-packages/mpmath/tests/test_functions.py new file mode 100644 index 0000000000000000000000000000000000000000..3bfe852f008173eb636c147abc83d71dbdd4d23a --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/tests/test_functions.py @@ -0,0 +1,920 @@ +from mpmath.libmp import * +from mpmath import * +import random +import time +import math +import cmath + +def mpc_ae(a, b, eps=eps): + res = True + res = res and a.real.ae(b.real, eps) + res = res and a.imag.ae(b.imag, eps) + return res + +#---------------------------------------------------------------------------- +# Constants and functions +# + +tpi = "3.1415926535897932384626433832795028841971693993751058209749445923078\ +1640628620899862803482534211706798" +te = "2.71828182845904523536028747135266249775724709369995957496696762772407\ +663035354759457138217852516642743" +tdegree = "0.017453292519943295769236907684886127134428718885417254560971914\ +4017100911460344944368224156963450948221" +teuler = "0.5772156649015328606065120900824024310421593359399235988057672348\ +84867726777664670936947063291746749516" +tln2 = "0.693147180559945309417232121458176568075500134360255254120680009493\ +393621969694715605863326996418687542" +tln10 = "2.30258509299404568401799145468436420760110148862877297603332790096\ +757260967735248023599720508959829834" +tcatalan = "0.91596559417721901505460351493238411077414937428167213426649811\ +9621763019776254769479356512926115106249" +tkhinchin = "2.6854520010653064453097148354817956938203822939944629530511523\ +4555721885953715200280114117493184769800" +tglaisher = "1.2824271291006226368753425688697917277676889273250011920637400\ +2174040630885882646112973649195820237439420646" +tapery = "1.2020569031595942853997381615114499907649862923404988817922715553\ +4183820578631309018645587360933525815" +tphi = "1.618033988749894848204586834365638117720309179805762862135448622705\ +26046281890244970720720418939113748475" +tmertens = "0.26149721284764278375542683860869585905156664826119920619206421\ +3924924510897368209714142631434246651052" +ttwinprime = "0.660161815846869573927812110014555778432623360284733413319448\ +423335405642304495277143760031413839867912" + +def test_constants(): + for prec in [3, 7, 10, 15, 20, 37, 80, 100, 29]: + mp.dps = prec + assert pi == mpf(tpi) + assert e == mpf(te) + assert degree == mpf(tdegree) + assert euler == mpf(teuler) + assert ln2 == mpf(tln2) + assert ln10 == mpf(tln10) + assert catalan == mpf(tcatalan) + assert khinchin == mpf(tkhinchin) + assert glaisher == mpf(tglaisher) + assert phi == mpf(tphi) + if prec < 50: + assert mertens == mpf(tmertens) + assert twinprime == mpf(ttwinprime) + mp.dps = 15 + assert pi >= -1 + assert pi > 2 + assert pi > 3 + assert pi < 4 + +def test_exact_sqrts(): + for i in range(20000): + assert sqrt(mpf(i*i)) == i + random.seed(1) + for prec in [100, 300, 1000, 10000]: + mp.dps = prec + for i in range(20): + A = random.randint(10**(prec//2-2), 10**(prec//2-1)) + assert sqrt(mpf(A*A)) == A + mp.dps = 15 + for i in range(100): + for a in [1, 8, 25, 112307]: + assert sqrt(mpf((a*a, 2*i))) == mpf((a, i)) + assert sqrt(mpf((a*a, -2*i))) == mpf((a, -i)) + +def test_sqrt_rounding(): + for i in [2, 3, 5, 6, 7, 8, 10, 11, 12, 13, 14, 15]: + i = from_int(i) + for dps in [7, 15, 83, 106, 2000]: + mp.dps = dps + a = mpf_pow_int(mpf_sqrt(i, mp.prec, round_down), 2, mp.prec, round_down) + b = mpf_pow_int(mpf_sqrt(i, mp.prec, round_up), 2, mp.prec, round_up) + assert mpf_lt(a, i) + assert mpf_gt(b, i) + random.seed(1234) + prec = 100 + for rnd in [round_down, round_nearest, round_ceiling]: + for i in range(100): + a = mpf_rand(prec) + b = mpf_mul(a, a) + assert mpf_sqrt(b, prec, rnd) == a + # Test some extreme cases + mp.dps = 100 + a = mpf(9) + 1e-90 + b = mpf(9) - 1e-90 + mp.dps = 15 + assert sqrt(a, rounding='d') == 3 + assert sqrt(a, rounding='n') == 3 + assert sqrt(a, rounding='u') > 3 + assert sqrt(b, rounding='d') < 3 + assert sqrt(b, rounding='n') == 3 + assert sqrt(b, rounding='u') == 3 + # A worst case, from the MPFR test suite + assert sqrt(mpf('7.0503726185518891')) == mpf('2.655253776675949') + +def test_float_sqrt(): + mp.dps = 15 + # These should round identically + for x in [0, 1e-7, 0.1, 0.5, 1, 2, 3, 4, 5, 0.333, 76.19]: + assert sqrt(mpf(x)) == float(x)**0.5 + assert sqrt(-1) == 1j + assert sqrt(-2).ae(cmath.sqrt(-2)) + assert sqrt(-3).ae(cmath.sqrt(-3)) + assert sqrt(-100).ae(cmath.sqrt(-100)) + assert sqrt(1j).ae(cmath.sqrt(1j)) + assert sqrt(-1j).ae(cmath.sqrt(-1j)) + assert sqrt(math.pi + math.e*1j).ae(cmath.sqrt(math.pi + math.e*1j)) + assert sqrt(math.pi - math.e*1j).ae(cmath.sqrt(math.pi - math.e*1j)) + +def test_hypot(): + assert hypot(0, 0) == 0 + assert hypot(0, 0.33) == mpf(0.33) + assert hypot(0.33, 0) == mpf(0.33) + assert hypot(-0.33, 0) == mpf(0.33) + assert hypot(3, 4) == mpf(5) + +def test_exact_cbrt(): + for i in range(0, 20000, 200): + assert cbrt(mpf(i*i*i)) == i + random.seed(1) + for prec in [100, 300, 1000, 10000]: + mp.dps = prec + A = random.randint(10**(prec//2-2), 10**(prec//2-1)) + assert cbrt(mpf(A*A*A)) == A + mp.dps = 15 + +def test_exp(): + assert exp(0) == 1 + assert exp(10000).ae(mpf('8.8068182256629215873e4342')) + assert exp(-10000).ae(mpf('1.1354838653147360985e-4343')) + a = exp(mpf((1, 8198646019315405, -53, 53))) + assert(a.bc == bitcount(a.man)) + mp.prec = 67 + a = exp(mpf((1, 1781864658064754565, -60, 61))) + assert(a.bc == bitcount(a.man)) + mp.prec = 53 + assert exp(ln2 * 10).ae(1024) + assert exp(2+2j).ae(cmath.exp(2+2j)) + +def test_issue_73(): + mp.dps = 512 + a = exp(-1) + b = exp(1) + mp.dps = 15 + assert (+a).ae(0.36787944117144233) + assert (+b).ae(2.7182818284590451) + +def test_log(): + mp.dps = 15 + assert log(1) == 0 + for x in [0.5, 1.5, 2.0, 3.0, 100, 10**50, 1e-50]: + assert log(x).ae(math.log(x)) + assert log(x, x) == 1 + assert log(1024, 2) == 10 + assert log(10**1234, 10) == 1234 + assert log(2+2j).ae(cmath.log(2+2j)) + # Accuracy near 1 + assert (log(0.6+0.8j).real*10**17).ae(2.2204460492503131) + assert (log(0.6-0.8j).real*10**17).ae(2.2204460492503131) + assert (log(0.8-0.6j).real*10**17).ae(2.2204460492503131) + assert (log(1+1e-8j).real*10**16).ae(0.5) + assert (log(1-1e-8j).real*10**16).ae(0.5) + assert (log(-1+1e-8j).real*10**16).ae(0.5) + assert (log(-1-1e-8j).real*10**16).ae(0.5) + assert (log(1j+1e-8).real*10**16).ae(0.5) + assert (log(1j-1e-8).real*10**16).ae(0.5) + assert (log(-1j+1e-8).real*10**16).ae(0.5) + assert (log(-1j-1e-8).real*10**16).ae(0.5) + assert (log(1+1e-40j).real*10**80).ae(0.5) + assert (log(1j+1e-40).real*10**80).ae(0.5) + # Huge + assert log(ldexp(1.234,10**20)).ae(log(2)*1e20) + assert log(ldexp(1.234,10**200)).ae(log(2)*1e200) + # Some special values + assert log(mpc(0,0)) == mpc(-inf,0) + assert isnan(log(mpc(nan,0)).real) + assert isnan(log(mpc(nan,0)).imag) + assert isnan(log(mpc(0,nan)).real) + assert isnan(log(mpc(0,nan)).imag) + assert isnan(log(mpc(nan,1)).real) + assert isnan(log(mpc(nan,1)).imag) + assert isnan(log(mpc(1,nan)).real) + assert isnan(log(mpc(1,nan)).imag) + +def test_trig_hyperb_basic(): + for x in (list(range(100)) + list(range(-100,0))): + t = x / 4.1 + assert cos(mpf(t)).ae(math.cos(t)) + assert sin(mpf(t)).ae(math.sin(t)) + assert tan(mpf(t)).ae(math.tan(t)) + assert cosh(mpf(t)).ae(math.cosh(t)) + assert sinh(mpf(t)).ae(math.sinh(t)) + assert tanh(mpf(t)).ae(math.tanh(t)) + assert sin(1+1j).ae(cmath.sin(1+1j)) + assert sin(-4-3.6j).ae(cmath.sin(-4-3.6j)) + assert cos(1+1j).ae(cmath.cos(1+1j)) + assert cos(-4-3.6j).ae(cmath.cos(-4-3.6j)) + +def test_degrees(): + assert cos(0*degree) == 1 + assert cos(90*degree).ae(0) + assert cos(180*degree).ae(-1) + assert cos(270*degree).ae(0) + assert cos(360*degree).ae(1) + assert sin(0*degree) == 0 + assert sin(90*degree).ae(1) + assert sin(180*degree).ae(0) + assert sin(270*degree).ae(-1) + assert sin(360*degree).ae(0) + +def random_complexes(N): + random.seed(1) + a = [] + for i in range(N): + x1 = random.uniform(-10, 10) + y1 = random.uniform(-10, 10) + x2 = random.uniform(-10, 10) + y2 = random.uniform(-10, 10) + z1 = complex(x1, y1) + z2 = complex(x2, y2) + a.append((z1, z2)) + return a + +def test_complex_powers(): + for dps in [15, 30, 100]: + # Check accuracy for complex square root + mp.dps = dps + a = mpc(1j)**0.5 + assert a.real == a.imag == mpf(2)**0.5 / 2 + mp.dps = 15 + random.seed(1) + for (z1, z2) in random_complexes(100): + assert (mpc(z1)**mpc(z2)).ae(z1**z2, 1e-12) + assert (e**(-pi*1j)).ae(-1) + mp.dps = 50 + assert (e**(-pi*1j)).ae(-1) + mp.dps = 15 + +def test_complex_sqrt_accuracy(): + def test_mpc_sqrt(lst): + for a, b in lst: + z = mpc(a + j*b) + assert mpc_ae(sqrt(z*z), z) + z = mpc(-a + j*b) + assert mpc_ae(sqrt(z*z), -z) + z = mpc(a - j*b) + assert mpc_ae(sqrt(z*z), z) + z = mpc(-a - j*b) + assert mpc_ae(sqrt(z*z), -z) + random.seed(2) + N = 10 + mp.dps = 30 + dps = mp.dps + test_mpc_sqrt([(random.uniform(0, 10),random.uniform(0, 10)) for i in range(N)]) + test_mpc_sqrt([(i + 0.1, (i + 0.2)*10**i) for i in range(N)]) + mp.dps = 15 + +def test_atan(): + mp.dps = 15 + assert atan(-2.3).ae(math.atan(-2.3)) + assert atan(1e-50) == 1e-50 + assert atan(1e50).ae(pi/2) + assert atan(-1e-50) == -1e-50 + assert atan(-1e50).ae(-pi/2) + assert atan(10**1000).ae(pi/2) + for dps in [25, 70, 100, 300, 1000]: + mp.dps = dps + assert (4*atan(1)).ae(pi) + mp.dps = 15 + pi2 = pi/2 + assert atan(mpc(inf,-1)).ae(pi2) + assert atan(mpc(inf,0)).ae(pi2) + assert atan(mpc(inf,1)).ae(pi2) + assert atan(mpc(1,inf)).ae(pi2) + assert atan(mpc(0,inf)).ae(pi2) + assert atan(mpc(-1,inf)).ae(-pi2) + assert atan(mpc(-inf,1)).ae(-pi2) + assert atan(mpc(-inf,0)).ae(-pi2) + assert atan(mpc(-inf,-1)).ae(-pi2) + assert atan(mpc(-1,-inf)).ae(-pi2) + assert atan(mpc(0,-inf)).ae(-pi2) + assert atan(mpc(1,-inf)).ae(pi2) + +def test_atan2(): + mp.dps = 15 + assert atan2(1,1).ae(pi/4) + assert atan2(1,-1).ae(3*pi/4) + assert atan2(-1,-1).ae(-3*pi/4) + assert atan2(-1,1).ae(-pi/4) + assert atan2(-1,0).ae(-pi/2) + assert atan2(1,0).ae(pi/2) + assert atan2(0,0) == 0 + assert atan2(inf,0).ae(pi/2) + assert atan2(-inf,0).ae(-pi/2) + assert isnan(atan2(inf,inf)) + assert isnan(atan2(-inf,inf)) + assert isnan(atan2(inf,-inf)) + assert isnan(atan2(3,nan)) + assert isnan(atan2(nan,3)) + assert isnan(atan2(0,nan)) + assert isnan(atan2(nan,0)) + assert atan2(0,inf) == 0 + assert atan2(0,-inf).ae(pi) + assert atan2(10,inf) == 0 + assert atan2(-10,inf) == 0 + assert atan2(-10,-inf).ae(-pi) + assert atan2(10,-inf).ae(pi) + assert atan2(inf,10).ae(pi/2) + assert atan2(inf,-10).ae(pi/2) + assert atan2(-inf,10).ae(-pi/2) + assert atan2(-inf,-10).ae(-pi/2) + +def test_areal_inverses(): + assert asin(mpf(0)) == 0 + assert asinh(mpf(0)) == 0 + assert acosh(mpf(1)) == 0 + assert isinstance(asin(mpf(0.5)), mpf) + assert isinstance(asin(mpf(2.0)), mpc) + assert isinstance(acos(mpf(0.5)), mpf) + assert isinstance(acos(mpf(2.0)), mpc) + assert isinstance(atanh(mpf(0.1)), mpf) + assert isinstance(atanh(mpf(1.1)), mpc) + + random.seed(1) + for i in range(50): + x = random.uniform(0, 1) + assert asin(mpf(x)).ae(math.asin(x)) + assert acos(mpf(x)).ae(math.acos(x)) + + x = random.uniform(-10, 10) + assert asinh(mpf(x)).ae(cmath.asinh(x).real) + assert isinstance(asinh(mpf(x)), mpf) + x = random.uniform(1, 10) + assert acosh(mpf(x)).ae(cmath.acosh(x).real) + assert isinstance(acosh(mpf(x)), mpf) + x = random.uniform(-10, 0.999) + assert isinstance(acosh(mpf(x)), mpc) + + x = random.uniform(-1, 1) + assert atanh(mpf(x)).ae(cmath.atanh(x).real) + assert isinstance(atanh(mpf(x)), mpf) + + dps = mp.dps + mp.dps = 300 + assert isinstance(asin(0.5), mpf) + mp.dps = 1000 + assert asin(1).ae(pi/2) + assert asin(-1).ae(-pi/2) + mp.dps = dps + +def test_invhyperb_inaccuracy(): + mp.dps = 15 + assert (asinh(1e-5)*10**5).ae(0.99999999998333333) + assert (asinh(1e-10)*10**10).ae(1) + assert (asinh(1e-50)*10**50).ae(1) + assert (asinh(-1e-5)*10**5).ae(-0.99999999998333333) + assert (asinh(-1e-10)*10**10).ae(-1) + assert (asinh(-1e-50)*10**50).ae(-1) + assert asinh(10**20).ae(46.744849040440862) + assert asinh(-10**20).ae(-46.744849040440862) + assert (tanh(1e-10)*10**10).ae(1) + assert (tanh(-1e-10)*10**10).ae(-1) + assert (atanh(1e-10)*10**10).ae(1) + assert (atanh(-1e-10)*10**10).ae(-1) + +def test_complex_functions(): + for x in (list(range(10)) + list(range(-10,0))): + for y in (list(range(10)) + list(range(-10,0))): + z = complex(x, y)/4.3 + 0.01j + assert exp(mpc(z)).ae(cmath.exp(z)) + assert log(mpc(z)).ae(cmath.log(z)) + assert cos(mpc(z)).ae(cmath.cos(z)) + assert sin(mpc(z)).ae(cmath.sin(z)) + assert tan(mpc(z)).ae(cmath.tan(z)) + assert sinh(mpc(z)).ae(cmath.sinh(z)) + assert cosh(mpc(z)).ae(cmath.cosh(z)) + assert tanh(mpc(z)).ae(cmath.tanh(z)) + +def test_complex_inverse_functions(): + mp.dps = 15 + iv.dps = 15 + for (z1, z2) in random_complexes(30): + # apparently cmath uses a different branch, so we + # can't use it for comparison + assert sinh(asinh(z1)).ae(z1) + # + assert acosh(z1).ae(cmath.acosh(z1)) + assert atanh(z1).ae(cmath.atanh(z1)) + assert atan(z1).ae(cmath.atan(z1)) + # the reason we set a big eps here is that the cmath + # functions are inaccurate + assert asin(z1).ae(cmath.asin(z1), rel_eps=1e-12) + assert acos(z1).ae(cmath.acos(z1), rel_eps=1e-12) + one = mpf(1) + for i in range(-9, 10, 3): + for k in range(-9, 10, 3): + a = 0.9*j*10**k + 0.8*one*10**i + b = cos(acos(a)) + assert b.ae(a) + b = sin(asin(a)) + assert b.ae(a) + one = mpf(1) + err = 2*10**-15 + for i in range(-9, 9, 3): + for k in range(-9, 9, 3): + a = -0.9*10**k + j*0.8*one*10**i + b = cosh(acosh(a)) + assert b.ae(a, err) + b = sinh(asinh(a)) + assert b.ae(a, err) + +def test_reciprocal_functions(): + assert sec(3).ae(-1.01010866590799375) + assert csc(3).ae(7.08616739573718592) + assert cot(3).ae(-7.01525255143453347) + assert sech(3).ae(0.0993279274194332078) + assert csch(3).ae(0.0998215696688227329) + assert coth(3).ae(1.00496982331368917) + assert asec(3).ae(1.23095941734077468) + assert acsc(3).ae(0.339836909454121937) + assert acot(3).ae(0.321750554396642193) + assert asech(0.5).ae(1.31695789692481671) + assert acsch(3).ae(0.327450150237258443) + assert acoth(3).ae(0.346573590279972655) + assert acot(0).ae(1.5707963267948966192) + assert acoth(0).ae(1.5707963267948966192j) + +def test_ldexp(): + mp.dps = 15 + assert ldexp(mpf(2.5), 0) == 2.5 + assert ldexp(mpf(2.5), -1) == 1.25 + assert ldexp(mpf(2.5), 2) == 10 + assert ldexp(mpf('inf'), 3) == mpf('inf') + +def test_frexp(): + mp.dps = 15 + assert frexp(0) == (0.0, 0) + assert frexp(9) == (0.5625, 4) + assert frexp(1) == (0.5, 1) + assert frexp(0.2) == (0.8, -2) + assert frexp(1000) == (0.9765625, 10) + +def test_aliases(): + assert ln(7) == log(7) + assert log10(3.75) == log(3.75,10) + assert degrees(5.6) == 5.6 / degree + assert radians(5.6) == 5.6 * degree + assert power(-1,0.5) == j + assert fmod(25,7) == 4.0 and isinstance(fmod(25,7), mpf) + +def test_arg_sign(): + assert arg(3) == 0 + assert arg(-3).ae(pi) + assert arg(j).ae(pi/2) + assert arg(-j).ae(-pi/2) + assert arg(0) == 0 + assert isnan(atan2(3,nan)) + assert isnan(atan2(nan,3)) + assert isnan(atan2(0,nan)) + assert isnan(atan2(nan,0)) + assert isnan(atan2(nan,nan)) + assert arg(inf) == 0 + assert arg(-inf).ae(pi) + assert isnan(arg(nan)) + #assert arg(inf*j).ae(pi/2) + assert sign(0) == 0 + assert sign(3) == 1 + assert sign(-3) == -1 + assert sign(inf) == 1 + assert sign(-inf) == -1 + assert isnan(sign(nan)) + assert sign(j) == j + assert sign(-3*j) == -j + assert sign(1+j).ae((1+j)/sqrt(2)) + +def test_misc_bugs(): + # test that this doesn't raise an exception + mp.dps = 1000 + log(1302) + mp.dps = 15 + +def test_arange(): + assert arange(10) == [mpf('0.0'), mpf('1.0'), mpf('2.0'), mpf('3.0'), + mpf('4.0'), mpf('5.0'), mpf('6.0'), mpf('7.0'), + mpf('8.0'), mpf('9.0')] + assert arange(-5, 5) == [mpf('-5.0'), mpf('-4.0'), mpf('-3.0'), + mpf('-2.0'), mpf('-1.0'), mpf('0.0'), + mpf('1.0'), mpf('2.0'), mpf('3.0'), mpf('4.0')] + assert arange(0, 1, 0.1) == [mpf('0.0'), mpf('0.10000000000000001'), + mpf('0.20000000000000001'), + mpf('0.30000000000000004'), + mpf('0.40000000000000002'), + mpf('0.5'), mpf('0.60000000000000009'), + mpf('0.70000000000000007'), + mpf('0.80000000000000004'), + mpf('0.90000000000000002')] + assert arange(17, -9, -3) == [mpf('17.0'), mpf('14.0'), mpf('11.0'), + mpf('8.0'), mpf('5.0'), mpf('2.0'), + mpf('-1.0'), mpf('-4.0'), mpf('-7.0')] + assert arange(0.2, 0.1, -0.1) == [mpf('0.20000000000000001')] + assert arange(0) == [] + assert arange(1000, -1) == [] + assert arange(-1.23, 3.21, -0.0000001) == [] + +def test_linspace(): + assert linspace(2, 9, 7) == [mpf('2.0'), mpf('3.166666666666667'), + mpf('4.3333333333333339'), mpf('5.5'), mpf('6.666666666666667'), + mpf('7.8333333333333339'), mpf('9.0')] + assert linspace(2, 9, 7, endpoint=0) == [mpf('2.0'), mpf('3.0'), mpf('4.0'), + mpf('5.0'), mpf('6.0'), mpf('7.0'), mpf('8.0')] + assert linspace(2, 7, 1) == [mpf(2)] + +def test_float_cbrt(): + mp.dps = 30 + for a in arange(0,10,0.1): + assert cbrt(a*a*a).ae(a, eps) + assert cbrt(-1).ae(0.5 + j*sqrt(3)/2) + one_third = mpf(1)/3 + for a in arange(0,10,2.7) + [0.1 + 10**5]: + a = mpc(a + 1.1j) + r1 = cbrt(a) + mp.dps += 10 + r2 = pow(a, one_third) + mp.dps -= 10 + assert r1.ae(r2, eps) + mp.dps = 100 + for n in range(100, 301, 100): + w = 10**n + j*10**-3 + z = w*w*w + r = cbrt(z) + assert mpc_ae(r, w, eps) + mp.dps = 15 + +def test_root(): + mp.dps = 30 + random.seed(1) + a = random.randint(0, 10000) + p = a*a*a + r = nthroot(mpf(p), 3) + assert r == a + for n in range(4, 10): + p = p*a + assert nthroot(mpf(p), n) == a + mp.dps = 40 + for n in range(10, 5000, 100): + for a in [random.random()*10000, random.random()*10**100]: + r = nthroot(a, n) + r1 = pow(a, mpf(1)/n) + assert r.ae(r1) + r = nthroot(a, -n) + r1 = pow(a, -mpf(1)/n) + assert r.ae(r1) + # XXX: this is broken right now + # tests for nthroot rounding + for rnd in ['nearest', 'up', 'down']: + mp.rounding = rnd + for n in [-5, -3, 3, 5]: + prec = 50 + for i in range(10): + mp.prec = prec + a = rand() + mp.prec = 2*prec + b = a**n + mp.prec = prec + r = nthroot(b, n) + assert r == a + mp.dps = 30 + for n in range(3, 21): + a = (random.random() + j*random.random()) + assert nthroot(a, n).ae(pow(a, mpf(1)/n)) + assert mpc_ae(nthroot(a, n), pow(a, mpf(1)/n)) + a = (random.random()*10**100 + j*random.random()) + r = nthroot(a, n) + mp.dps += 4 + r1 = pow(a, mpf(1)/n) + mp.dps -= 4 + assert r.ae(r1) + assert mpc_ae(r, r1, eps) + r = nthroot(a, -n) + mp.dps += 4 + r1 = pow(a, -mpf(1)/n) + mp.dps -= 4 + assert r.ae(r1) + assert mpc_ae(r, r1, eps) + mp.dps = 15 + assert nthroot(4, 1) == 4 + assert nthroot(4, 0) == 1 + assert nthroot(4, -1) == 0.25 + assert nthroot(inf, 1) == inf + assert nthroot(inf, 2) == inf + assert nthroot(inf, 3) == inf + assert nthroot(inf, -1) == 0 + assert nthroot(inf, -2) == 0 + assert nthroot(inf, -3) == 0 + assert nthroot(j, 1) == j + assert nthroot(j, 0) == 1 + assert nthroot(j, -1) == -j + assert isnan(nthroot(nan, 1)) + assert isnan(nthroot(nan, 0)) + assert isnan(nthroot(nan, -1)) + assert isnan(nthroot(inf, 0)) + assert root(2,3) == nthroot(2,3) + assert root(16,4,0) == 2 + assert root(16,4,1) == 2j + assert root(16,4,2) == -2 + assert root(16,4,3) == -2j + assert root(16,4,4) == 2 + assert root(-125,3,1) == -5 + +def test_issue_136(): + for dps in [20, 80]: + mp.dps = dps + r = nthroot(mpf('-1e-20'), 4) + assert r.ae(mpf(10)**(-5) * (1 + j) * mpf(2)**(-0.5)) + mp.dps = 80 + assert nthroot('-1e-3', 4).ae(mpf(10)**(-3./4) * (1 + j)/sqrt(2)) + assert nthroot('-1e-6', 4).ae((1 + j)/(10 * sqrt(20))) + # Check that this doesn't take eternity to compute + mp.dps = 20 + assert nthroot('-1e100000000', 4).ae((1+j)*mpf('1e25000000')/sqrt(2)) + mp.dps = 15 + +def test_mpcfun_real_imag(): + mp.dps = 15 + x = mpf(0.3) + y = mpf(0.4) + assert exp(mpc(x,0)) == exp(x) + assert exp(mpc(0,y)) == mpc(cos(y),sin(y)) + assert cos(mpc(x,0)) == cos(x) + assert sin(mpc(x,0)) == sin(x) + assert cos(mpc(0,y)) == cosh(y) + assert sin(mpc(0,y)) == mpc(0,sinh(y)) + assert cospi(mpc(x,0)) == cospi(x) + assert sinpi(mpc(x,0)) == sinpi(x) + assert cospi(mpc(0,y)).ae(cosh(pi*y)) + assert sinpi(mpc(0,y)).ae(mpc(0,sinh(pi*y))) + c, s = cospi_sinpi(mpc(x,0)) + assert c == cospi(x) + assert s == sinpi(x) + c, s = cospi_sinpi(mpc(0,y)) + assert c.ae(cosh(pi*y)) + assert s.ae(mpc(0,sinh(pi*y))) + c, s = cos_sin(mpc(x,0)) + assert c == cos(x) + assert s == sin(x) + c, s = cos_sin(mpc(0,y)) + assert c == cosh(y) + assert s == mpc(0,sinh(y)) + +def test_perturbation_rounding(): + mp.dps = 100 + a = pi/10**50 + b = -pi/10**50 + c = 1 + a + d = 1 + b + mp.dps = 15 + assert exp(a) == 1 + assert exp(a, rounding='c') > 1 + assert exp(b, rounding='c') == 1 + assert exp(a, rounding='f') == 1 + assert exp(b, rounding='f') < 1 + assert cos(a) == 1 + assert cos(a, rounding='c') == 1 + assert cos(b, rounding='c') == 1 + assert cos(a, rounding='f') < 1 + assert cos(b, rounding='f') < 1 + for f in [sin, atan, asinh, tanh]: + assert f(a) == +a + assert f(a, rounding='c') > a + assert f(a, rounding='f') < a + assert f(b) == +b + assert f(b, rounding='c') > b + assert f(b, rounding='f') < b + for f in [asin, tan, sinh, atanh]: + assert f(a) == +a + assert f(b) == +b + assert f(a, rounding='c') > a + assert f(b, rounding='c') > b + assert f(a, rounding='f') < a + assert f(b, rounding='f') < b + assert ln(c) == +a + assert ln(d) == +b + assert ln(c, rounding='c') > a + assert ln(c, rounding='f') < a + assert ln(d, rounding='c') > b + assert ln(d, rounding='f') < b + assert cosh(a) == 1 + assert cosh(b) == 1 + assert cosh(a, rounding='c') > 1 + assert cosh(b, rounding='c') > 1 + assert cosh(a, rounding='f') == 1 + assert cosh(b, rounding='f') == 1 + +def test_integer_parts(): + assert floor(3.2) == 3 + assert ceil(3.2) == 4 + assert floor(3.2+5j) == 3+5j + assert ceil(3.2+5j) == 4+5j + +def test_complex_parts(): + assert fabs('3') == 3 + assert fabs(3+4j) == 5 + assert re(3) == 3 + assert re(1+4j) == 1 + assert im(3) == 0 + assert im(1+4j) == 4 + assert conj(3) == 3 + assert conj(3+4j) == 3-4j + assert mpf(3).conjugate() == 3 + +def test_cospi_sinpi(): + assert sinpi(0) == 0 + assert sinpi(0.5) == 1 + assert sinpi(1) == 0 + assert sinpi(1.5) == -1 + assert sinpi(2) == 0 + assert sinpi(2.5) == 1 + assert sinpi(-0.5) == -1 + assert cospi(0) == 1 + assert cospi(0.5) == 0 + assert cospi(1) == -1 + assert cospi(1.5) == 0 + assert cospi(2) == 1 + assert cospi(2.5) == 0 + assert cospi(-0.5) == 0 + assert cospi(100000000000.25).ae(sqrt(2)/2) + a = cospi(2+3j) + assert a.real.ae(cos((2+3j)*pi).real) + assert a.imag == 0 + b = sinpi(2+3j) + assert b.imag.ae(sin((2+3j)*pi).imag) + assert b.real == 0 + mp.dps = 35 + x1 = mpf(10000) - mpf('1e-15') + x2 = mpf(10000) + mpf('1e-15') + x3 = mpf(10000.5) - mpf('1e-15') + x4 = mpf(10000.5) + mpf('1e-15') + x5 = mpf(10001) - mpf('1e-15') + x6 = mpf(10001) + mpf('1e-15') + x7 = mpf(10001.5) - mpf('1e-15') + x8 = mpf(10001.5) + mpf('1e-15') + mp.dps = 15 + M = 10**15 + assert (sinpi(x1)*M).ae(-pi) + assert (sinpi(x2)*M).ae(pi) + assert (cospi(x3)*M).ae(pi) + assert (cospi(x4)*M).ae(-pi) + assert (sinpi(x5)*M).ae(pi) + assert (sinpi(x6)*M).ae(-pi) + assert (cospi(x7)*M).ae(-pi) + assert (cospi(x8)*M).ae(pi) + assert 0.999 < cospi(x1, rounding='d') < 1 + assert 0.999 < cospi(x2, rounding='d') < 1 + assert 0.999 < sinpi(x3, rounding='d') < 1 + assert 0.999 < sinpi(x4, rounding='d') < 1 + assert -1 < cospi(x5, rounding='d') < -0.999 + assert -1 < cospi(x6, rounding='d') < -0.999 + assert -1 < sinpi(x7, rounding='d') < -0.999 + assert -1 < sinpi(x8, rounding='d') < -0.999 + assert (sinpi(1e-15)*M).ae(pi) + assert (sinpi(-1e-15)*M).ae(-pi) + assert cospi(1e-15) == 1 + assert cospi(1e-15, rounding='d') < 1 + +def test_expj(): + assert expj(0) == 1 + assert expj(1).ae(exp(j)) + assert expj(j).ae(exp(-1)) + assert expj(1+j).ae(exp(j*(1+j))) + assert expjpi(0) == 1 + assert expjpi(1).ae(exp(j*pi)) + assert expjpi(j).ae(exp(-pi)) + assert expjpi(1+j).ae(exp(j*pi*(1+j))) + assert expjpi(-10**15 * j).ae('2.22579818340535731e+1364376353841841') + +def test_sinc(): + assert sinc(0) == sincpi(0) == 1 + assert sinc(inf) == sincpi(inf) == 0 + assert sinc(-inf) == sincpi(-inf) == 0 + assert sinc(2).ae(0.45464871341284084770) + assert sinc(2+3j).ae(0.4463290318402435457-2.7539470277436474940j) + assert sincpi(2) == 0 + assert sincpi(1.5).ae(-0.212206590789193781) + +def test_fibonacci(): + mp.dps = 15 + assert [fibonacci(n) for n in range(-5, 10)] == \ + [5, -3, 2, -1, 1, 0, 1, 1, 2, 3, 5, 8, 13, 21, 34] + assert fib(2.5).ae(1.4893065462657091) + assert fib(3+4j).ae(-5248.51130728372 - 14195.962288353j) + assert fib(1000).ae(4.3466557686937455e+208) + assert str(fib(10**100)) == '6.24499112864607e+2089876402499787337692720892375554168224592399182109535392875613974104853496745963277658556235103534' + mp.dps = 2100 + a = fib(10000) + assert a % 10**10 == 9947366875 + mp.dps = 15 + assert fibonacci(inf) == inf + assert fib(3+0j) == 2 + +def test_call_with_dps(): + mp.dps = 15 + assert abs(exp(1, dps=30)-e(dps=35)) < 1e-29 + +def test_tanh(): + mp.dps = 15 + assert tanh(0) == 0 + assert tanh(inf) == 1 + assert tanh(-inf) == -1 + assert isnan(tanh(nan)) + assert tanh(mpc('inf', '0')) == 1 + +def test_atanh(): + mp.dps = 15 + assert atanh(0) == 0 + assert atanh(0.5).ae(0.54930614433405484570) + assert atanh(-0.5).ae(-0.54930614433405484570) + assert atanh(1) == inf + assert atanh(-1) == -inf + assert isnan(atanh(nan)) + assert isinstance(atanh(1), mpf) + assert isinstance(atanh(-1), mpf) + # Limits at infinity + jpi2 = j*pi/2 + assert atanh(inf).ae(-jpi2) + assert atanh(-inf).ae(jpi2) + assert atanh(mpc(inf,-1)).ae(-jpi2) + assert atanh(mpc(inf,0)).ae(-jpi2) + assert atanh(mpc(inf,1)).ae(jpi2) + assert atanh(mpc(1,inf)).ae(jpi2) + assert atanh(mpc(0,inf)).ae(jpi2) + assert atanh(mpc(-1,inf)).ae(jpi2) + assert atanh(mpc(-inf,1)).ae(jpi2) + assert atanh(mpc(-inf,0)).ae(jpi2) + assert atanh(mpc(-inf,-1)).ae(-jpi2) + assert atanh(mpc(-1,-inf)).ae(-jpi2) + assert atanh(mpc(0,-inf)).ae(-jpi2) + assert atanh(mpc(1,-inf)).ae(-jpi2) + +def test_expm1(): + mp.dps = 15 + assert expm1(0) == 0 + assert expm1(3).ae(exp(3)-1) + assert expm1(inf) == inf + assert expm1(1e-50).ae(1e-50) + assert (expm1(1e-10)*1e10).ae(1.00000000005) + +def test_log1p(): + mp.dps = 15 + assert log1p(0) == 0 + assert log1p(3).ae(log(1+3)) + assert log1p(inf) == inf + assert log1p(1e-50).ae(1e-50) + assert (log1p(1e-10)*1e10).ae(0.99999999995) + +def test_powm1(): + mp.dps = 15 + assert powm1(2,3) == 7 + assert powm1(-1,2) == 0 + assert powm1(-1,0) == 0 + assert powm1(-2,0) == 0 + assert powm1(3+4j,0) == 0 + assert powm1(0,1) == -1 + assert powm1(0,0) == 0 + assert powm1(1,0) == 0 + assert powm1(1,2) == 0 + assert powm1(1,3+4j) == 0 + assert powm1(1,5) == 0 + assert powm1(j,4) == 0 + assert powm1(-j,4) == 0 + assert (powm1(2,1e-100)*1e100).ae(ln2) + assert powm1(2,'1e-100000000000') != 0 + assert (powm1(fadd(1,1e-100,exact=True), 5)*1e100).ae(5) + +def test_unitroots(): + assert unitroots(1) == [1] + assert unitroots(2) == [1, -1] + a, b, c = unitroots(3) + assert a == 1 + assert b.ae(-0.5 + 0.86602540378443864676j) + assert c.ae(-0.5 - 0.86602540378443864676j) + assert unitroots(1, primitive=True) == [1] + assert unitroots(2, primitive=True) == [-1] + assert unitroots(3, primitive=True) == unitroots(3)[1:] + assert unitroots(4, primitive=True) == [j, -j] + assert len(unitroots(17, primitive=True)) == 16 + assert len(unitroots(16, primitive=True)) == 8 + +def test_cyclotomic(): + mp.dps = 15 + assert [cyclotomic(n,1) for n in range(31)] == [1,0,2,3,2,5,1,7,2,3,1,11,1,13,1,1,2,17,1,19,1,1,1,23,1,5,1,3,1,29,1] + assert [cyclotomic(n,-1) for n in range(31)] == [1,-2,0,1,2,1,3,1,2,1,5,1,1,1,7,1,2,1,3,1,1,1,11,1,1,1,13,1,1,1,1] + assert [cyclotomic(n,j) for n in range(21)] == [1,-1+j,1+j,j,0,1,-j,j,2,-j,1,j,3,1,-j,1,2,1,j,j,5] + assert [cyclotomic(n,-j) for n in range(21)] == [1,-1-j,1-j,-j,0,1,j,-j,2,j,1,-j,3,1,j,1,2,1,-j,-j,5] + assert cyclotomic(1624,j) == 1 + assert cyclotomic(33600,j) == 1 + u = sqrt(j, prec=500) + assert cyclotomic(8, u).ae(0) + assert cyclotomic(30, u).ae(5.8284271247461900976) + assert cyclotomic(2040, u).ae(1) + assert cyclotomic(0,2.5) == 1 + assert cyclotomic(1,2.5) == 2.5-1 + assert cyclotomic(2,2.5) == 2.5+1 + assert cyclotomic(3,2.5) == 2.5**2 + 2.5 + 1 + assert cyclotomic(7,2.5) == 406.234375 diff --git a/lib/python3.12/site-packages/mpmath/tests/test_functions2.py b/lib/python3.12/site-packages/mpmath/tests/test_functions2.py new file mode 100644 index 0000000000000000000000000000000000000000..2b2d57fcec9be0db4d921b013f24fd6a5e0e9930 --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/tests/test_functions2.py @@ -0,0 +1,2384 @@ +import math +import pytest +from mpmath import * + +def test_bessel(): + mp.dps = 15 + assert j0(1).ae(0.765197686557966551) + assert j0(pi).ae(-0.304242177644093864) + assert j0(1000).ae(0.0247866861524201746) + assert j0(-25).ae(0.0962667832759581162) + assert j1(1).ae(0.440050585744933516) + assert j1(pi).ae(0.284615343179752757) + assert j1(1000).ae(0.00472831190708952392) + assert j1(-25).ae(0.125350249580289905) + assert besselj(5,1).ae(0.000249757730211234431) + assert besselj(5+0j,1).ae(0.000249757730211234431) + assert besselj(5,pi).ae(0.0521411843671184747) + assert besselj(5,1000).ae(0.00502540694523318607) + assert besselj(5,-25).ae(0.0660079953984229934) + assert besselj(-3,2).ae(-0.128943249474402051) + assert besselj(-4,2).ae(0.0339957198075684341) + assert besselj(3,3+2j).ae(0.424718794929639595942 + 0.625665327745785804812j) + assert besselj(0.25,4).ae(-0.374760630804249715) + assert besselj(1+2j,3+4j).ae(0.319247428741872131 - 0.669557748880365678j) + assert (besselj(3, 10**10) * 10**5).ae(0.76765081748139204023) + assert bessely(-0.5, 0) == 0 + assert bessely(0.5, 0) == -inf + assert bessely(1.5, 0) == -inf + assert bessely(0,0) == -inf + assert bessely(-0.4, 0) == -inf + assert bessely(-0.6, 0) == inf + assert bessely(-1, 0) == inf + assert bessely(-1.4, 0) == inf + assert bessely(-1.6, 0) == -inf + assert bessely(-1, 0) == inf + assert bessely(-2, 0) == -inf + assert bessely(-3, 0) == inf + assert bessely(0.5, 0) == -inf + assert bessely(1, 0) == -inf + assert bessely(1.5, 0) == -inf + assert bessely(2, 0) == -inf + assert bessely(2.5, 0) == -inf + assert bessely(3, 0) == -inf + assert bessely(0,0.5).ae(-0.44451873350670655715) + assert bessely(1,0.5).ae(-1.4714723926702430692) + assert bessely(-1,0.5).ae(1.4714723926702430692) + assert bessely(3.5,0.5).ae(-138.86400867242488443) + assert bessely(0,3+4j).ae(4.6047596915010138655-8.8110771408232264208j) + assert bessely(0,j).ae(-0.26803248203398854876+1.26606587775200833560j) + assert (bessely(3, 10**10) * 10**5).ae(0.21755917537013204058) + assert besseli(0,0) == 1 + assert besseli(1,0) == 0 + assert besseli(2,0) == 0 + assert besseli(-1,0) == 0 + assert besseli(-2,0) == 0 + assert besseli(0,0.5).ae(1.0634833707413235193) + assert besseli(1,0.5).ae(0.25789430539089631636) + assert besseli(-1,0.5).ae(0.25789430539089631636) + assert besseli(3.5,0.5).ae(0.00068103597085793815863) + assert besseli(0,3+4j).ae(-3.3924877882755196097-1.3239458916287264815j) + assert besseli(0,j).ae(besselj(0,1)) + assert (besseli(3, 10**10) * mpf(10)**(-4342944813)).ae(4.2996028505491271875) + assert besselk(0,0) == inf + assert besselk(1,0) == inf + assert besselk(2,0) == inf + assert besselk(-1,0) == inf + assert besselk(-2,0) == inf + assert besselk(0,0.5).ae(0.92441907122766586178) + assert besselk(1,0.5).ae(1.6564411200033008937) + assert besselk(-1,0.5).ae(1.6564411200033008937) + assert besselk(3.5,0.5).ae(207.48418747548460607) + assert besselk(0,3+4j).ae(-0.007239051213570155013+0.026510418350267677215j) + assert besselk(0,j).ae(-0.13863371520405399968-1.20196971531720649914j) + assert (besselk(3, 10**10) * mpf(10)**4342944824).ae(1.1628981033356187851) + # test for issue 331, bug reported by Michael Hartmann + for n in range(10,100,10): + mp.dps = n + assert besseli(91.5,24.7708).ae("4.00830632138673963619656140653537080438462342928377020695738635559218797348548092636896796324190271316137982810144874264e-41") + +def test_bessel_zeros(): + mp.dps = 15 + assert besseljzero(0,1).ae(2.40482555769577276869) + assert besseljzero(2,1).ae(5.1356223018406825563) + assert besseljzero(1,50).ae(157.86265540193029781) + assert besseljzero(10,1).ae(14.475500686554541220) + assert besseljzero(0.5,3).ae(9.4247779607693797153) + assert besseljzero(2,1,1).ae(3.0542369282271403228) + assert besselyzero(0,1).ae(0.89357696627916752158) + assert besselyzero(2,1).ae(3.3842417671495934727) + assert besselyzero(1,50).ae(156.29183520147840108) + assert besselyzero(10,1).ae(12.128927704415439387) + assert besselyzero(0.5,3).ae(7.8539816339744830962) + assert besselyzero(2,1,1).ae(5.0025829314460639452) + +def test_hankel(): + mp.dps = 15 + assert hankel1(0,0.5).ae(0.93846980724081290423-0.44451873350670655715j) + assert hankel1(1,0.5).ae(0.2422684576748738864-1.4714723926702430692j) + assert hankel1(-1,0.5).ae(-0.2422684576748738864+1.4714723926702430692j) + assert hankel1(1.5,0.5).ae(0.0917016996256513026-2.5214655504213378514j) + assert hankel1(1.5,3+4j).ae(0.0066806866476728165382-0.0036684231610839127106j) + assert hankel2(0,0.5).ae(0.93846980724081290423+0.44451873350670655715j) + assert hankel2(1,0.5).ae(0.2422684576748738864+1.4714723926702430692j) + assert hankel2(-1,0.5).ae(-0.2422684576748738864-1.4714723926702430692j) + assert hankel2(1.5,0.5).ae(0.0917016996256513026+2.5214655504213378514j) + assert hankel2(1.5,3+4j).ae(14.783528526098567526-7.397390270853446512j) + +def test_struve(): + mp.dps = 15 + assert struveh(2,3).ae(0.74238666967748318564) + assert struveh(-2.5,3).ae(0.41271003220971599344) + assert struvel(2,3).ae(1.7476573277362782744) + assert struvel(-2.5,3).ae(1.5153394466819651377) + +def test_whittaker(): + mp.dps = 15 + assert whitm(2,3,4).ae(49.753745589025246591) + assert whitw(2,3,4).ae(14.111656223052932215) + +def test_kelvin(): + mp.dps = 15 + assert ber(2,3).ae(0.80836846563726819091) + assert ber(3,4).ae(-0.28262680167242600233) + assert ber(-3,2).ae(-0.085611448496796363669) + assert bei(2,3).ae(-0.89102236377977331571) + assert bei(-3,2).ae(-0.14420994155731828415) + assert ker(2,3).ae(0.12839126695733458928) + assert ker(-3,2).ae(-0.29802153400559142783) + assert ker(0.5,3).ae(-0.085662378535217097524) + assert kei(2,3).ae(0.036804426134164634000) + assert kei(-3,2).ae(0.88682069845786731114) + assert kei(0.5,3).ae(0.013633041571314302948) + +def test_hyper_misc(): + mp.dps = 15 + assert hyp0f1(1,0) == 1 + assert hyp1f1(1,2,0) == 1 + assert hyp1f2(1,2,3,0) == 1 + assert hyp2f1(1,2,3,0) == 1 + assert hyp2f2(1,2,3,4,0) == 1 + assert hyp2f3(1,2,3,4,5,0) == 1 + # Degenerate case: 0F0 + assert hyper([],[],0) == 1 + assert hyper([],[],-2).ae(exp(-2)) + # Degenerate case: 1F0 + assert hyper([2],[],1.5) == 4 + # + assert hyp2f1((1,3),(2,3),(5,6),mpf(27)/32).ae(1.6) + assert hyp2f1((1,4),(1,2),(3,4),mpf(80)/81).ae(1.8) + assert hyp2f1((2,3),(1,1),(3,2),(2+j)/3).ae(1.327531603558679093+0.439585080092769253j) + mp.dps = 25 + v = mpc('1.2282306665029814734863026', '-0.1225033830118305184672133') + assert hyper([(3,4),2+j,1],[1,5,j/3],mpf(1)/5+j/8).ae(v) + mp.dps = 15 + +def test_elliptic_integrals(): + mp.dps = 15 + assert ellipk(0).ae(pi/2) + assert ellipk(0.5).ae(gamma(0.25)**2/(4*sqrt(pi))) + assert ellipk(1) == inf + assert ellipk(1+0j) == inf + assert ellipk(-1).ae('1.3110287771460599052') + assert ellipk(-2).ae('1.1714200841467698589') + assert isinstance(ellipk(-2), mpf) + assert isinstance(ellipe(-2), mpf) + assert ellipk(-50).ae('0.47103424540873331679') + mp.dps = 30 + n1 = +fraction(99999,100000) + n2 = +fraction(100001,100000) + mp.dps = 15 + assert ellipk(n1).ae('7.1427724505817781901') + assert ellipk(n2).ae(mpc('7.1427417367963090109', '-1.5707923998261688019')) + assert ellipe(n1).ae('1.0000332138990829170') + v = ellipe(n2) + assert v.real.ae('0.999966786328145474069137') + assert (v.imag*10**6).ae('7.853952181727432') + assert ellipk(2).ae(mpc('1.3110287771460599052', '-1.3110287771460599052')) + assert ellipk(50).ae(mpc('0.22326753950210985451', '-0.47434723226254522087')) + assert ellipk(3+4j).ae(mpc('0.91119556380496500866', '0.63133428324134524388')) + assert ellipk(3-4j).ae(mpc('0.91119556380496500866', '-0.63133428324134524388')) + assert ellipk(-3+4j).ae(mpc('0.95357894880405122483', '0.23093044503746114444')) + assert ellipk(-3-4j).ae(mpc('0.95357894880405122483', '-0.23093044503746114444')) + assert isnan(ellipk(nan)) + assert isnan(ellipe(nan)) + assert ellipk(inf) == 0 + assert isinstance(ellipk(inf), mpc) + assert ellipk(-inf) == 0 + assert ellipk(1+0j) == inf + assert ellipe(0).ae(pi/2) + assert ellipe(0.5).ae(pi**(mpf(3)/2)/gamma(0.25)**2 +gamma(0.25)**2/(8*sqrt(pi))) + assert ellipe(1) == 1 + assert ellipe(1+0j) == 1 + assert ellipe(inf) == mpc(0,inf) + assert ellipe(-inf) == inf + assert ellipe(3+4j).ae(1.4995535209333469543-1.5778790079127582745j) + assert ellipe(3-4j).ae(1.4995535209333469543+1.5778790079127582745j) + assert ellipe(-3+4j).ae(2.5804237855343377803-0.8306096791000413778j) + assert ellipe(-3-4j).ae(2.5804237855343377803+0.8306096791000413778j) + assert ellipe(2).ae(0.59907011736779610372+0.59907011736779610372j) + assert ellipe('1e-1000000000').ae(pi/2) + assert ellipk('1e-1000000000').ae(pi/2) + assert ellipe(-pi).ae(2.4535865983838923) + mp.dps = 50 + assert ellipk(1/pi).ae('1.724756270009501831744438120951614673874904182624739673') + assert ellipe(1/pi).ae('1.437129808135123030101542922290970050337425479058225712') + assert ellipk(-10*pi).ae('0.5519067523886233967683646782286965823151896970015484512') + assert ellipe(-10*pi).ae('5.926192483740483797854383268707108012328213431657645509') + v = ellipk(pi) + assert v.real.ae('0.973089521698042334840454592642137667227167622330325225') + assert v.imag.ae('-1.156151296372835303836814390793087600271609993858798016') + v = ellipe(pi) + assert v.real.ae('0.4632848917264710404078033487934663562998345622611263332') + assert v.imag.ae('1.0637961621753130852473300451583414489944099504180510966') + mp.dps = 15 + +def test_exp_integrals(): + mp.dps = 15 + x = +e + z = e + sqrt(3)*j + assert ei(x).ae(8.21168165538361560) + assert li(x).ae(1.89511781635593676) + assert si(x).ae(1.82104026914756705) + assert ci(x).ae(0.213958001340379779) + assert shi(x).ae(4.11520706247846193) + assert chi(x).ae(4.09647459290515367) + assert fresnels(x).ae(0.437189718149787643) + assert fresnelc(x).ae(0.401777759590243012) + assert airyai(x).ae(0.0108502401568586681) + assert airybi(x).ae(8.98245748585468627) + assert ei(z).ae(3.72597969491314951 + 7.34213212314224421j) + assert li(z).ae(2.28662658112562502 + 1.50427225297269364j) + assert si(z).ae(2.48122029237669054 + 0.12684703275254834j) + assert ci(z).ae(0.169255590269456633 - 0.892020751420780353j) + assert shi(z).ae(1.85810366559344468 + 3.66435842914920263j) + assert chi(z).ae(1.86787602931970484 + 3.67777369399304159j) + assert fresnels(z/3).ae(0.034534397197008182 + 0.754859844188218737j) + assert fresnelc(z/3).ae(1.261581645990027372 + 0.417949198775061893j) + assert airyai(z).ae(-0.0162552579839056062 - 0.0018045715700210556j) + assert airybi(z).ae(-4.98856113282883371 + 2.08558537872180623j) + assert li(0) == 0.0 + assert li(1) == -inf + assert li(inf) == inf + assert isinstance(li(0.7), mpf) + assert si(inf).ae(pi/2) + assert si(-inf).ae(-pi/2) + assert ci(inf) == 0 + assert ci(0) == -inf + assert isinstance(ei(-0.7), mpf) + assert airyai(inf) == 0 + assert airybi(inf) == inf + assert airyai(-inf) == 0 + assert airybi(-inf) == 0 + assert fresnels(inf) == 0.5 + assert fresnelc(inf) == 0.5 + assert fresnels(-inf) == -0.5 + assert fresnelc(-inf) == -0.5 + assert shi(0) == 0 + assert shi(inf) == inf + assert shi(-inf) == -inf + assert chi(0) == -inf + assert chi(inf) == inf + +def test_ei(): + mp.dps = 15 + assert ei(0) == -inf + assert ei(inf) == inf + assert ei(-inf) == -0.0 + assert ei(20+70j).ae(6.1041351911152984397e6 - 2.7324109310519928872e6j) + # tests for the asymptotic expansion + # values checked with Mathematica ExpIntegralEi + mp.dps = 50 + r = ei(20000) + s = '3.8781962825045010930273870085501819470698476975019e+8681' + assert str(r) == s + r = ei(-200) + s = '-6.8852261063076355977108174824557929738368086933303e-90' + assert str(r) == s + r =ei(20000 + 10*j) + sre = '-3.255138234032069402493850638874410725961401274106e+8681' + sim = '-2.1081929993474403520785942429469187647767369645423e+8681' + assert str(r.real) == sre and str(r.imag) == sim + mp.dps = 15 + # More asymptotic expansions + assert chi(-10**6+100j).ae('1.3077239389562548386e+434288 + 7.6808956999707408158e+434287j') + assert shi(-10**6+100j).ae('-1.3077239389562548386e+434288 - 7.6808956999707408158e+434287j') + mp.dps = 15 + assert ei(10j).ae(-0.0454564330044553726+3.2291439210137706686j) + assert ei(100j).ae(-0.0051488251426104921+3.1330217936839529126j) + u = ei(fmul(10**20, j, exact=True)) + assert u.real.ae(-6.4525128526578084421345e-21, abs_eps=0, rel_eps=8*eps) + assert u.imag.ae(pi) + assert ei(-10j).ae(-0.0454564330044553726-3.2291439210137706686j) + assert ei(-100j).ae(-0.0051488251426104921-3.1330217936839529126j) + u = ei(fmul(-10**20, j, exact=True)) + assert u.real.ae(-6.4525128526578084421345e-21, abs_eps=0, rel_eps=8*eps) + assert u.imag.ae(-pi) + assert ei(10+10j).ae(-1576.1504265768517448+436.9192317011328140j) + u = ei(-10+10j) + assert u.real.ae(7.6698978415553488362543e-7, abs_eps=0, rel_eps=8*eps) + assert u.imag.ae(3.141595611735621062025) + +def test_e1(): + mp.dps = 15 + assert e1(0) == inf + assert e1(inf) == 0 + assert e1(-inf) == mpc(-inf, -pi) + assert e1(10j).ae(0.045456433004455372635 + 0.087551267423977430100j) + assert e1(100j).ae(0.0051488251426104921444 - 0.0085708599058403258790j) + assert e1(fmul(10**20, j, exact=True)).ae(6.4525128526578084421e-21 - 7.6397040444172830039e-21j, abs_eps=0, rel_eps=8*eps) + assert e1(-10j).ae(0.045456433004455372635 - 0.087551267423977430100j) + assert e1(-100j).ae(0.0051488251426104921444 + 0.0085708599058403258790j) + assert e1(fmul(-10**20, j, exact=True)).ae(6.4525128526578084421e-21 + 7.6397040444172830039e-21j, abs_eps=0, rel_eps=8*eps) + +def test_expint(): + mp.dps = 15 + assert expint(0,0) == inf + assert expint(0,1).ae(1/e) + assert expint(0,1.5).ae(2/exp(1.5)/3) + assert expint(1,1).ae(-ei(-1)) + assert expint(2,0).ae(1) + assert expint(3,0).ae(1/2.) + assert expint(4,0).ae(1/3.) + assert expint(-2, 0.5).ae(26/sqrt(e)) + assert expint(-1,-1) == 0 + assert expint(-2,-1).ae(-e) + assert expint(5.5, 0).ae(2/9.) + assert expint(2.00000001,0).ae(100000000./100000001) + assert expint(2+3j,4-j).ae(0.0023461179581675065414+0.0020395540604713669262j) + assert expint('1.01', '1e-1000').ae(99.9999999899412802) + assert expint('1.000000000001', 3.5).ae(0.00697013985754701819446) + assert expint(2,3).ae(3*ei(-3)+exp(-3)) + assert (expint(10,20)*10**10).ae(0.694439055541231353) + assert expint(3,inf) == 0 + assert expint(3.2,inf) == 0 + assert expint(3.2+2j,inf) == 0 + assert expint(1,3j).ae(-0.11962978600800032763 + 0.27785620120457163717j) + assert expint(1,3).ae(0.013048381094197037413) + assert expint(1,-3).ae(-ei(3)-pi*j) + #assert expint(3) == expint(1,3) + assert expint(1,-20).ae(-25615652.66405658882 - 3.1415926535897932385j) + assert expint(1000000,0).ae(1./999999) + assert expint(0,2+3j).ae(-0.025019798357114678171 + 0.027980439405104419040j) + assert expint(-1,2+3j).ae(-0.022411973626262070419 + 0.038058922011377716932j) + assert expint(-1.5,0) == inf + +def test_trig_integrals(): + mp.dps = 30 + assert si(mpf(1)/1000000).ae('0.000000999999999999944444444444446111') + assert ci(mpf(1)/1000000).ae('-13.2382948930629912435014366276') + assert si(10**10).ae('1.5707963267075846569685111517747537') + assert ci(10**10).ae('-4.87506025174822653785729773959e-11') + assert si(10**100).ae(pi/2) + assert (ci(10**100)*10**100).ae('-0.372376123661276688262086695553') + assert si(-3) == -si(3) + assert ci(-3).ae(ci(3) + pi*j) + # Test complex structure + mp.dps = 15 + assert mp.ci(50).ae(-0.0056283863241163054402) + assert mp.ci(50+2j).ae(-0.018378282946133067149+0.070352808023688336193j) + assert mp.ci(20j).ae(1.28078263320282943611e7+1.5707963267949j) + assert mp.ci(-2+20j).ae(-4.050116856873293505e6+1.207476188206989909e7j) + assert mp.ci(-50+2j).ae(-0.0183782829461330671+3.0712398455661049023j) + assert mp.ci(-50).ae(-0.0056283863241163054+3.1415926535897932385j) + assert mp.ci(-50-2j).ae(-0.0183782829461330671-3.0712398455661049023j) + assert mp.ci(-2-20j).ae(-4.050116856873293505e6-1.207476188206989909e7j) + assert mp.ci(-20j).ae(1.28078263320282943611e7-1.5707963267949j) + assert mp.ci(50-2j).ae(-0.018378282946133067149-0.070352808023688336193j) + assert mp.si(50).ae(1.5516170724859358947) + assert mp.si(50+2j).ae(1.497884414277228461-0.017515007378437448j) + assert mp.si(20j).ae(1.2807826332028294459e7j) + assert mp.si(-2+20j).ae(-1.20747603112735722103e7-4.050116856873293554e6j) + assert mp.si(-50+2j).ae(-1.497884414277228461-0.017515007378437448j) + assert mp.si(-50).ae(-1.5516170724859358947) + assert mp.si(-50-2j).ae(-1.497884414277228461+0.017515007378437448j) + assert mp.si(-2-20j).ae(-1.20747603112735722103e7+4.050116856873293554e6j) + assert mp.si(-20j).ae(-1.2807826332028294459e7j) + assert mp.si(50-2j).ae(1.497884414277228461+0.017515007378437448j) + assert mp.chi(50j).ae(-0.0056283863241163054+1.5707963267948966192j) + assert mp.chi(-2+50j).ae(-0.0183782829461330671+1.6411491348185849554j) + assert mp.chi(-20).ae(1.28078263320282943611e7+3.1415926535898j) + assert mp.chi(-20-2j).ae(-4.050116856873293505e6+1.20747571696809187053e7j) + assert mp.chi(-2-50j).ae(-0.0183782829461330671-1.6411491348185849554j) + assert mp.chi(-50j).ae(-0.0056283863241163054-1.5707963267948966192j) + assert mp.chi(2-50j).ae(-0.0183782829461330671-1.500443518771208283j) + assert mp.chi(20-2j).ae(-4.050116856873293505e6-1.20747603112735722951e7j) + assert mp.chi(20).ae(1.2807826332028294361e7) + assert mp.chi(2+50j).ae(-0.0183782829461330671+1.500443518771208283j) + assert mp.shi(50j).ae(1.5516170724859358947j) + assert mp.shi(-2+50j).ae(0.017515007378437448+1.497884414277228461j) + assert mp.shi(-20).ae(-1.2807826332028294459e7) + assert mp.shi(-20-2j).ae(4.050116856873293554e6-1.20747603112735722103e7j) + assert mp.shi(-2-50j).ae(0.017515007378437448-1.497884414277228461j) + assert mp.shi(-50j).ae(-1.5516170724859358947j) + assert mp.shi(2-50j).ae(-0.017515007378437448-1.497884414277228461j) + assert mp.shi(20-2j).ae(-4.050116856873293554e6-1.20747603112735722103e7j) + assert mp.shi(20).ae(1.2807826332028294459e7) + assert mp.shi(2+50j).ae(-0.017515007378437448+1.497884414277228461j) + def ae(x,y,tol=1e-12): + return abs(x-y) <= abs(y)*tol + assert fp.ci(fp.inf) == 0 + assert ae(fp.ci(fp.ninf), fp.pi*1j) + assert ae(fp.si(fp.inf), fp.pi/2) + assert ae(fp.si(fp.ninf), -fp.pi/2) + assert fp.si(0) == 0 + assert ae(fp.ci(50), -0.0056283863241163054402) + assert ae(fp.ci(50+2j), -0.018378282946133067149+0.070352808023688336193j) + assert ae(fp.ci(20j), 1.28078263320282943611e7+1.5707963267949j) + assert ae(fp.ci(-2+20j), -4.050116856873293505e6+1.207476188206989909e7j) + assert ae(fp.ci(-50+2j), -0.0183782829461330671+3.0712398455661049023j) + assert ae(fp.ci(-50), -0.0056283863241163054+3.1415926535897932385j) + assert ae(fp.ci(-50-2j), -0.0183782829461330671-3.0712398455661049023j) + assert ae(fp.ci(-2-20j), -4.050116856873293505e6-1.207476188206989909e7j) + assert ae(fp.ci(-20j), 1.28078263320282943611e7-1.5707963267949j) + assert ae(fp.ci(50-2j), -0.018378282946133067149-0.070352808023688336193j) + assert ae(fp.si(50), 1.5516170724859358947) + assert ae(fp.si(50+2j), 1.497884414277228461-0.017515007378437448j) + assert ae(fp.si(20j), 1.2807826332028294459e7j) + assert ae(fp.si(-2+20j), -1.20747603112735722103e7-4.050116856873293554e6j) + assert ae(fp.si(-50+2j), -1.497884414277228461-0.017515007378437448j) + assert ae(fp.si(-50), -1.5516170724859358947) + assert ae(fp.si(-50-2j), -1.497884414277228461+0.017515007378437448j) + assert ae(fp.si(-2-20j), -1.20747603112735722103e7+4.050116856873293554e6j) + assert ae(fp.si(-20j), -1.2807826332028294459e7j) + assert ae(fp.si(50-2j), 1.497884414277228461+0.017515007378437448j) + assert ae(fp.chi(50j), -0.0056283863241163054+1.5707963267948966192j) + assert ae(fp.chi(-2+50j), -0.0183782829461330671+1.6411491348185849554j) + assert ae(fp.chi(-20), 1.28078263320282943611e7+3.1415926535898j) + assert ae(fp.chi(-20-2j), -4.050116856873293505e6+1.20747571696809187053e7j) + assert ae(fp.chi(-2-50j), -0.0183782829461330671-1.6411491348185849554j) + assert ae(fp.chi(-50j), -0.0056283863241163054-1.5707963267948966192j) + assert ae(fp.chi(2-50j), -0.0183782829461330671-1.500443518771208283j) + assert ae(fp.chi(20-2j), -4.050116856873293505e6-1.20747603112735722951e7j) + assert ae(fp.chi(20), 1.2807826332028294361e7) + assert ae(fp.chi(2+50j), -0.0183782829461330671+1.500443518771208283j) + assert ae(fp.shi(50j), 1.5516170724859358947j) + assert ae(fp.shi(-2+50j), 0.017515007378437448+1.497884414277228461j) + assert ae(fp.shi(-20), -1.2807826332028294459e7) + assert ae(fp.shi(-20-2j), 4.050116856873293554e6-1.20747603112735722103e7j) + assert ae(fp.shi(-2-50j), 0.017515007378437448-1.497884414277228461j) + assert ae(fp.shi(-50j), -1.5516170724859358947j) + assert ae(fp.shi(2-50j), -0.017515007378437448-1.497884414277228461j) + assert ae(fp.shi(20-2j), -4.050116856873293554e6-1.20747603112735722103e7j) + assert ae(fp.shi(20), 1.2807826332028294459e7) + assert ae(fp.shi(2+50j), -0.017515007378437448+1.497884414277228461j) + +def test_airy(): + mp.dps = 15 + assert (airyai(10)*10**10).ae(1.1047532552898687) + assert (airybi(10)/10**9).ae(0.45564115354822515) + assert (airyai(1000)*10**9158).ae(9.306933063179556004) + assert (airybi(1000)/10**9154).ae(5.4077118391949465477) + assert airyai(-1000).ae(0.055971895773019918842) + assert airybi(-1000).ae(-0.083264574117080633012) + assert (airyai(100+100j)*10**188).ae(2.9099582462207032076 + 2.353013591706178756j) + assert (airybi(100+100j)/10**185).ae(1.7086751714463652039 - 3.1416590020830804578j) + +def test_hyper_0f1(): + mp.dps = 15 + v = 8.63911136507950465 + assert hyper([],[(1,3)],1.5).ae(v) + assert hyper([],[1/3.],1.5).ae(v) + assert hyp0f1(1/3.,1.5).ae(v) + assert hyp0f1((1,3),1.5).ae(v) + # Asymptotic expansion + assert hyp0f1(3,1e9).ae('4.9679055380347771271e+27455') + assert hyp0f1(3,1e9j).ae('-2.1222788784457702157e+19410 + 5.0840597555401854116e+19410j') + +def test_hyper_1f1(): + mp.dps = 15 + v = 1.2917526488617656673 + assert hyper([(1,2)],[(3,2)],0.7).ae(v) + assert hyper([(1,2)],[(3,2)],0.7+0j).ae(v) + assert hyper([0.5],[(3,2)],0.7).ae(v) + assert hyper([0.5],[1.5],0.7).ae(v) + assert hyper([0.5],[(3,2)],0.7+0j).ae(v) + assert hyper([0.5],[1.5],0.7+0j).ae(v) + assert hyper([(1,2)],[1.5+0j],0.7).ae(v) + assert hyper([0.5+0j],[1.5],0.7).ae(v) + assert hyper([0.5+0j],[1.5+0j],0.7+0j).ae(v) + assert hyp1f1(0.5,1.5,0.7).ae(v) + assert hyp1f1((1,2),1.5,0.7).ae(v) + # Asymptotic expansion + assert hyp1f1(2,3,1e10).ae('2.1555012157015796988e+4342944809') + assert (hyp1f1(2,3,1e10j)*10**10).ae(-0.97501205020039745852 - 1.7462392454512132074j) + # Shouldn't use asymptotic expansion + assert hyp1f1(-2, 1, 10000).ae(49980001) + # Bug + assert hyp1f1(1j,fraction(1,3),0.415-69.739j).ae(25.857588206024346592 + 15.738060264515292063j) + +def test_hyper_2f1(): + mp.dps = 15 + v = 1.0652207633823291032 + assert hyper([(1,2), (3,4)], [2], 0.3).ae(v) + assert hyper([(1,2), 0.75], [2], 0.3).ae(v) + assert hyper([0.5, 0.75], [2.0], 0.3).ae(v) + assert hyper([0.5, 0.75], [2.0], 0.3+0j).ae(v) + assert hyper([0.5+0j, (3,4)], [2.0], 0.3+0j).ae(v) + assert hyper([0.5+0j, (3,4)], [2.0], 0.3).ae(v) + assert hyper([0.5, (3,4)], [2.0+0j], 0.3).ae(v) + assert hyper([0.5+0j, 0.75+0j], [2.0+0j], 0.3+0j).ae(v) + v = 1.09234681096223231717 + 0.18104859169479360380j + assert hyper([(1,2),0.75+j], [2], 0.5).ae(v) + assert hyper([0.5,0.75+j], [2.0], 0.5).ae(v) + assert hyper([0.5,0.75+j], [2.0], 0.5+0j).ae(v) + assert hyper([0.5,0.75+j], [2.0+0j], 0.5+0j).ae(v) + v = 0.9625 - 0.125j + assert hyper([(3,2),-1],[4], 0.1+j/3).ae(v) + assert hyper([1.5,-1.0],[4], 0.1+j/3).ae(v) + assert hyper([1.5,-1.0],[4+0j], 0.1+j/3).ae(v) + assert hyper([1.5+0j,-1.0+0j],[4+0j], 0.1+j/3).ae(v) + v = 1.02111069501693445001 - 0.50402252613466859521j + assert hyper([(2,10),(3,10)],[(4,10)],1.5).ae(v) + assert hyper([0.2,(3,10)],[0.4+0j],1.5).ae(v) + assert hyper([0.2,(3,10)],[0.4+0j],1.5+0j).ae(v) + v = 0.76922501362865848528 + 0.32640579593235886194j + assert hyper([(2,10),(3,10)],[(4,10)],4+2j).ae(v) + assert hyper([0.2,(3,10)],[0.4+0j],4+2j).ae(v) + assert hyper([0.2,(3,10)],[(4,10)],4+2j).ae(v) + +def test_hyper_2f1_hard(): + mp.dps = 15 + # Singular cases + assert hyp2f1(2,-1,-1,3).ae(7) + assert hyp2f1(2,-1,-1,3,eliminate_all=True).ae(0.25) + assert hyp2f1(2,-2,-2,3).ae(34) + assert hyp2f1(2,-2,-2,3,eliminate_all=True).ae(0.25) + assert hyp2f1(2,-2,-3,3) == 14 + assert hyp2f1(2,-3,-2,3) == inf + assert hyp2f1(2,-1.5,-1.5,3) == 0.25 + assert hyp2f1(1,2,3,0) == 1 + assert hyp2f1(0,1,0,0) == 1 + assert hyp2f1(0,0,0,0) == 1 + assert isnan(hyp2f1(1,1,0,0)) + assert hyp2f1(2,-1,-5, 0.25+0.25j).ae(1.1+0.1j) + assert hyp2f1(2,-5,-5, 0.25+0.25j, eliminate=False).ae(163./128 + 125./128*j) + assert hyp2f1(0.7235, -1, -5, 0.3).ae(1.04341) + assert hyp2f1(0.7235, -5, -5, 0.3, eliminate=False).ae(1.2939225017815903812) + assert hyp2f1(-1,-2,4,1) == 1.5 + assert hyp2f1(1,2,-3,1) == inf + assert hyp2f1(-2,-2,1,1) == 6 + assert hyp2f1(1,-2,-4,1).ae(5./3) + assert hyp2f1(0,-6,-4,1) == 1 + assert hyp2f1(0,-3,-4,1) == 1 + assert hyp2f1(0,0,0,1) == 1 + assert hyp2f1(1,0,0,1,eliminate=False) == 1 + assert hyp2f1(1,1,0,1) == inf + assert hyp2f1(1,-6,-4,1) == inf + assert hyp2f1(-7.2,-0.5,-4.5,1) == 0 + assert hyp2f1(-7.2,-1,-2,1).ae(-2.6) + assert hyp2f1(1,-0.5,-4.5, 1) == inf + assert hyp2f1(1,0.5,-4.5, 1) == -inf + # Check evaluation on / close to unit circle + z = exp(j*pi/3) + w = (nthroot(2,3)+1)*exp(j*pi/12)/nthroot(3,4)**3 + assert hyp2f1('1/2','1/6','1/3', z).ae(w) + assert hyp2f1('1/2','1/6','1/3', z.conjugate()).ae(w.conjugate()) + assert hyp2f1(0.25, (1,3), 2, '0.999').ae(1.06826449496030635) + assert hyp2f1(0.25, (1,3), 2, '1.001').ae(1.06867299254830309446-0.00001446586793975874j) + assert hyp2f1(0.25, (1,3), 2, -1).ae(0.96656584492524351673) + assert hyp2f1(0.25, (1,3), 2, j).ae(0.99041766248982072266+0.03777135604180735522j) + assert hyp2f1(2,3,5,'0.99').ae(27.699347904322690602) + assert hyp2f1((3,2),-0.5,3,'0.99').ae(0.68403036843911661388) + assert hyp2f1(2,3,5,1j).ae(0.37290667145974386127+0.59210004902748285917j) + assert fsum([hyp2f1((7,10),(2,3),(-1,2), 0.95*exp(j*k)) for k in range(1,15)]).ae(52.851400204289452922+6.244285013912953225j) + assert fsum([hyp2f1((7,10),(2,3),(-1,2), 1.05*exp(j*k)) for k in range(1,15)]).ae(54.506013786220655330-3.000118813413217097j) + assert fsum([hyp2f1((7,10),(2,3),(-1,2), exp(j*k)) for k in range(1,15)]).ae(55.792077935955314887+1.731986485778500241j) + assert hyp2f1(2,2.5,-3.25,0.999).ae(218373932801217082543180041.33) + # Branches + assert hyp2f1(1,1,2,1.01).ae(4.5595744415723676911-3.1104877758314784539j) + assert hyp2f1(1,1,2,1.01+0.1j).ae(2.4149427480552782484+1.4148224796836938829j) + assert hyp2f1(1,1,2,3+4j).ae(0.14576709331407297807+0.48379185417980360773j) + assert hyp2f1(1,1,2,4).ae(-0.27465307216702742285 - 0.78539816339744830962j) + assert hyp2f1(1,1,2,-4).ae(0.40235947810852509365) + # Other: + # Cancellation with a large parameter involved (bug reported on sage-devel) + assert hyp2f1(112, (51,10), (-9,10), -0.99999).ae(-1.6241361047970862961e-24, abs_eps=0, rel_eps=eps*16) + +def test_hyper_3f2_etc(): + assert hyper([1,2,3],[1.5,8],-1).ae(0.67108992351533333030) + assert hyper([1,2,3,4],[5,6,7], -1).ae(0.90232988035425506008) + assert hyper([1,2,3],[1.25,5], 1).ae(28.924181329701905701) + assert hyper([1,2,3,4],[5,6,7],5).ae(1.5192307344006649499-1.1529845225075537461j) + assert hyper([1,2,3,4,5],[6,7,8,9],-1).ae(0.96288759462882357253) + assert hyper([1,2,3,4,5],[6,7,8,9],1).ae(1.0428697385885855841) + assert hyper([1,2,3,4,5],[6,7,8,9],5).ae(1.33980653631074769423-0.07143405251029226699j) + assert hyper([1,2.79,3.08,4.37],[5.2,6.1,7.3],5).ae(1.0996321464692607231-1.7748052293979985001j) + assert hyper([1,1,1],[1,2],1) == inf + assert hyper([1,1,1],[2,(101,100)],1).ae(100.01621213528313220) + # slow -- covered by doctests + #assert hyper([1,1,1],[2,3],0.9999).ae(1.2897972005319693905) + +def test_hyper_u(): + mp.dps = 15 + assert hyperu(2,-3,0).ae(0.05) + assert hyperu(2,-3.5,0).ae(4./99) + assert hyperu(2,0,0) == 0.5 + assert hyperu(-5,1,0) == -120 + assert hyperu(-5,2,0) == inf + assert hyperu(-5,-2,0) == 0 + assert hyperu(7,7,3).ae(0.00014681269365593503986) #exp(3)*gammainc(-6,3) + assert hyperu(2,-3,4).ae(0.011836478100271995559) + assert hyperu(3,4,5).ae(1./125) + assert hyperu(2,3,0.0625) == 256 + assert hyperu(-1,2,0.25+0.5j) == -1.75+0.5j + assert hyperu(0.5,1.5,7.25).ae(2/sqrt(29)) + assert hyperu(2,6,pi).ae(0.55804439825913399130) + assert (hyperu((3,2),8,100+201j)*10**4).ae(-0.3797318333856738798 - 2.9974928453561707782j) + assert (hyperu((5,2),(-1,2),-5000)*10**10).ae(-5.6681877926881664678j) + # XXX: fails because of undetected cancellation in low level series code + # Alternatively: could use asymptotic series here, if convergence test + # tweaked back to recognize this one + #assert (hyperu((5,2),(-1,2),-500)*10**7).ae(-1.82526906001593252847j) + +def test_hyper_2f0(): + mp.dps = 15 + assert hyper([1,2],[],3) == hyp2f0(1,2,3) + assert hyp2f0(2,3,7).ae(0.0116108068639728714668 - 0.0073727413865865802130j) + assert hyp2f0(2,3,0) == 1 + assert hyp2f0(0,0,0) == 1 + assert hyp2f0(-1,-1,1).ae(2) + assert hyp2f0(-4,1,1.5).ae(62.5) + assert hyp2f0(-4,1,50).ae(147029801) + assert hyp2f0(-4,1,0.0001).ae(0.99960011997600240000) + assert hyp2f0(0.5,0.25,0.001).ae(1.0001251174078538115) + assert hyp2f0(0.5,0.25,3+4j).ae(0.85548875824755163518 + 0.21636041283392292973j) + # Important: cancellation check + assert hyp2f0((1,6),(5,6),-0.02371708245126284498).ae(0.996785723120804309) + # Should be exact; polynomial case + assert hyp2f0(-2,1,0.5+0.5j,zeroprec=200) == 0 + assert hyp2f0(1,-2,0.5+0.5j,zeroprec=200) == 0 + # There used to be a bug in thresholds that made one of the following hang + for d in [15, 50, 80]: + mp.dps = d + assert hyp2f0(1.5, 0.5, 0.009).ae('1.006867007239309717945323585695344927904000945829843527398772456281301440034218290443367270629519483 + 1.238277162240704919639384945859073461954721356062919829456053965502443570466701567100438048602352623e-46j') + +def test_hyper_1f2(): + mp.dps = 15 + assert hyper([1],[2,3],4) == hyp1f2(1,2,3,4) + a1,b1,b2 = (1,10),(2,3),1./16 + assert hyp1f2(a1,b1,b2,10).ae(298.7482725554557568) + assert hyp1f2(a1,b1,b2,100).ae(224128961.48602947604) + assert hyp1f2(a1,b1,b2,1000).ae(1.1669528298622675109e+27) + assert hyp1f2(a1,b1,b2,10000).ae(2.4780514622487212192e+86) + assert hyp1f2(a1,b1,b2,100000).ae(1.3885391458871523997e+274) + assert hyp1f2(a1,b1,b2,1000000).ae('9.8851796978960318255e+867') + assert hyp1f2(a1,b1,b2,10**7).ae('1.1505659189516303646e+2746') + assert hyp1f2(a1,b1,b2,10**8).ae('1.4672005404314334081e+8685') + assert hyp1f2(a1,b1,b2,10**20).ae('3.6888217332150976493e+8685889636') + assert hyp1f2(a1,b1,b2,10*j).ae(-16.163252524618572878 - 44.321567896480184312j) + assert hyp1f2(a1,b1,b2,100*j).ae(61938.155294517848171 + 637349.45215942348739j) + assert hyp1f2(a1,b1,b2,1000*j).ae(8455057657257695958.7 + 6261969266997571510.6j) + assert hyp1f2(a1,b1,b2,10000*j).ae(-8.9771211184008593089e+60 + 4.6550528111731631456e+59j) + assert hyp1f2(a1,b1,b2,100000*j).ae(2.6398091437239324225e+193 + 4.1658080666870618332e+193j) + assert hyp1f2(a1,b1,b2,1000000*j).ae('3.5999042951925965458e+613 + 1.5026014707128947992e+613j') + assert hyp1f2(a1,b1,b2,10**7*j).ae('-8.3208715051623234801e+1939 - 3.6752883490851869429e+1941j') + assert hyp1f2(a1,b1,b2,10**8*j).ae('2.0724195707891484454e+6140 - 1.3276619482724266387e+6141j') + assert hyp1f2(a1,b1,b2,10**20*j).ae('-1.1734497974795488504e+6141851462 + 1.1498106965385471542e+6141851462j') + +def test_hyper_2f3(): + mp.dps = 15 + assert hyper([1,2],[3,4,5],6) == hyp2f3(1,2,3,4,5,6) + a1,a2,b1,b2,b3 = (1,10),(2,3),(3,10), 2, 1./16 + # Check asymptotic expansion + assert hyp2f3(a1,a2,b1,b2,b3,10).ae(128.98207160698659976) + assert hyp2f3(a1,a2,b1,b2,b3,1000).ae(6.6309632883131273141e25) + assert hyp2f3(a1,a2,b1,b2,b3,10000).ae(4.6863639362713340539e84) + assert hyp2f3(a1,a2,b1,b2,b3,100000).ae(8.6632451236103084119e271) + assert hyp2f3(a1,a2,b1,b2,b3,10**6).ae('2.0291718386574980641e865') + assert hyp2f3(a1,a2,b1,b2,b3,10**7).ae('7.7639836665710030977e2742') + assert hyp2f3(a1,a2,b1,b2,b3,10**8).ae('3.2537462584071268759e8681') + assert hyp2f3(a1,a2,b1,b2,b3,10**20).ae('1.2966030542911614163e+8685889627') + assert hyp2f3(a1,a2,b1,b2,b3,10*j).ae(-18.551602185587547854 - 13.348031097874113552j) + assert hyp2f3(a1,a2,b1,b2,b3,100*j).ae(78634.359124504488695 + 74459.535945281973996j) + assert hyp2f3(a1,a2,b1,b2,b3,1000*j).ae(597682550276527901.59 - 65136194809352613.078j) + assert hyp2f3(a1,a2,b1,b2,b3,10000*j).ae(-1.1779696326238582496e+59 + 1.2297607505213133872e+59j) + assert hyp2f3(a1,a2,b1,b2,b3,100000*j).ae(2.9844228969804380301e+191 + 7.5587163231490273296e+190j) + assert hyp2f3(a1,a2,b1,b2,b3,1000000*j).ae('7.4859161049322370311e+610 - 2.8467477015940090189e+610j') + assert hyp2f3(a1,a2,b1,b2,b3,10**7*j).ae('-1.7477645579418800826e+1938 - 1.7606522995808116405e+1938j') + assert hyp2f3(a1,a2,b1,b2,b3,10**8*j).ae('-1.6932731942958401784e+6137 - 2.4521909113114629368e+6137j') + assert hyp2f3(a1,a2,b1,b2,b3,10**20*j).ae('-2.0988815677627225449e+6141851451 + 5.7708223542739208681e+6141851452j') + +def test_hyper_2f2(): + mp.dps = 15 + assert hyper([1,2],[3,4],5) == hyp2f2(1,2,3,4,5) + a1,a2,b1,b2 = (3,10),4,(1,2),1./16 + assert hyp2f2(a1,a2,b1,b2,10).ae(448225936.3377556696) + assert hyp2f2(a1,a2,b1,b2,10000).ae('1.2012553712966636711e+4358') + assert hyp2f2(a1,a2,b1,b2,-20000).ae(-0.04182343755661214626) + assert hyp2f2(a1,a2,b1,b2,10**20).ae('1.1148680024303263661e+43429448190325182840') + +def test_orthpoly(): + mp.dps = 15 + assert jacobi(-4,2,3,0.7).ae(22800./4913) + assert jacobi(3,2,4,5.5) == 4133.125 + assert jacobi(1.5,5/6.,4,0).ae(-1.0851951434075508417) + assert jacobi(-2, 1, 2, 4).ae(-0.16) + assert jacobi(2, -1, 2.5, 4).ae(34.59375) + #assert jacobi(2, -1, 2, 4) == 28.5 + assert legendre(5, 7) == 129367 + assert legendre(0.5,0).ae(0.53935260118837935667) + assert legendre(-1,-1) == 1 + assert legendre(0,-1) == 1 + assert legendre(0, 1) == 1 + assert legendre(1, -1) == -1 + assert legendre(7, 1) == 1 + assert legendre(7, -1) == -1 + assert legendre(8,1.5).ae(15457523./32768) + assert legendre(j,-j).ae(2.4448182735671431011 + 0.6928881737669934843j) + assert chebyu(5,1) == 6 + assert chebyt(3,2) == 26 + assert legendre(3.5,-1) == inf + assert legendre(4.5,-1) == -inf + assert legendre(3.5+1j,-1) == mpc(inf,inf) + assert legendre(4.5+1j,-1) == mpc(-inf,-inf) + assert laguerre(4, -2, 3).ae(-1.125) + assert laguerre(3, 1+j, 0.5).ae(0.2291666666666666667 + 2.5416666666666666667j) + +def test_hermite(): + mp.dps = 15 + assert hermite(-2, 0).ae(0.5) + assert hermite(-1, 0).ae(0.88622692545275801365) + assert hermite(0, 0).ae(1) + assert hermite(1, 0) == 0 + assert hermite(2, 0).ae(-2) + assert hermite(0, 2).ae(1) + assert hermite(1, 2).ae(4) + assert hermite(1, -2).ae(-4) + assert hermite(2, -2).ae(14) + assert hermite(0.5, 0).ae(0.69136733903629335053) + assert hermite(9, 0) == 0 + assert hermite(4,4).ae(3340) + assert hermite(3,4).ae(464) + assert hermite(-4,4).ae(0.00018623860287512396181) + assert hermite(-3,4).ae(0.0016540169879668766270) + assert hermite(9, 2.5j).ae(13638725j) + assert hermite(9, -2.5j).ae(-13638725j) + assert hermite(9, 100).ae(511078883759363024000) + assert hermite(9, -100).ae(-511078883759363024000) + assert hermite(9, 100j).ae(512922083920643024000j) + assert hermite(9, -100j).ae(-512922083920643024000j) + assert hermite(-9.5, 2.5j).ae(-2.9004951258126778174e-6 + 1.7601372934039951100e-6j) + assert hermite(-9.5, -2.5j).ae(-2.9004951258126778174e-6 - 1.7601372934039951100e-6j) + assert hermite(-9.5, 100).ae(1.3776300722767084162e-22, abs_eps=0, rel_eps=eps) + assert hermite(-9.5, -100).ae('1.3106082028470671626e4355') + assert hermite(-9.5, 100j).ae(-9.7900218581864768430e-23 - 9.7900218581864768430e-23j, abs_eps=0, rel_eps=eps) + assert hermite(-9.5, -100j).ae(-9.7900218581864768430e-23 + 9.7900218581864768430e-23j, abs_eps=0, rel_eps=eps) + assert hermite(2+3j, -1-j).ae(851.3677063883687676 - 1496.4373467871007997j) + +def test_gegenbauer(): + mp.dps = 15 + assert gegenbauer(1,2,3).ae(12) + assert gegenbauer(2,3,4).ae(381) + assert gegenbauer(0,0,0) == 0 + assert gegenbauer(2,-1,3) == 0 + assert gegenbauer(-7, 0.5, 3).ae(8989) + assert gegenbauer(1, -0.5, 3).ae(-3) + assert gegenbauer(1, -1.5, 3).ae(-9) + assert gegenbauer(1, -0.5, 3).ae(-3) + assert gegenbauer(-0.5, -0.5, 3).ae(-2.6383553159023906245) + assert gegenbauer(2+3j, 1-j, 3+4j).ae(14.880536623203696780 + 20.022029711598032898j) + #assert gegenbauer(-2, -0.5, 3).ae(-12) + +def test_legenp(): + mp.dps = 15 + assert legenp(2,0,4) == legendre(2,4) + assert legenp(-2, -1, 0.5).ae(0.43301270189221932338) + assert legenp(-2, -1, 0.5, type=3).ae(0.43301270189221932338j) + assert legenp(-2, 1, 0.5).ae(-0.86602540378443864676) + assert legenp(2+j, 3+4j, -j).ae(134742.98773236786148 + 429782.72924463851745j) + assert legenp(2+j, 3+4j, -j, type=3).ae(802.59463394152268507 - 251.62481308942906447j) + assert legenp(2,4,3).ae(0) + assert legenp(2,4,3,type=3).ae(0) + assert legenp(2,1,0.5).ae(-1.2990381056766579701) + assert legenp(2,1,0.5,type=3).ae(1.2990381056766579701j) + assert legenp(3,2,3).ae(-360) + assert legenp(3,3,3).ae(240j*2**0.5) + assert legenp(3,4,3).ae(0) + assert legenp(0,0.5,2).ae(0.52503756790433198939 - 0.52503756790433198939j) + assert legenp(-1,-0.5,2).ae(0.60626116232846498110 + 0.60626116232846498110j) + assert legenp(-2,0.5,2).ae(1.5751127037129959682 - 1.5751127037129959682j) + assert legenp(-2,0.5,-0.5).ae(-0.85738275810499171286) + +def test_legenq(): + mp.dps = 15 + f = legenq + # Evaluation at poles + assert isnan(f(3,2,1)) + assert isnan(f(3,2,-1)) + assert isnan(f(3,2,1,type=3)) + assert isnan(f(3,2,-1,type=3)) + # Evaluation at 0 + assert f(0,1,0,type=2).ae(-1) + assert f(-2,2,0,type=2,zeroprec=200).ae(0) + assert f(1.5,3,0,type=2).ae(-2.2239343475841951023) + assert f(0,1,0,type=3).ae(j) + assert f(-2,2,0,type=3,zeroprec=200).ae(0) + assert f(1.5,3,0,type=3).ae(2.2239343475841951022*(1-1j)) + # Standard case, degree 0 + assert f(0,0,-1.5).ae(-0.8047189562170501873 + 1.5707963267948966192j) + assert f(0,0,-0.5).ae(-0.54930614433405484570) + assert f(0,0,0,zeroprec=200).ae(0) + assert f(0,0,0.5).ae(0.54930614433405484570) + assert f(0,0,1.5).ae(0.8047189562170501873 - 1.5707963267948966192j) + assert f(0,0,-1.5,type=3).ae(-0.80471895621705018730) + assert f(0,0,-0.5,type=3).ae(-0.5493061443340548457 - 1.5707963267948966192j) + assert f(0,0,0,type=3).ae(-1.5707963267948966192j) + assert f(0,0,0.5,type=3).ae(0.5493061443340548457 - 1.5707963267948966192j) + assert f(0,0,1.5,type=3).ae(0.80471895621705018730) + # Standard case, degree 1 + assert f(1,0,-1.5).ae(0.2070784343255752810 - 2.3561944901923449288j) + assert f(1,0,-0.5).ae(-0.72534692783297257715) + assert f(1,0,0).ae(-1) + assert f(1,0,0.5).ae(-0.72534692783297257715) + assert f(1,0,1.5).ae(0.2070784343255752810 - 2.3561944901923449288j) + # Standard case, degree 2 + assert f(2,0,-1.5).ae(-0.0635669991240192885 + 4.5160394395353277803j) + assert f(2,0,-0.5).ae(0.81866326804175685571) + assert f(2,0,0,zeroprec=200).ae(0) + assert f(2,0,0.5).ae(-0.81866326804175685571) + assert f(2,0,1.5).ae(0.0635669991240192885 - 4.5160394395353277803j) + # Misc orders and degrees + assert f(2,3,1.5,type=2).ae(-5.7243340223994616228j) + assert f(2,3,1.5,type=3).ae(-5.7243340223994616228) + assert f(2,3,0.5,type=2).ae(-12.316805742712016310) + assert f(2,3,0.5,type=3).ae(-12.316805742712016310j) + assert f(2,3,-1.5,type=2).ae(-5.7243340223994616228j) + assert f(2,3,-1.5,type=3).ae(5.7243340223994616228) + assert f(2,3,-0.5,type=2).ae(-12.316805742712016310) + assert f(2,3,-0.5,type=3).ae(-12.316805742712016310j) + assert f(2+3j, 3+4j, 0.5, type=3).ae(0.0016119404873235186807 - 0.0005885900510718119836j) + assert f(2+3j, 3+4j, -1.5, type=3).ae(0.008451400254138808670 + 0.020645193304593235298j) + assert f(-2.5,1,-1.5).ae(3.9553395527435335749j) + assert f(-2.5,1,-0.5).ae(1.9290561746445456908) + assert f(-2.5,1,0).ae(1.2708196271909686299) + assert f(-2.5,1,0.5).ae(-0.31584812990742202869) + assert f(-2.5,1,1.5).ae(-3.9553395527435335742 + 0.2993235655044701706j) + assert f(-2.5,1,-1.5,type=3).ae(0.29932356550447017254j) + assert f(-2.5,1,-0.5,type=3).ae(-0.3158481299074220287 - 1.9290561746445456908j) + assert f(-2.5,1,0,type=3).ae(1.2708196271909686292 - 1.2708196271909686299j) + assert f(-2.5,1,0.5,type=3).ae(1.9290561746445456907 + 0.3158481299074220287j) + assert f(-2.5,1,1.5,type=3).ae(-0.29932356550447017254) + +def test_agm(): + mp.dps = 15 + assert agm(0,0) == 0 + assert agm(0,1) == 0 + assert agm(1,1) == 1 + assert agm(7,7) == 7 + assert agm(j,j) == j + assert (1/agm(1,sqrt(2))).ae(0.834626841674073186) + assert agm(1,2).ae(1.4567910310469068692) + assert agm(1,3).ae(1.8636167832448965424) + assert agm(1,j).ae(0.599070117367796104+0.599070117367796104j) + assert agm(2) == agm(1,2) + assert agm(-3,4).ae(0.63468509766550907+1.3443087080896272j) + +def test_gammainc(): + mp.dps = 15 + assert gammainc(2,5).ae(6*exp(-5)) + assert gammainc(2,0,5).ae(1-6*exp(-5)) + assert gammainc(2,3,5).ae(-6*exp(-5)+4*exp(-3)) + assert gammainc(-2.5,-0.5).ae(-0.9453087204829418812-5.3164237738936178621j) + assert gammainc(0,2,4).ae(0.045121158298212213088) + assert gammainc(0,3).ae(0.013048381094197037413) + assert gammainc(0,2+j,1-j).ae(0.00910653685850304839-0.22378752918074432574j) + assert gammainc(0,1-j).ae(0.00028162445198141833+0.17932453503935894015j) + assert gammainc(3,4,5,True).ae(0.11345128607046320253) + assert gammainc(3.5,0,inf).ae(gamma(3.5)) + assert gammainc(-150.5,500).ae('6.9825435345798951153e-627') + assert gammainc(-150.5,800).ae('4.6885137549474089431e-788') + assert gammainc(-3.5, -20.5).ae(0.27008820585226911 - 1310.31447140574997636j) + assert gammainc(-3.5, -200.5).ae(0.27008820585226911 - 5.3264597096208368435e76j) # XXX real part + assert gammainc(0,0,2) == inf + assert gammainc(1,b=1).ae(0.6321205588285576784) + assert gammainc(3,2,2) == 0 + assert gammainc(2,3+j,3-j).ae(-0.28135485191849314194j) + assert gammainc(4+0j,1).ae(5.8860710587430771455) + # GH issue #301 + assert gammainc(-1,-1).ae(-0.8231640121031084799 + 3.1415926535897932385j) + assert gammainc(-2,-1).ae(1.7707229202810768576 - 1.5707963267948966192j) + assert gammainc(-3,-1).ae(-1.4963349162467073643 + 0.5235987755982988731j) + assert gammainc(-4,-1).ae(1.05365418617643814992 - 0.13089969389957471827j) + # Regularized upper gamma + assert isnan(gammainc(0, 0, regularized=True)) + assert gammainc(-1, 0, regularized=True) == inf + assert gammainc(1, 0, regularized=True) == 1 + assert gammainc(0, 5, regularized=True) == 0 + assert gammainc(0, 2+3j, regularized=True) == 0 + assert gammainc(0, 5000, regularized=True) == 0 + assert gammainc(0, 10**30, regularized=True) == 0 + assert gammainc(-1, 5, regularized=True) == 0 + assert gammainc(-1, 5000, regularized=True) == 0 + assert gammainc(-1, 10**30, regularized=True) == 0 + assert gammainc(-1, -5, regularized=True) == 0 + assert gammainc(-1, -5000, regularized=True) == 0 + assert gammainc(-1, -10**30, regularized=True) == 0 + assert gammainc(-1, 3+4j, regularized=True) == 0 + assert gammainc(1, 5, regularized=True).ae(exp(-5)) + assert gammainc(1, 5000, regularized=True).ae(exp(-5000)) + assert gammainc(1, 10**30, regularized=True).ae(exp(-10**30)) + assert gammainc(1, 3+4j, regularized=True).ae(exp(-3-4j)) + assert gammainc(-1000000,2).ae('1.3669297209397347754e-301037', abs_eps=0, rel_eps=8*eps) + assert gammainc(-1000000,2,regularized=True) == 0 + assert gammainc(-1000000,3+4j).ae('-1.322575609404222361e-698979 - 4.9274570591854533273e-698978j', abs_eps=0, rel_eps=8*eps) + assert gammainc(-1000000,3+4j,regularized=True) == 0 + assert gammainc(2+3j, 4+5j, regularized=True).ae(0.085422013530993285774-0.052595379150390078503j) + assert gammainc(1000j, 1000j, regularized=True).ae(0.49702647628921131761 + 0.00297355675013575341j) + # Generalized + assert gammainc(3,4,2) == -gammainc(3,2,4) + assert gammainc(4, 2, 3).ae(1.2593494302978947396) + assert gammainc(4, 2, 3, regularized=True).ae(0.20989157171631578993) + assert gammainc(0, 2, 3).ae(0.035852129613864082155) + assert gammainc(0, 2, 3, regularized=True) == 0 + assert gammainc(-1, 2, 3).ae(0.015219822548487616132) + assert gammainc(-1, 2, 3, regularized=True) == 0 + assert gammainc(0, 2, 3).ae(0.035852129613864082155) + assert gammainc(0, 2, 3, regularized=True) == 0 + # Should use upper gammas + assert gammainc(5, 10000, 12000).ae('1.1359381951461801687e-4327', abs_eps=0, rel_eps=8*eps) + # Should use lower gammas + assert gammainc(10000, 2, 3).ae('8.1244514125995785934e4765') + # GH issue 306 + assert gammainc(3,-1-1j) == 0 + assert gammainc(3,-1+1j) == 0 + assert gammainc(2,-1) == 0 + assert gammainc(2,-1+0j) == 0 + assert gammainc(2+0j,-1) == 0 + +def test_gammainc_expint_n(): + # These tests are intended to check all cases of the low-level code + # for upper gamma and expint with small integer index. + # Need to cover positive/negative arguments; small/large/huge arguments + # for both positive and negative indices, as well as indices 0 and 1 + # which may be special-cased + mp.dps = 15 + assert expint(-3,3.5).ae(0.021456366563296693987) + assert expint(-2,3.5).ae(0.014966633183073309405) + assert expint(-1,3.5).ae(0.011092916359219041088) + assert expint(0,3.5).ae(0.0086278238349481430685) + assert expint(1,3.5).ae(0.0069701398575483929193) + assert expint(2,3.5).ae(0.0058018939208991255223) + assert expint(3,3.5).ae(0.0049453773495857807058) + assert expint(-3,-3.5).ae(-4.6618170604073311319) + assert expint(-2,-3.5).ae(-5.5996974157555515963) + assert expint(-1,-3.5).ae(-6.7582555017739415818) + assert expint(0,-3.5).ae(-9.4615577024835182145) + assert expint(1,-3.5).ae(-13.925353995152335292 - 3.1415926535897932385j) + assert expint(2,-3.5).ae(-15.62328702434085977 - 10.995574287564276335j) + assert expint(3,-3.5).ae(-10.783026313250347722 - 19.242255003237483586j) + assert expint(-3,350).ae(2.8614825451252838069e-155, abs_eps=0, rel_eps=8*eps) + assert expint(-2,350).ae(2.8532837224504675901e-155, abs_eps=0, rel_eps=8*eps) + assert expint(-1,350).ae(2.8451316155828634555e-155, abs_eps=0, rel_eps=8*eps) + assert expint(0,350).ae(2.8370258275042797989e-155, abs_eps=0, rel_eps=8*eps) + assert expint(1,350).ae(2.8289659656701459404e-155, abs_eps=0, rel_eps=8*eps) + assert expint(2,350).ae(2.8209516419468505006e-155, abs_eps=0, rel_eps=8*eps) + assert expint(3,350).ae(2.8129824725501272171e-155, abs_eps=0, rel_eps=8*eps) + assert expint(-3,-350).ae(-2.8528796154044839443e+149) + assert expint(-2,-350).ae(-2.8610072121701264351e+149) + assert expint(-1,-350).ae(-2.8691813842677537647e+149) + assert expint(0,-350).ae(-2.8774025343659421709e+149) + u = expint(1,-350) + assert u.ae(-2.8856710698020863568e+149) + assert u.imag.ae(-3.1415926535897932385) + u = expint(2,-350) + assert u.ae(-2.8939874026504650534e+149) + assert u.imag.ae(-1099.5574287564276335) + u = expint(3,-350) + assert u.ae(-2.9023519497915044349e+149) + assert u.imag.ae(-192422.55003237483586) + assert expint(-3,350000000000000000000000).ae('2.1592908471792544286e-152003068666138139677919', abs_eps=0, rel_eps=8*eps) + assert expint(-2,350000000000000000000000).ae('2.1592908471792544286e-152003068666138139677919', abs_eps=0, rel_eps=8*eps) + assert expint(-1,350000000000000000000000).ae('2.1592908471792544286e-152003068666138139677919', abs_eps=0, rel_eps=8*eps) + assert expint(0,350000000000000000000000).ae('2.1592908471792544286e-152003068666138139677919', abs_eps=0, rel_eps=8*eps) + assert expint(1,350000000000000000000000).ae('2.1592908471792544286e-152003068666138139677919', abs_eps=0, rel_eps=8*eps) + assert expint(2,350000000000000000000000).ae('2.1592908471792544286e-152003068666138139677919', abs_eps=0, rel_eps=8*eps) + assert expint(3,350000000000000000000000).ae('2.1592908471792544286e-152003068666138139677919', abs_eps=0, rel_eps=8*eps) + assert expint(-3,-350000000000000000000000).ae('-3.7805306852415755699e+152003068666138139677871') + assert expint(-2,-350000000000000000000000).ae('-3.7805306852415755699e+152003068666138139677871') + assert expint(-1,-350000000000000000000000).ae('-3.7805306852415755699e+152003068666138139677871') + assert expint(0,-350000000000000000000000).ae('-3.7805306852415755699e+152003068666138139677871') + u = expint(1,-350000000000000000000000) + assert u.ae('-3.7805306852415755699e+152003068666138139677871') + assert u.imag.ae(-3.1415926535897932385) + u = expint(2,-350000000000000000000000) + assert u.imag.ae(-1.0995574287564276335e+24) + assert u.ae('-3.7805306852415755699e+152003068666138139677871') + u = expint(3,-350000000000000000000000) + assert u.imag.ae(-1.9242255003237483586e+47) + assert u.ae('-3.7805306852415755699e+152003068666138139677871') + # Small case; no branch cut + assert gammainc(-3,3.5).ae(0.00010020262545203707109) + assert gammainc(-2,3.5).ae(0.00040370427343557393517) + assert gammainc(-1,3.5).ae(0.0016576839773997501492) + assert gammainc(0,3.5).ae(0.0069701398575483929193) + assert gammainc(1,3.5).ae(0.03019738342231850074) + assert gammainc(2,3.5).ae(0.13588822540043325333) + assert gammainc(3,3.5).ae(0.64169439772426814072) + # Small case; with branch cut + assert gammainc(-3,-3.5).ae(0.03595832954467563286 + 0.52359877559829887308j) + assert gammainc(-2,-3.5).ae(-0.88024704597962022221 - 1.5707963267948966192j) + assert gammainc(-1,-3.5).ae(4.4637962926688170771 + 3.1415926535897932385j) + assert gammainc(0,-3.5).ae(-13.925353995152335292 - 3.1415926535897932385j) + assert gammainc(1,-3.5).ae(33.115451958692313751) + assert gammainc(2,-3.5).ae(-82.788629896730784377) + assert gammainc(3,-3.5).ae(240.08702670051927469) + # Asymptotic case; no branch cut + assert gammainc(-3,350).ae(6.5424095113340358813e-163, abs_eps=0, rel_eps=8*eps) + assert gammainc(-2,350).ae(2.296312222489899769e-160, abs_eps=0, rel_eps=8*eps) + assert gammainc(-1,350).ae(8.059861834133858573e-158, abs_eps=0, rel_eps=8*eps) + assert gammainc(0,350).ae(2.8289659656701459404e-155, abs_eps=0, rel_eps=8*eps) + assert gammainc(1,350).ae(9.9295903962649792963e-153, abs_eps=0, rel_eps=8*eps) + assert gammainc(2,350).ae(3.485286229089007733e-150, abs_eps=0, rel_eps=8*eps) + assert gammainc(3,350).ae(1.2233453960006379793e-147, abs_eps=0, rel_eps=8*eps) + # Asymptotic case; branch cut + u = gammainc(-3,-350) + assert u.ae(6.7889565783842895085e+141) + assert u.imag.ae(0.52359877559829887308) + u = gammainc(-2,-350) + assert u.ae(-2.3692668977889832121e+144) + assert u.imag.ae(-1.5707963267948966192) + u = gammainc(-1,-350) + assert u.ae(8.2685354361441858669e+146) + assert u.imag.ae(3.1415926535897932385) + u = gammainc(0,-350) + assert u.ae(-2.8856710698020863568e+149) + assert u.imag.ae(-3.1415926535897932385) + u = gammainc(1,-350) + assert u.ae(1.0070908870280797598e+152) + assert u.imag == 0 + u = gammainc(2,-350) + assert u.ae(-3.5147471957279983618e+154) + assert u.imag == 0 + u = gammainc(3,-350) + assert u.ae(1.2266568422179417091e+157) + assert u.imag == 0 + # Extreme asymptotic case + assert gammainc(-3,350000000000000000000000).ae('5.0362468738874738859e-152003068666138139677990', abs_eps=0, rel_eps=8*eps) + assert gammainc(-2,350000000000000000000000).ae('1.7626864058606158601e-152003068666138139677966', abs_eps=0, rel_eps=8*eps) + assert gammainc(-1,350000000000000000000000).ae('6.1694024205121555102e-152003068666138139677943', abs_eps=0, rel_eps=8*eps) + assert gammainc(0,350000000000000000000000).ae('2.1592908471792544286e-152003068666138139677919', abs_eps=0, rel_eps=8*eps) + assert gammainc(1,350000000000000000000000).ae('7.5575179651273905e-152003068666138139677896', abs_eps=0, rel_eps=8*eps) + assert gammainc(2,350000000000000000000000).ae('2.645131287794586675e-152003068666138139677872', abs_eps=0, rel_eps=8*eps) + assert gammainc(3,350000000000000000000000).ae('9.2579595072810533625e-152003068666138139677849', abs_eps=0, rel_eps=8*eps) + u = gammainc(-3,-350000000000000000000000) + assert u.ae('8.8175642804468234866e+152003068666138139677800') + assert u.imag.ae(0.52359877559829887308) + u = gammainc(-2,-350000000000000000000000) + assert u.ae('-3.0861474981563882203e+152003068666138139677824') + assert u.imag.ae(-1.5707963267948966192) + u = gammainc(-1,-350000000000000000000000) + assert u.ae('1.0801516243547358771e+152003068666138139677848') + assert u.imag.ae(3.1415926535897932385) + u = gammainc(0,-350000000000000000000000) + assert u.ae('-3.7805306852415755699e+152003068666138139677871') + assert u.imag.ae(-3.1415926535897932385) + assert gammainc(1,-350000000000000000000000).ae('1.3231857398345514495e+152003068666138139677895') + assert gammainc(2,-350000000000000000000000).ae('-4.6311500894209300731e+152003068666138139677918') + assert gammainc(3,-350000000000000000000000).ae('1.6209025312973255256e+152003068666138139677942') + +def test_incomplete_beta(): + mp.dps = 15 + assert betainc(-2,-3,0.5,0.75).ae(63.4305673311255413583969) + assert betainc(4.5,0.5+2j,2.5,6).ae(0.2628801146130621387903065 + 0.5162565234467020592855378j) + assert betainc(4,5,0,6).ae(90747.77142857142857142857) + +def test_erf(): + mp.dps = 15 + assert erf(0) == 0 + assert erf(1).ae(0.84270079294971486934) + assert erf(3+4j).ae(-120.186991395079444098 - 27.750337293623902498j) + assert erf(-4-3j).ae(-0.99991066178539168236 + 0.00004972026054496604j) + assert erf(pi).ae(0.99999112385363235839) + assert erf(1j).ae(1.6504257587975428760j) + assert erf(-1j).ae(-1.6504257587975428760j) + assert isinstance(erf(1), mpf) + assert isinstance(erf(-1), mpf) + assert isinstance(erf(0), mpf) + assert isinstance(erf(0j), mpc) + assert erf(inf) == 1 + assert erf(-inf) == -1 + assert erfi(0) == 0 + assert erfi(1/pi).ae(0.371682698493894314) + assert erfi(inf) == inf + assert erfi(-inf) == -inf + assert erf(1+0j) == erf(1) + assert erfc(1+0j) == erfc(1) + assert erf(0.2+0.5j).ae(1 - erfc(0.2+0.5j)) + assert erfc(0) == 1 + assert erfc(1).ae(1-erf(1)) + assert erfc(-1).ae(1-erf(-1)) + assert erfc(1/pi).ae(1-erf(1/pi)) + assert erfc(-10) == 2 + assert erfc(-1000000) == 2 + assert erfc(-inf) == 2 + assert erfc(inf) == 0 + assert isnan(erfc(nan)) + assert (erfc(10**4)*mpf(10)**43429453).ae('3.63998738656420') + assert erf(8+9j).ae(-1072004.2525062051158 + 364149.91954310255423j) + assert erfc(8+9j).ae(1072005.2525062051158 - 364149.91954310255423j) + assert erfc(-8-9j).ae(-1072003.2525062051158 + 364149.91954310255423j) + mp.dps = 50 + # This one does not use the asymptotic series + assert (erfc(10)*10**45).ae('2.0884875837625447570007862949577886115608181193212') + # This one does + assert (erfc(50)*10**1088).ae('2.0709207788416560484484478751657887929322509209954') + mp.dps = 15 + assert str(erfc(10**50)) == '3.66744826532555e-4342944819032518276511289189166050822943970058036665661144537831658646492088707747292249493384317534' + assert erfinv(0) == 0 + assert erfinv(0.5).ae(0.47693627620446987338) + assert erfinv(-0.5).ae(-0.47693627620446987338) + assert erfinv(1) == inf + assert erfinv(-1) == -inf + assert erf(erfinv(0.95)).ae(0.95) + assert erf(erfinv(0.999999999995)).ae(0.999999999995) + assert erf(erfinv(-0.999999999995)).ae(-0.999999999995) + mp.dps = 50 + assert erf(erfinv('0.99999999999999999999999999999995')).ae('0.99999999999999999999999999999995') + assert erf(erfinv('0.999999999999999999999999999999995')).ae('0.999999999999999999999999999999995') + assert erf(erfinv('-0.999999999999999999999999999999995')).ae('-0.999999999999999999999999999999995') + mp.dps = 15 + # Complex asymptotic expansions + v = erfc(50j) + assert v.real == 1 + assert v.imag.ae('-6.1481820666053078736e+1083') + assert erfc(-100+5j).ae(2) + assert (erfc(100+5j)*10**4335).ae(2.3973567853824133572 - 3.9339259530609420597j) + assert erfc(100+100j).ae(0.00065234366376857698698 - 0.0039357263629214118437j) + +def test_pdf(): + mp.dps = 15 + assert npdf(-inf) == 0 + assert npdf(inf) == 0 + assert npdf(5,0,2).ae(npdf(5+4,4,2)) + assert quadts(lambda x: npdf(x,-0.5,0.8), [-inf, inf]) == 1 + assert ncdf(0) == 0.5 + assert ncdf(3,3) == 0.5 + assert ncdf(-inf) == 0 + assert ncdf(inf) == 1 + assert ncdf(10) == 1 + # Verify that this is computed accurately + assert (ncdf(-10)*10**24).ae(7.619853024160526) + +def test_lambertw(): + mp.dps = 15 + assert lambertw(0) == 0 + assert lambertw(0+0j) == 0 + assert lambertw(inf) == inf + assert isnan(lambertw(nan)) + assert lambertw(inf,1).real == inf + assert lambertw(inf,1).imag.ae(2*pi) + assert lambertw(-inf,1).real == inf + assert lambertw(-inf,1).imag.ae(3*pi) + assert lambertw(0,-1) == -inf + assert lambertw(0,1) == -inf + assert lambertw(0,3) == -inf + assert lambertw(e).ae(1) + assert lambertw(1).ae(0.567143290409783873) + assert lambertw(-pi/2).ae(j*pi/2) + assert lambertw(-log(2)/2).ae(-log(2)) + assert lambertw(0.25).ae(0.203888354702240164) + assert lambertw(-0.25).ae(-0.357402956181388903) + assert lambertw(-1./10000,0).ae(-0.000100010001500266719) + assert lambertw(-0.25,-1).ae(-2.15329236411034965) + assert lambertw(0.25,-1).ae(-3.00899800997004620-4.07652978899159763j) + assert lambertw(-0.25,-1).ae(-2.15329236411034965) + assert lambertw(0.25,1).ae(-3.00899800997004620+4.07652978899159763j) + assert lambertw(-0.25,1).ae(-3.48973228422959210+7.41405453009603664j) + assert lambertw(-4).ae(0.67881197132094523+1.91195078174339937j) + assert lambertw(-4,1).ae(-0.66743107129800988+7.76827456802783084j) + assert lambertw(-4,-1).ae(0.67881197132094523-1.91195078174339937j) + assert lambertw(1000).ae(5.24960285240159623) + assert lambertw(1000,1).ae(4.91492239981054535+5.44652615979447070j) + assert lambertw(1000,-1).ae(4.91492239981054535-5.44652615979447070j) + assert lambertw(1000,5).ae(3.5010625305312892+29.9614548941181328j) + assert lambertw(3+4j).ae(1.281561806123775878+0.533095222020971071j) + assert lambertw(-0.4+0.4j).ae(-0.10396515323290657+0.61899273315171632j) + assert lambertw(3+4j,1).ae(-0.11691092896595324+5.61888039871282334j) + assert lambertw(3+4j,-1).ae(0.25856740686699742-3.85211668616143559j) + assert lambertw(-0.5,-1).ae(-0.794023632344689368-0.770111750510379110j) + assert lambertw(-1./10000,1).ae(-11.82350837248724344+6.80546081842002101j) + assert lambertw(-1./10000,-1).ae(-11.6671145325663544) + assert lambertw(-1./10000,-2).ae(-11.82350837248724344-6.80546081842002101j) + assert lambertw(-1./100000,4).ae(-14.9186890769540539+26.1856750178782046j) + assert lambertw(-1./100000,5).ae(-15.0931437726379218666+32.5525721210262290086j) + assert lambertw((2+j)/10).ae(0.173704503762911669+0.071781336752835511j) + assert lambertw((2+j)/10,1).ae(-3.21746028349820063+4.56175438896292539j) + assert lambertw((2+j)/10,-1).ae(-3.03781405002993088-3.53946629633505737j) + assert lambertw((2+j)/10,4).ae(-4.6878509692773249+23.8313630697683291j) + assert lambertw(-(2+j)/10).ae(-0.226933772515757933-0.164986470020154580j) + assert lambertw(-(2+j)/10,1).ae(-2.43569517046110001+0.76974067544756289j) + assert lambertw(-(2+j)/10,-1).ae(-3.54858738151989450-6.91627921869943589j) + assert lambertw(-(2+j)/10,4).ae(-4.5500846928118151+20.6672982215434637j) + mp.dps = 50 + assert lambertw(pi).ae('1.073658194796149172092178407024821347547745350410314531') + mp.dps = 15 + # Former bug in generated branch + assert lambertw(-0.5+0.002j).ae(-0.78917138132659918344 + 0.76743539379990327749j) + assert lambertw(-0.5-0.002j).ae(-0.78917138132659918344 - 0.76743539379990327749j) + assert lambertw(-0.448+0.4j).ae(-0.11855133765652382241 + 0.66570534313583423116j) + assert lambertw(-0.448-0.4j).ae(-0.11855133765652382241 - 0.66570534313583423116j) + assert lambertw(-0.65475+0.0001j).ae(-0.61053421111385310898+1.0396534993944097723803j) + # Huge branch index + w = lambertw(1,10**20) + assert w.real.ae(-47.889578926290259164) + assert w.imag.ae(6.2831853071795864769e+20) + +def test_lambertw_hard(): + def check(x,y): + y = convert(y) + type_ok = True + if isinstance(y, mpf): + type_ok = isinstance(x, mpf) + real_ok = abs(x.real-y.real) <= abs(y.real)*8*eps + imag_ok = abs(x.imag-y.imag) <= abs(y.imag)*8*eps + #print x, y, abs(x.real-y.real), abs(x.imag-y.imag) + return real_ok and imag_ok + # Evaluation near 0 + mp.dps = 15 + assert check(lambertw(1e-10), 9.999999999000000000e-11) + assert check(lambertw(-1e-10), -1.000000000100000000e-10) + assert check(lambertw(1e-10j), 9.999999999999999999733e-21 + 9.99999999999999999985e-11j) + assert check(lambertw(-1e-10j), 9.999999999999999999733e-21 - 9.99999999999999999985e-11j) + assert check(lambertw(1e-10,1), -26.303186778379041559 + 3.265093911703828397j) + assert check(lambertw(-1e-10,1), -26.326236166739163892 + 6.526183280686333315j) + assert check(lambertw(1e-10j,1), -26.312931726911421551 + 4.896366881798013421j) + assert check(lambertw(-1e-10j,1), -26.297238779529035066 + 1.632807161345576513j) + assert check(lambertw(1e-10,-1), -26.303186778379041559 - 3.265093911703828397j) + assert check(lambertw(-1e-10,-1), -26.295238819246925694) + assert check(lambertw(1e-10j,-1), -26.297238779529035028 - 1.6328071613455765135j) + assert check(lambertw(-1e-10j,-1), -26.312931726911421551 - 4.896366881798013421j) + # Test evaluation very close to the branch point -1/e + # on the -1, 0, and 1 branches + add = lambda x, y: fadd(x,y,exact=True) + sub = lambda x, y: fsub(x,y,exact=True) + addj = lambda x, y: fadd(x,fmul(y,1j,exact=True),exact=True) + subj = lambda x, y: fadd(x,fmul(y,-1j,exact=True),exact=True) + mp.dps = 1500 + a = -1/e + 10*eps + d3 = mpf('1e-3') + d10 = mpf('1e-10') + d20 = mpf('1e-20') + d40 = mpf('1e-40') + d80 = mpf('1e-80') + d300 = mpf('1e-300') + d1000 = mpf('1e-1000') + mp.dps = 15 + # ---- Branch 0 ---- + # -1/e + eps + assert check(lambertw(add(a,d3)), -0.92802015005456704876) + assert check(lambertw(add(a,d10)), -0.99997668374140088071) + assert check(lambertw(add(a,d20)), -0.99999999976683560186) + assert lambertw(add(a,d40)) == -1 + assert lambertw(add(a,d80)) == -1 + assert lambertw(add(a,d300)) == -1 + assert lambertw(add(a,d1000)) == -1 + # -1/e - eps + assert check(lambertw(sub(a,d3)), -0.99819016149860989001+0.07367191188934638577j) + assert check(lambertw(sub(a,d10)), -0.9999999998187812114595992+0.0000233164398140346109194j) + assert check(lambertw(sub(a,d20)), -0.99999999999999999998187+2.331643981597124203344e-10j) + assert check(lambertw(sub(a,d40)), -1.0+2.33164398159712420336e-20j) + assert check(lambertw(sub(a,d80)), -1.0+2.33164398159712420336e-40j) + assert check(lambertw(sub(a,d300)), -1.0+2.33164398159712420336e-150j) + assert check(lambertw(sub(a,d1000)), mpc(-1,'2.33164398159712420336e-500')) + # -1/e + eps*j + assert check(lambertw(addj(a,d3)), -0.94790387486938526634+0.05036819639190132490j) + assert check(lambertw(addj(a,d10)), -0.9999835127872943680999899+0.0000164870314895821225256j) + assert check(lambertw(addj(a,d20)), -0.999999999835127872929987+1.64872127051890935830e-10j) + assert check(lambertw(addj(a,d40)), -0.9999999999999999999835+1.6487212707001281468305e-20j) + assert check(lambertw(addj(a,d80)), -1.0 + 1.64872127070012814684865e-40j) + assert check(lambertw(addj(a,d300)), -1.0 + 1.64872127070012814684865e-150j) + assert check(lambertw(addj(a,d1000)), mpc(-1.0,'1.64872127070012814684865e-500')) + # -1/e - eps*j + assert check(lambertw(subj(a,d3)), -0.94790387486938526634-0.05036819639190132490j) + assert check(lambertw(subj(a,d10)), -0.9999835127872943680999899-0.0000164870314895821225256j) + assert check(lambertw(subj(a,d20)), -0.999999999835127872929987-1.64872127051890935830e-10j) + assert check(lambertw(subj(a,d40)), -0.9999999999999999999835-1.6487212707001281468305e-20j) + assert check(lambertw(subj(a,d80)), -1.0 - 1.64872127070012814684865e-40j) + assert check(lambertw(subj(a,d300)), -1.0 - 1.64872127070012814684865e-150j) + assert check(lambertw(subj(a,d1000)), mpc(-1.0,'-1.64872127070012814684865e-500')) + # ---- Branch 1 ---- + assert check(lambertw(addj(a,d3),1), -3.088501303219933378005990 + 7.458676867597474813950098j) + assert check(lambertw(addj(a,d80),1), -3.088843015613043855957087 + 7.461489285654254556906117j) + assert check(lambertw(addj(a,d300),1), -3.088843015613043855957087 + 7.461489285654254556906117j) + assert check(lambertw(addj(a,d1000),1), -3.088843015613043855957087 + 7.461489285654254556906117j) + assert check(lambertw(subj(a,d3),1), -1.0520914180450129534365906 + 0.0539925638125450525673175j) + assert check(lambertw(subj(a,d10),1), -1.0000164872127056318529390 + 0.000016487393927159250398333077j) + assert check(lambertw(subj(a,d20),1), -1.0000000001648721270700128 + 1.64872127088134693542628e-10j) + assert check(lambertw(subj(a,d40),1), -1.000000000000000000016487 + 1.64872127070012814686677e-20j) + assert check(lambertw(subj(a,d80),1), -1.0 + 1.64872127070012814684865e-40j) + assert check(lambertw(subj(a,d300),1), -1.0 + 1.64872127070012814684865e-150j) + assert check(lambertw(subj(a,d1000),1), mpc(-1.0, '1.64872127070012814684865e-500')) + # ---- Branch -1 ---- + # -1/e + eps + assert check(lambertw(add(a,d3),-1), -1.075608941186624989414945) + assert check(lambertw(add(a,d10),-1), -1.000023316621036696460620) + assert check(lambertw(add(a,d20),-1), -1.000000000233164398177834) + assert lambertw(add(a,d40),-1) == -1 + assert lambertw(add(a,d80),-1) == -1 + assert lambertw(add(a,d300),-1) == -1 + assert lambertw(add(a,d1000),-1) == -1 + # -1/e - eps + assert check(lambertw(sub(a,d3),-1), -0.99819016149860989001-0.07367191188934638577j) + assert check(lambertw(sub(a,d10),-1), -0.9999999998187812114595992-0.0000233164398140346109194j) + assert check(lambertw(sub(a,d20),-1), -0.99999999999999999998187-2.331643981597124203344e-10j) + assert check(lambertw(sub(a,d40),-1), -1.0-2.33164398159712420336e-20j) + assert check(lambertw(sub(a,d80),-1), -1.0-2.33164398159712420336e-40j) + assert check(lambertw(sub(a,d300),-1), -1.0-2.33164398159712420336e-150j) + assert check(lambertw(sub(a,d1000),-1), mpc(-1,'-2.33164398159712420336e-500')) + # -1/e + eps*j + assert check(lambertw(addj(a,d3),-1), -1.0520914180450129534365906 - 0.0539925638125450525673175j) + assert check(lambertw(addj(a,d10),-1), -1.0000164872127056318529390 - 0.0000164873939271592503983j) + assert check(lambertw(addj(a,d20),-1), -1.0000000001648721270700 - 1.64872127088134693542628e-10j) + assert check(lambertw(addj(a,d40),-1), -1.00000000000000000001648 - 1.6487212707001281468667726e-20j) + assert check(lambertw(addj(a,d80),-1), -1.0 - 1.64872127070012814684865e-40j) + assert check(lambertw(addj(a,d300),-1), -1.0 - 1.64872127070012814684865e-150j) + assert check(lambertw(addj(a,d1000),-1), mpc(-1.0,'-1.64872127070012814684865e-500')) + # -1/e - eps*j + assert check(lambertw(subj(a,d3),-1), -3.088501303219933378005990-7.458676867597474813950098j) + assert check(lambertw(subj(a,d10),-1), -3.088843015579260686911033-7.461489285372968780020716j) + assert check(lambertw(subj(a,d20),-1), -3.088843015613043855953708-7.461489285654254556877988j) + assert check(lambertw(subj(a,d40),-1), -3.088843015613043855957087-7.461489285654254556906117j) + assert check(lambertw(subj(a,d80),-1), -3.088843015613043855957087 - 7.461489285654254556906117j) + assert check(lambertw(subj(a,d300),-1), -3.088843015613043855957087 - 7.461489285654254556906117j) + assert check(lambertw(subj(a,d1000),-1), -3.088843015613043855957087 - 7.461489285654254556906117j) + # One more case, testing higher precision + mp.dps = 500 + x = -1/e + mpf('1e-13') + ans = "-0.99999926266961377166355784455394913638782494543377383"\ + "744978844374498153493943725364881490261187530235150668593869563"\ + "168276697689459394902153960200361935311512317183678882" + mp.dps = 15 + assert lambertw(x).ae(ans) + mp.dps = 50 + assert lambertw(x).ae(ans) + mp.dps = 150 + assert lambertw(x).ae(ans) + +def test_meijerg(): + mp.dps = 15 + assert meijerg([[2,3],[1]],[[0.5,2],[3,4]], 2.5).ae(4.2181028074787439386) + assert meijerg([[],[1+j]],[[1],[1]], 3+4j).ae(271.46290321152464592 - 703.03330399954820169j) + assert meijerg([[0.25],[1]],[[0.5],[2]],0) == 0 + assert meijerg([[0],[]],[[0,0,'1/3','2/3'], []], '2/27').ae(2.2019391389653314120) + # Verify 1/z series being used + assert meijerg([[-3],[-0.5]], [[-1],[-2.5]], -0.5).ae(-1.338096165935754898687431) + assert meijerg([[1-(-1)],[1-(-2.5)]], [[1-(-3)],[1-(-0.5)]], -2.0).ae(-1.338096165935754898687431) + assert meijerg([[-3],[-0.5]], [[-1],[-2.5]], -1).ae(-(pi+4)/(4*pi)) + a = 2.5 + b = 1.25 + for z in [mpf(0.25), mpf(2)]: + x1 = hyp1f1(a,b,z) + x2 = gamma(b)/gamma(a)*meijerg([[1-a],[]],[[0],[1-b]],-z) + x3 = gamma(b)/gamma(a)*meijerg([[1-0],[1-(1-b)]],[[1-(1-a)],[]],-1/z) + assert x1.ae(x2) + assert x1.ae(x3) + +def test_appellf1(): + mp.dps = 15 + assert appellf1(2,-2,1,1,2,3).ae(-1.75) + assert appellf1(2,1,-2,1,2,3).ae(-8) + assert appellf1(2,1,-2,1,0.5,0.25).ae(1.5) + assert appellf1(-2,1,3,2,3,3).ae(19) + assert appellf1(1,2,3,4,0.5,0.125).ae( 1.53843285792549786518) + +def test_coulomb(): + # Note: most tests are doctests + # Test for a bug: + mp.dps = 15 + assert coulombg(mpc(-5,0),2,3).ae(20.087729487721430394) + +def test_hyper_param_accuracy(): + mp.dps = 15 + As = [n+1e-10 for n in range(-5,-1)] + Bs = [n+1e-10 for n in range(-12,-5)] + assert hyper(As,Bs,10).ae(-381757055858.652671927) + assert legenp(0.5, 100, 0.25).ae(-2.4124576567211311755e+144) + assert (hyp1f1(1000,1,-100)*10**24).ae(5.2589445437370169113) + assert (hyp2f1(10, -900, 10.5, 0.99)*10**24).ae(1.9185370579660768203) + assert (hyp2f1(1000,1.5,-3.5,-1.5)*10**385).ae(-2.7367529051334000764) + assert hyp2f1(-5, 10, 3, 0.5, zeroprec=500) == 0 + assert (hyp1f1(-10000, 1000, 100)*10**424).ae(-3.1046080515824859974) + assert (hyp2f1(1000,1.5,-3.5,-0.75,maxterms=100000)*10**231).ae(-4.0534790813913998643) + assert legenp(2, 3, 0.25) == 0 + pytest.raises(ValueError, lambda: hypercomb(lambda a: [([],[],[],[],[a],[-a],0.5)], [3])) + assert hypercomb(lambda a: [([],[],[],[],[a],[-a],0.5)], [3], infprec=200) == inf + assert meijerg([[],[]],[[0,0,0,0],[]],0.1).ae(1.5680822343832351418) + assert (besselk(400,400)*10**94).ae(1.4387057277018550583) + mp.dps = 5 + (hyp1f1(-5000.5, 1500, 100)*10**185).ae(8.5185229673381935522) + (hyp1f1(-5000, 1500, 100)*10**185).ae(9.1501213424563944311) + mp.dps = 15 + (hyp1f1(-5000.5, 1500, 100)*10**185).ae(8.5185229673381935522) + (hyp1f1(-5000, 1500, 100)*10**185).ae(9.1501213424563944311) + assert hyp0f1(fadd(-20,'1e-100',exact=True), 0.25).ae(1.85014429040102783e+49) + assert hyp0f1((-20*10**100+1, 10**100), 0.25).ae(1.85014429040102783e+49) + +def test_hypercomb_zero_pow(): + # check that 0^0 = 1 + assert hypercomb(lambda a: (([0],[a],[],[],[],[],0),), [0]) == 1 + assert meijerg([[-1.5],[]],[[0],[-0.75]],0).ae(1.4464090846320771425) + +def test_spherharm(): + mp.dps = 15 + t = 0.5; r = 0.25 + assert spherharm(0,0,t,r).ae(0.28209479177387814347) + assert spherharm(1,-1,t,r).ae(0.16048941205971996369 - 0.04097967481096344271j) + assert spherharm(1,0,t,r).ae(0.42878904414183579379) + assert spherharm(1,1,t,r).ae(-0.16048941205971996369 - 0.04097967481096344271j) + assert spherharm(2,-2,t,r).ae(0.077915886919031181734 - 0.042565643022253962264j) + assert spherharm(2,-1,t,r).ae(0.31493387233497459884 - 0.08041582001959297689j) + assert spherharm(2,0,t,r).ae(0.41330596756220761898) + assert spherharm(2,1,t,r).ae(-0.31493387233497459884 - 0.08041582001959297689j) + assert spherharm(2,2,t,r).ae(0.077915886919031181734 + 0.042565643022253962264j) + assert spherharm(3,-3,t,r).ae(0.033640236589690881646 - 0.031339125318637082197j) + assert spherharm(3,-2,t,r).ae(0.18091018743101461963 - 0.09883168583167010241j) + assert spherharm(3,-1,t,r).ae(0.42796713930907320351 - 0.10927795157064962317j) + assert spherharm(3,0,t,r).ae(0.27861659336351639787) + assert spherharm(3,1,t,r).ae(-0.42796713930907320351 - 0.10927795157064962317j) + assert spherharm(3,2,t,r).ae(0.18091018743101461963 + 0.09883168583167010241j) + assert spherharm(3,3,t,r).ae(-0.033640236589690881646 - 0.031339125318637082197j) + assert spherharm(0,-1,t,r) == 0 + assert spherharm(0,-2,t,r) == 0 + assert spherharm(0,1,t,r) == 0 + assert spherharm(0,2,t,r) == 0 + assert spherharm(1,2,t,r) == 0 + assert spherharm(1,3,t,r) == 0 + assert spherharm(1,-2,t,r) == 0 + assert spherharm(1,-3,t,r) == 0 + assert spherharm(2,3,t,r) == 0 + assert spherharm(2,4,t,r) == 0 + assert spherharm(2,-3,t,r) == 0 + assert spherharm(2,-4,t,r) == 0 + assert spherharm(3,4.5,0.5,0.25).ae(-22.831053442240790148 + 10.910526059510013757j) + assert spherharm(2+3j, 1-j, 1+j, 3+4j).ae(-2.6582752037810116935 - 1.0909214905642160211j) + assert spherharm(-6,2.5,t,r).ae(0.39383644983851448178 + 0.28414687085358299021j) + assert spherharm(-3.5, 3, 0.5, 0.25).ae(0.014516852987544698924 - 0.015582769591477628495j) + assert spherharm(-3, 3, 0.5, 0.25) == 0 + assert spherharm(-6, 3, 0.5, 0.25).ae(-0.16544349818782275459 - 0.15412657723253924562j) + assert spherharm(-6, 1.5, 0.5, 0.25).ae(0.032208193499767402477 + 0.012678000924063664921j) + assert spherharm(3,0,0,1).ae(0.74635266518023078283) + assert spherharm(3,-2,0,1) == 0 + assert spherharm(3,-2,1,1).ae(-0.16270707338254028971 - 0.35552144137546777097j) + +def test_qfunctions(): + mp.dps = 15 + assert qp(2,3,100).ae('2.7291482267247332183e2391') + +def test_issue_239(): + mp.prec = 150 + x = ldexp(2476979795053773,-52) + assert betainc(206, 385, 0, 0.55, 1).ae('0.99999999999999999999996570910644857895771110649954') + mp.dps = 15 + pytest.raises(ValueError, lambda: hyp2f1(-5,5,0.5,0.5)) + +# Extra stress testing for Bessel functions +# Reference zeros generated with the aid of scipy.special +# jn_zero, jnp_zero, yn_zero, ynp_zero + +V = 15 +M = 15 + +jn_small_zeros = \ +[[2.4048255576957728, + 5.5200781102863106, + 8.6537279129110122, + 11.791534439014282, + 14.930917708487786, + 18.071063967910923, + 21.211636629879259, + 24.352471530749303, + 27.493479132040255, + 30.634606468431975, + 33.775820213573569, + 36.917098353664044, + 40.058425764628239, + 43.19979171317673, + 46.341188371661814], + [3.8317059702075123, + 7.0155866698156188, + 10.173468135062722, + 13.323691936314223, + 16.470630050877633, + 19.615858510468242, + 22.760084380592772, + 25.903672087618383, + 29.046828534916855, + 32.189679910974404, + 35.332307550083865, + 38.474766234771615, + 41.617094212814451, + 44.759318997652822, + 47.901460887185447], + [5.1356223018406826, + 8.4172441403998649, + 11.619841172149059, + 14.795951782351261, + 17.959819494987826, + 21.116997053021846, + 24.270112313573103, + 27.420573549984557, + 30.569204495516397, + 33.7165195092227, + 36.86285651128381, + 40.008446733478192, + 43.153453778371463, + 46.297996677236919, + 49.442164110416873], + [6.3801618959239835, + 9.7610231299816697, + 13.015200721698434, + 16.223466160318768, + 19.409415226435012, + 22.582729593104442, + 25.748166699294978, + 28.908350780921758, + 32.064852407097709, + 35.218670738610115, + 38.370472434756944, + 41.520719670406776, + 44.669743116617253, + 47.817785691533302, + 50.965029906205183], + [7.5883424345038044, + 11.064709488501185, + 14.37253667161759, + 17.615966049804833, + 20.826932956962388, + 24.01901952477111, + 27.199087765981251, + 30.371007667117247, + 33.537137711819223, + 36.699001128744649, + 39.857627302180889, + 43.01373772335443, + 46.167853512924375, + 49.320360686390272, + 52.471551398458023], + [8.771483815959954, + 12.338604197466944, + 15.700174079711671, + 18.980133875179921, + 22.217799896561268, + 25.430341154222704, + 28.626618307291138, + 31.811716724047763, + 34.988781294559295, + 38.159868561967132, + 41.326383254047406, + 44.489319123219673, + 47.649399806697054, + 50.80716520300633, + 53.963026558378149], + [9.9361095242176849, + 13.589290170541217, + 17.003819667816014, + 20.320789213566506, + 23.58608443558139, + 26.820151983411405, + 30.033722386570469, + 33.233041762847123, + 36.422019668258457, + 39.603239416075404, + 42.778481613199507, + 45.949015998042603, + 49.11577372476426, + 52.279453903601052, + 55.440592068853149], + [11.086370019245084, + 14.821268727013171, + 18.287582832481726, + 21.641541019848401, + 24.934927887673022, + 28.191188459483199, + 31.42279419226558, + 34.637089352069324, + 37.838717382853611, + 41.030773691585537, + 44.21540850526126, + 47.394165755570512, + 50.568184679795566, + 53.738325371963291, + 56.905249991978781], + [12.225092264004655, + 16.037774190887709, + 19.554536430997055, + 22.94517313187462, + 26.266814641176644, + 29.54565967099855, + 32.795800037341462, + 36.025615063869571, + 39.240447995178135, + 42.443887743273558, + 45.638444182199141, + 48.825930381553857, + 52.007691456686903, + 55.184747939289049, + 58.357889025269694], + [13.354300477435331, + 17.241220382489128, + 20.807047789264107, + 24.233885257750552, + 27.583748963573006, + 30.885378967696675, + 34.154377923855096, + 37.400099977156589, + 40.628553718964528, + 43.843801420337347, + 47.048700737654032, + 50.245326955305383, + 53.435227157042058, + 56.619580266508436, + 59.799301630960228], + [14.475500686554541, + 18.433463666966583, + 22.046985364697802, + 25.509450554182826, + 28.887375063530457, + 32.211856199712731, + 35.499909205373851, + 38.761807017881651, + 42.004190236671805, + 45.231574103535045, + 48.447151387269394, + 51.653251668165858, + 54.851619075963349, + 58.043587928232478, + 61.230197977292681], + [15.589847884455485, + 19.61596690396692, + 23.275853726263409, + 26.773322545509539, + 30.17906117878486, + 33.526364075588624, + 36.833571341894905, + 40.111823270954241, + 43.368360947521711, + 46.608132676274944, + 49.834653510396724, + 53.050498959135054, + 56.257604715114484, + 59.457456908388002, + 62.651217388202912], + [16.698249933848246, + 20.789906360078443, + 24.494885043881354, + 28.026709949973129, + 31.45996003531804, + 34.829986990290238, + 38.156377504681354, + 41.451092307939681, + 44.721943543191147, + 47.974293531269048, + 51.211967004101068, + 54.437776928325074, + 57.653844811906946, + 60.8618046824805, + 64.062937824850136], + [17.801435153282442, + 21.95624406783631, + 25.705103053924724, + 29.270630441874802, + 32.731053310978403, + 36.123657666448762, + 39.469206825243883, + 42.780439265447158, + 46.06571091157561, + 49.330780096443524, + 52.579769064383396, + 55.815719876305778, + 59.040934037249271, + 62.257189393731728, + 65.465883797232125], + [18.899997953174024, + 23.115778347252756, + 26.907368976182104, + 30.505950163896036, + 33.993184984781542, + 37.408185128639695, + 40.772827853501868, + 44.100590565798301, + 47.400347780543231, + 50.678236946479898, + 53.93866620912693, + 57.184898598119301, + 60.419409852130297, + 63.644117508962281, + 66.860533012260103]] + +jnp_small_zeros = \ +[[0.0, + 3.8317059702075123, + 7.0155866698156188, + 10.173468135062722, + 13.323691936314223, + 16.470630050877633, + 19.615858510468242, + 22.760084380592772, + 25.903672087618383, + 29.046828534916855, + 32.189679910974404, + 35.332307550083865, + 38.474766234771615, + 41.617094212814451, + 44.759318997652822], + [1.8411837813406593, + 5.3314427735250326, + 8.5363163663462858, + 11.706004902592064, + 14.863588633909033, + 18.015527862681804, + 21.16436985918879, + 24.311326857210776, + 27.457050571059246, + 30.601922972669094, + 33.746182898667383, + 36.889987409236811, + 40.033444053350675, + 43.176628965448822, + 46.319597561173912], + [3.0542369282271403, + 6.7061331941584591, + 9.9694678230875958, + 13.170370856016123, + 16.347522318321783, + 19.512912782488205, + 22.671581772477426, + 25.826037141785263, + 28.977672772993679, + 32.127327020443474, + 35.275535050674691, + 38.422654817555906, + 41.568934936074314, + 44.714553532819734, + 47.859641607992093], + [4.2011889412105285, + 8.0152365983759522, + 11.345924310743006, + 14.585848286167028, + 17.78874786606647, + 20.9724769365377, + 24.144897432909265, + 27.310057930204349, + 30.470268806290424, + 33.626949182796679, + 36.781020675464386, + 39.933108623659488, + 43.083652662375079, + 46.232971081836478, + 49.381300092370349], + [5.3175531260839944, + 9.2823962852416123, + 12.681908442638891, + 15.964107037731551, + 19.196028800048905, + 22.401032267689004, + 25.589759681386733, + 28.767836217666503, + 31.938539340972783, + 35.103916677346764, + 38.265316987088158, + 41.423666498500732, + 44.579623137359257, + 47.733667523865744, + 50.886159153182682], + [6.4156163757002403, + 10.519860873772308, + 13.9871886301403, + 17.312842487884625, + 20.575514521386888, + 23.803581476593863, + 27.01030789777772, + 30.20284907898166, + 33.385443901010121, + 36.560777686880356, + 39.730640230067416, + 42.896273163494417, + 46.058566273567043, + 49.218174614666636, + 52.375591529563596], + [7.501266144684147, + 11.734935953042708, + 15.268181461097873, + 18.637443009666202, + 21.931715017802236, + 25.183925599499626, + 28.409776362510085, + 31.617875716105035, + 34.81339298429743, + 37.999640897715301, + 41.178849474321413, + 44.352579199070217, + 47.521956905768113, + 50.687817781723741, + 53.85079463676896], + [8.5778364897140741, + 12.932386237089576, + 16.529365884366944, + 19.941853366527342, + 23.268052926457571, + 26.545032061823576, + 29.790748583196614, + 33.015178641375142, + 36.224380548787162, + 39.422274578939259, + 42.611522172286684, + 45.793999658055002, + 48.971070951900596, + 52.143752969301988, + 55.312820330403446], + [9.6474216519972168, + 14.115518907894618, + 17.774012366915256, + 21.229062622853124, + 24.587197486317681, + 27.889269427955092, + 31.155326556188325, + 34.39662855427218, + 37.620078044197086, + 40.830178681822041, + 44.030010337966153, + 47.221758471887113, + 50.407020967034367, + 53.586995435398319, + 56.762598475105272], + [10.711433970699945, + 15.28673766733295, + 19.004593537946053, + 22.501398726777283, + 25.891277276839136, + 29.218563499936081, + 32.505247352375523, + 35.763792928808799, + 39.001902811514218, + 42.224638430753279, + 45.435483097475542, + 48.636922645305525, + 51.830783925834728, + 55.01844255063594, + 58.200955824859509], + [11.770876674955582, + 16.447852748486498, + 20.223031412681701, + 23.760715860327448, + 27.182021527190532, + 30.534504754007074, + 33.841965775135715, + 37.118000423665604, + 40.371068905333891, + 43.606764901379516, + 46.828959446564562, + 50.040428970943456, + 53.243223214220535, + 56.438892058982552, + 59.628631306921512], + [12.826491228033465, + 17.600266557468326, + 21.430854238060294, + 25.008518704644261, + 28.460857279654847, + 31.838424458616998, + 35.166714427392629, + 38.460388720328256, + 41.728625562624312, + 44.977526250903469, + 48.211333836373288, + 51.433105171422278, + 54.645106240447105, + 57.849056857839799, + 61.046288512821078], + [13.878843069697276, + 18.745090916814406, + 22.629300302835503, + 26.246047773946584, + 29.72897816891134, + 33.131449953571661, + 36.480548302231658, + 39.791940718940855, + 43.075486800191012, + 46.337772104541405, + 49.583396417633095, + 52.815686826850452, + 56.037118687012179, + 59.249577075517968, + 62.454525995970462], + [14.928374492964716, + 19.88322436109951, + 23.81938909003628, + 27.474339750968247, + 30.987394331665278, + 34.414545662167183, + 37.784378506209499, + 41.113512376883377, + 44.412454519229281, + 47.688252845993366, + 50.945849245830813, + 54.188831071035124, + 57.419876154678179, + 60.641030026538746, + 63.853885828967512], + [15.975438807484321, + 21.015404934568315, + 25.001971500138194, + 28.694271223110755, + 32.236969407878118, + 35.688544091185301, + 39.078998185245057, + 42.425854432866141, + 45.740236776624833, + 49.029635055514276, + 52.299319390331728, + 55.553127779547459, + 58.793933759028134, + 62.02393848337554, + 65.244860767043859]] + +yn_small_zeros = \ +[[0.89357696627916752, + 3.9576784193148579, + 7.0860510603017727, + 10.222345043496417, + 13.361097473872763, + 16.500922441528091, + 19.64130970088794, + 22.782028047291559, + 25.922957653180923, + 29.064030252728398, + 32.205204116493281, + 35.346452305214321, + 38.487756653081537, + 41.629104466213808, + 44.770486607221993], + [2.197141326031017, + 5.4296810407941351, + 8.5960058683311689, + 11.749154830839881, + 14.897442128336725, + 18.043402276727856, + 21.188068934142213, + 24.331942571356912, + 27.475294980449224, + 30.618286491641115, + 33.761017796109326, + 36.90355531614295, + 40.045944640266876, + 43.188218097393211, + 46.330399250701687], + [3.3842417671495935, + 6.7938075132682675, + 10.023477979360038, + 13.209986710206416, + 16.378966558947457, + 19.539039990286384, + 22.69395593890929, + 25.845613720902269, + 28.995080395650151, + 32.143002257627551, + 35.289793869635804, + 38.435733485446343, + 41.581014867297885, + 44.725777117640461, + 47.870122696676504], + [4.5270246611496439, + 8.0975537628604907, + 11.396466739595867, + 14.623077742393873, + 17.81845523294552, + 20.997284754187761, + 24.166235758581828, + 27.328799850405162, + 30.486989604098659, + 33.642049384702463, + 36.794791029185579, + 39.945767226378749, + 43.095367507846703, + 46.2438744334407, + 49.391498015725107], + [5.6451478942208959, + 9.3616206152445429, + 12.730144474090465, + 15.999627085382479, + 19.22442895931681, + 22.424810599698521, + 25.610267054939328, + 28.785893657666548, + 31.954686680031668, + 35.118529525584828, + 38.278668089521758, + 41.435960629910073, + 44.591018225353424, + 47.744288086361052, + 50.896105199722123], + [6.7471838248710219, + 10.597176726782031, + 14.033804104911233, + 17.347086393228382, + 20.602899017175335, + 23.826536030287532, + 27.030134937138834, + 30.220335654231385, + 33.401105611047908, + 36.574972486670962, + 39.743627733020277, + 42.908248189569535, + 46.069679073215439, + 49.228543693445843, + 52.385312123112282], + [7.8377378223268716, + 11.811037107609447, + 15.313615118517857, + 18.670704965906724, + 21.958290897126571, + 25.206207715021249, + 28.429037095235496, + 31.634879502950644, + 34.828638524084437, + 38.013473399691765, + 41.19151880917741, + 44.364272633271975, + 47.53281875312084, + 50.697961822183806, + 53.860312300118388], + [8.919605734873789, + 13.007711435388313, + 16.573915129085334, + 19.974342312352426, + 23.293972585596648, + 26.5667563757203, + 29.809531451608321, + 33.031769327150685, + 36.239265816598239, + 39.435790312675323, + 42.623910919472727, + 45.805442883111651, + 48.981708325514764, + 52.153694518185572, + 55.322154420959698], + [9.9946283820824834, + 14.190361295800141, + 17.817887841179873, + 21.26093227125945, + 24.612576377421522, + 27.910524883974868, + 31.173701563441602, + 34.412862242025045, + 37.634648706110989, + 40.843415321050884, + 44.04214994542435, + 47.232978012841169, + 50.417456447370186, + 53.596753874948731, + 56.771765754432457], + [11.064090256031013, + 15.361301343575925, + 19.047949646361388, + 22.532765416313869, + 25.91620496332662, + 29.2394205079349, + 32.523270869465881, + 35.779715464475261, + 39.016196664616095, + 42.237627509803703, + 45.4474001519274, + 48.647941127433196, + 51.841036928216499, + 55.028034667184916, + 58.209970905250097], + [12.128927704415439, + 16.522284394784426, + 20.265984501212254, + 23.791669719454272, + 27.206568881574774, + 30.555020011020762, + 33.859683872746356, + 37.133649760307504, + 40.385117593813002, + 43.619533085646856, + 46.840676630553575, + 50.051265851897857, + 53.253310556711732, + 56.448332488918971, + 59.637507005589829], + [13.189846995683845, + 17.674674253171487, + 21.473493977824902, + 25.03913093040942, + 28.485081336558058, + 31.858644293774859, + 35.184165245422787, + 38.475796636190897, + 41.742455848758449, + 44.990096293791186, + 48.222870660068338, + 51.443777308699826, + 54.655042589416311, + 57.858358441436511, + 61.055036135780528], + [14.247395665073945, + 18.819555894710682, + 22.671697117872794, + 26.276375544903892, + 29.752925495549038, + 33.151412708998983, + 36.497763772987645, + 39.807134090704376, + 43.089121522203808, + 46.350163579538652, + 49.594769786270069, + 52.82620892320143, + 56.046916910756961, + 59.258751140598783, + 62.463155567737854], + [15.30200785858925, + 19.957808654258601, + 23.861599172945054, + 27.504429642227545, + 31.011103429019229, + 34.434283425782942, + 37.801385632318459, + 41.128514139788358, + 44.425913324440663, + 47.700482714581842, + 50.957073905278458, + 54.199216028087261, + 57.429547607017405, + 60.65008661807661, + 63.862406280068586], + [16.354034360047551, + 21.090156519983806, + 25.044040298785627, + 28.724161640881914, + 32.260472459522644, + 35.708083982611664, + 39.095820003878235, + 42.440684315990936, + 45.75353669045622, + 49.041718113283529, + 52.310408280968073, + 55.56338698149062, + 58.803488508906895, + 62.032886550960831, + 65.253280088312461]] + +ynp_small_zeros = \ +[[2.197141326031017, + 5.4296810407941351, + 8.5960058683311689, + 11.749154830839881, + 14.897442128336725, + 18.043402276727856, + 21.188068934142213, + 24.331942571356912, + 27.475294980449224, + 30.618286491641115, + 33.761017796109326, + 36.90355531614295, + 40.045944640266876, + 43.188218097393211, + 46.330399250701687], + [3.6830228565851777, + 6.9414999536541757, + 10.123404655436613, + 13.285758156782854, + 16.440058007293282, + 19.590241756629495, + 22.738034717396327, + 25.884314618788867, + 29.029575819372535, + 32.174118233366201, + 35.318134458192094, + 38.461753870997549, + 41.605066618873108, + 44.74813744908079, + 47.891014070791065], + [5.0025829314460639, + 8.3507247014130795, + 11.574195465217647, + 14.760909306207676, + 17.931285939466855, + 21.092894504412739, + 24.249231678519058, + 27.402145837145258, + 30.552708880564553, + 33.70158627151572, + 36.849213419846257, + 39.995887376143356, + 43.141817835750686, + 46.287157097544201, + 49.432018469138281], + [6.2536332084598136, + 9.6987879841487711, + 12.972409052292216, + 16.19044719506921, + 19.38238844973613, + 22.559791857764261, + 25.728213194724094, + 28.890678419054777, + 32.048984005266337, + 35.204266606440635, + 38.357281675961019, + 41.508551443818436, + 44.658448731963676, + 47.807246956681162, + 50.95515126455207], + [7.4649217367571329, + 11.005169149809189, + 14.3317235192331, + 17.58443601710272, + 20.801062338411128, + 23.997004122902644, + 27.179886689853435, + 30.353960608554323, + 33.521797098666792, + 36.685048382072301, + 39.844826969405863, + 43.001910515625288, + 46.15685955107263, + 49.310088614282257, + 52.461911043685864], + [8.6495562436971983, + 12.280868725807848, + 15.660799304540377, + 18.949739756016503, + 22.192841809428241, + 25.409072788867674, + 28.608039283077593, + 31.795195353138159, + 34.973890634255288, + 38.14630522169358, + 41.313923188794905, + 44.477791768537617, + 47.638672065035628, + 50.797131066967842, + 53.953600129601663], + [9.8147970120105779, + 13.532811875789828, + 16.965526446046053, + 20.291285512443867, + 23.56186260680065, + 26.799499736027237, + 30.015665481543419, + 33.216968050039509, + 36.407516858984748, + 39.590015243560459, + 42.766320595957378, + 45.937754257017323, + 49.105283450953203, + 52.269633324547373, + 55.431358715604255], + [10.965152105242974, + 14.765687379508912, + 18.250123150217555, + 21.612750053384621, + 24.911310600813573, + 28.171051927637585, + 31.40518108895689, + 34.621401012564177, + 37.824552065973114, + 41.017847386464902, + 44.203512240871601, + 47.3831408366063, + 50.557907466622796, + 53.728697478957026, + 56.896191727313342], + [12.103641941939539, + 15.982840905145284, + 19.517731005559611, + 22.916962141504605, + 26.243700855690533, + 29.525960140695407, + 32.778568197561124, + 36.010261572392516, + 39.226578757802172, + 42.43122493258747, + 45.626783824134354, + 48.815117837929515, + 51.997606404328863, + 55.175294723956816, + 58.348990221754937], + [13.232403808592215, + 17.186756572616758, + 20.770762917490496, + 24.206152448722253, + 27.561059462697153, + 30.866053571250639, + 34.137476603379774, + 37.385039772270268, + 40.614946085165892, + 43.831373184731238, + 47.037251786726299, + 50.234705848765229, + 53.425316228549359, + 56.610286079882087, + 59.790548623216652], + [14.35301374369987, + 18.379337301642568, + 22.011118775283494, + 25.482116178696707, + 28.865046588695164, + 32.192853922166294, + 35.483296655830277, + 38.747005493021857, + 41.990815194320955, + 45.219355876831731, + 48.435892856078888, + 51.642803925173029, + 54.84186659475857, + 58.034439083840155, + 61.221578745109862], + [15.466672066554263, + 19.562077985759503, + 23.240325531101082, + 26.746322986645901, + 30.157042415639891, + 33.507642948240263, + 36.817212798512775, + 40.097251300178642, + 43.355193847719752, + 46.596103410173672, + 49.823567279972794, + 53.040208868780832, + 56.247996968470062, + 59.448441365714251, + 62.642721301357187], + [16.574317035530872, + 20.73617763753932, + 24.459631728238804, + 27.999993668839644, + 31.438208790267783, + 34.811512070805535, + 38.140243708611251, + 41.436725143893739, + 44.708963264433333, + 47.962435051891027, + 51.201037321915983, + 54.427630745992975, + 57.644369734615238, + 60.852911791989989, + 64.054555435720397], + [17.676697936439624, + 21.9026148697762, + 25.670073356263225, + 29.244155124266438, + 32.709534477396028, + 36.105399554497548, + 39.453272918267025, + 42.766255701958017, + 46.052899215578358, + 49.319076602061401, + 52.568982147952547, + 55.805705507386287, + 59.031580956740466, + 62.248409689597653, + 65.457606670836759], + [18.774423978290318, + 23.06220035979272, + 26.872520985976736, + 30.479680663499762, + 33.971869047372436, + 37.390118854896324, + 40.757072537673599, + 44.086572292170345, + 47.387688809191869, + 50.66667461073936, + 53.928009929563275, + 57.175005343085052, + 60.410169281219877, + 63.635442539153021, + 66.85235358587768]] + +@pytest.mark.slow +def test_bessel_zeros_extra(): + mp.dps = 15 + for v in range(V): + for m in range(1,M+1): + print(v, m, "of", V, M) + # Twice to test cache (if used) + assert besseljzero(v,m).ae(jn_small_zeros[v][m-1]) + assert besseljzero(v,m).ae(jn_small_zeros[v][m-1]) + assert besseljzero(v,m,1).ae(jnp_small_zeros[v][m-1]) + assert besseljzero(v,m,1).ae(jnp_small_zeros[v][m-1]) + assert besselyzero(v,m).ae(yn_small_zeros[v][m-1]) + assert besselyzero(v,m).ae(yn_small_zeros[v][m-1]) + assert besselyzero(v,m,1).ae(ynp_small_zeros[v][m-1]) + assert besselyzero(v,m,1).ae(ynp_small_zeros[v][m-1]) diff --git a/lib/python3.12/site-packages/mpmath/tests/test_gammazeta.py b/lib/python3.12/site-packages/mpmath/tests/test_gammazeta.py new file mode 100644 index 0000000000000000000000000000000000000000..6a18a7964d746561dfd5f81177cd78ccc46d2a5d --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/tests/test_gammazeta.py @@ -0,0 +1,698 @@ +from mpmath import * +from mpmath.libmp import round_up, from_float, mpf_zeta_int + +def test_zeta_int_bug(): + assert mpf_zeta_int(0, 10) == from_float(-0.5) + +def test_bernoulli(): + assert bernfrac(0) == (1,1) + assert bernfrac(1) == (-1,2) + assert bernfrac(2) == (1,6) + assert bernfrac(3) == (0,1) + assert bernfrac(4) == (-1,30) + assert bernfrac(5) == (0,1) + assert bernfrac(6) == (1,42) + assert bernfrac(8) == (-1,30) + assert bernfrac(10) == (5,66) + assert bernfrac(12) == (-691,2730) + assert bernfrac(18) == (43867,798) + p, q = bernfrac(228) + assert p % 10**10 == 164918161 + assert q == 625170 + p, q = bernfrac(1000) + assert p % 10**10 == 7950421099 + assert q == 342999030 + mp.dps = 15 + assert bernoulli(0) == 1 + assert bernoulli(1) == -0.5 + assert bernoulli(2).ae(1./6) + assert bernoulli(3) == 0 + assert bernoulli(4).ae(-1./30) + assert bernoulli(5) == 0 + assert bernoulli(6).ae(1./42) + assert str(bernoulli(10)) == '0.0757575757575758' + assert str(bernoulli(234)) == '7.62772793964344e+267' + assert str(bernoulli(10**5)) == '-5.82229431461335e+376755' + assert str(bernoulli(10**8+2)) == '1.19570355039953e+676752584' + + mp.dps = 50 + assert str(bernoulli(10)) == '0.075757575757575757575757575757575757575757575757576' + assert str(bernoulli(234)) == '7.6277279396434392486994969020496121553385863373331e+267' + assert str(bernoulli(10**5)) == '-5.8222943146133508236497045360612887555320691004308e+376755' + assert str(bernoulli(10**8+2)) == '1.1957035503995297272263047884604346914602088317782e+676752584' + + mp.dps = 1000 + assert bernoulli(10).ae(mpf(5)/66) + + mp.dps = 50000 + assert bernoulli(10).ae(mpf(5)/66) + + mp.dps = 15 + +def test_bernpoly_eulerpoly(): + mp.dps = 15 + assert bernpoly(0,-1).ae(1) + assert bernpoly(0,0).ae(1) + assert bernpoly(0,'1/2').ae(1) + assert bernpoly(0,'3/4').ae(1) + assert bernpoly(0,1).ae(1) + assert bernpoly(0,2).ae(1) + assert bernpoly(1,-1).ae('-3/2') + assert bernpoly(1,0).ae('-1/2') + assert bernpoly(1,'1/2').ae(0) + assert bernpoly(1,'3/4').ae('1/4') + assert bernpoly(1,1).ae('1/2') + assert bernpoly(1,2).ae('3/2') + assert bernpoly(2,-1).ae('13/6') + assert bernpoly(2,0).ae('1/6') + assert bernpoly(2,'1/2').ae('-1/12') + assert bernpoly(2,'3/4').ae('-1/48') + assert bernpoly(2,1).ae('1/6') + assert bernpoly(2,2).ae('13/6') + assert bernpoly(3,-1).ae(-3) + assert bernpoly(3,0).ae(0) + assert bernpoly(3,'1/2').ae(0) + assert bernpoly(3,'3/4').ae('-3/64') + assert bernpoly(3,1).ae(0) + assert bernpoly(3,2).ae(3) + assert bernpoly(4,-1).ae('119/30') + assert bernpoly(4,0).ae('-1/30') + assert bernpoly(4,'1/2').ae('7/240') + assert bernpoly(4,'3/4').ae('7/3840') + assert bernpoly(4,1).ae('-1/30') + assert bernpoly(4,2).ae('119/30') + assert bernpoly(5,-1).ae(-5) + assert bernpoly(5,0).ae(0) + assert bernpoly(5,'1/2').ae(0) + assert bernpoly(5,'3/4').ae('25/1024') + assert bernpoly(5,1).ae(0) + assert bernpoly(5,2).ae(5) + assert bernpoly(10,-1).ae('665/66') + assert bernpoly(10,0).ae('5/66') + assert bernpoly(10,'1/2').ae('-2555/33792') + assert bernpoly(10,'3/4').ae('-2555/34603008') + assert bernpoly(10,1).ae('5/66') + assert bernpoly(10,2).ae('665/66') + assert bernpoly(11,-1).ae(-11) + assert bernpoly(11,0).ae(0) + assert bernpoly(11,'1/2').ae(0) + assert bernpoly(11,'3/4').ae('-555731/4194304') + assert bernpoly(11,1).ae(0) + assert bernpoly(11,2).ae(11) + assert eulerpoly(0,-1).ae(1) + assert eulerpoly(0,0).ae(1) + assert eulerpoly(0,'1/2').ae(1) + assert eulerpoly(0,'3/4').ae(1) + assert eulerpoly(0,1).ae(1) + assert eulerpoly(0,2).ae(1) + assert eulerpoly(1,-1).ae('-3/2') + assert eulerpoly(1,0).ae('-1/2') + assert eulerpoly(1,'1/2').ae(0) + assert eulerpoly(1,'3/4').ae('1/4') + assert eulerpoly(1,1).ae('1/2') + assert eulerpoly(1,2).ae('3/2') + assert eulerpoly(2,-1).ae(2) + assert eulerpoly(2,0).ae(0) + assert eulerpoly(2,'1/2').ae('-1/4') + assert eulerpoly(2,'3/4').ae('-3/16') + assert eulerpoly(2,1).ae(0) + assert eulerpoly(2,2).ae(2) + assert eulerpoly(3,-1).ae('-9/4') + assert eulerpoly(3,0).ae('1/4') + assert eulerpoly(3,'1/2').ae(0) + assert eulerpoly(3,'3/4').ae('-11/64') + assert eulerpoly(3,1).ae('-1/4') + assert eulerpoly(3,2).ae('9/4') + assert eulerpoly(4,-1).ae(2) + assert eulerpoly(4,0).ae(0) + assert eulerpoly(4,'1/2').ae('5/16') + assert eulerpoly(4,'3/4').ae('57/256') + assert eulerpoly(4,1).ae(0) + assert eulerpoly(4,2).ae(2) + assert eulerpoly(5,-1).ae('-3/2') + assert eulerpoly(5,0).ae('-1/2') + assert eulerpoly(5,'1/2').ae(0) + assert eulerpoly(5,'3/4').ae('361/1024') + assert eulerpoly(5,1).ae('1/2') + assert eulerpoly(5,2).ae('3/2') + assert eulerpoly(10,-1).ae(2) + assert eulerpoly(10,0).ae(0) + assert eulerpoly(10,'1/2').ae('-50521/1024') + assert eulerpoly(10,'3/4').ae('-36581523/1048576') + assert eulerpoly(10,1).ae(0) + assert eulerpoly(10,2).ae(2) + assert eulerpoly(11,-1).ae('-699/4') + assert eulerpoly(11,0).ae('691/4') + assert eulerpoly(11,'1/2').ae(0) + assert eulerpoly(11,'3/4').ae('-512343611/4194304') + assert eulerpoly(11,1).ae('-691/4') + assert eulerpoly(11,2).ae('699/4') + # Potential accuracy issues + assert bernpoly(10000,10000).ae('5.8196915936323387117e+39999') + assert bernpoly(200,17.5).ae(3.8048418524583064909e244) + assert eulerpoly(200,17.5).ae(-3.7309911582655785929e275) + +def test_gamma(): + mp.dps = 15 + assert gamma(0.25).ae(3.6256099082219083119) + assert gamma(0.0001).ae(9999.4228832316241908) + assert gamma(300).ae('1.0201917073881354535e612') + assert gamma(-0.5).ae(-3.5449077018110320546) + assert gamma(-7.43).ae(0.00026524416464197007186) + #assert gamma(Rational(1,2)) == gamma(0.5) + #assert gamma(Rational(-7,3)).ae(gamma(mpf(-7)/3)) + assert gamma(1+1j).ae(0.49801566811835604271 - 0.15494982830181068512j) + assert gamma(-1+0.01j).ae(-0.422733904013474115 + 99.985883082635367436j) + assert gamma(20+30j).ae(-1453876687.5534810 + 1163777777.8031573j) + # Should always give exact factorials when they can + # be represented as mpfs under the current working precision + fact = 1 + for i in range(1, 18): + assert gamma(i) == fact + fact *= i + for dps in [170, 600]: + fact = 1 + mp.dps = dps + for i in range(1, 105): + assert gamma(i) == fact + fact *= i + mp.dps = 100 + assert gamma(0.5).ae(sqrt(pi)) + mp.dps = 15 + assert factorial(0) == fac(0) == 1 + assert factorial(3) == 6 + assert isnan(gamma(nan)) + assert gamma(1100).ae('4.8579168073569433667e2866') + assert rgamma(0) == 0 + assert rgamma(-1) == 0 + assert rgamma(2) == 1.0 + assert rgamma(3) == 0.5 + assert loggamma(2+8j).ae(-8.5205176753667636926 + 10.8569497125597429366j) + assert loggamma('1e10000').ae('2.302485092994045684017991e10004') + assert loggamma('1e10000j').ae(mpc('-1.570796326794896619231322e10000','2.302485092994045684017991e10004')) + +def test_fac2(): + mp.dps = 15 + assert [fac2(n) for n in range(10)] == [1,1,2,3,8,15,48,105,384,945] + assert fac2(-5).ae(1./3) + assert fac2(-11).ae(-1./945) + assert fac2(50).ae(5.20469842636666623e32) + assert fac2(0.5+0.75j).ae(0.81546769394688069176-0.34901016085573266889j) + assert fac2(inf) == inf + assert isnan(fac2(-inf)) + +def test_gamma_quotients(): + mp.dps = 15 + h = 1e-8 + ep = 1e-4 + G = gamma + assert gammaprod([-1],[-3,-4]) == 0 + assert gammaprod([-1,0],[-5]) == inf + assert abs(gammaprod([-1],[-2]) - G(-1+h)/G(-2+h)) < 1e-4 + assert abs(gammaprod([-4,-3],[-2,0]) - G(-4+h)*G(-3+h)/G(-2+h)/G(0+h)) < 1e-4 + assert rf(3,0) == 1 + assert rf(2.5,1) == 2.5 + assert rf(-5,2) == 20 + assert rf(j,j).ae(gamma(2*j)/gamma(j)) + assert rf('-255.5815971722918','-0.5119253100282322').ae('-0.1952720278805729485') # issue 421 + assert ff(-2,0) == 1 + assert ff(-2,1) == -2 + assert ff(4,3) == 24 + assert ff(3,4) == 0 + assert binomial(0,0) == 1 + assert binomial(1,0) == 1 + assert binomial(0,-1) == 0 + assert binomial(3,2) == 3 + assert binomial(5,2) == 10 + assert binomial(5,3) == 10 + assert binomial(5,5) == 1 + assert binomial(-1,0) == 1 + assert binomial(-2,-4) == 3 + assert binomial(4.5, 1.5) == 6.5625 + assert binomial(1100,1) == 1100 + assert binomial(1100,2) == 604450 + assert beta(1,1) == 1 + assert beta(0,0) == inf + assert beta(3,0) == inf + assert beta(-1,-1) == inf + assert beta(1.5,1).ae(2/3.) + assert beta(1.5,2.5).ae(pi/16) + assert (10**15*beta(10,100)).ae(2.3455339739604649879) + assert beta(inf,inf) == 0 + assert isnan(beta(-inf,inf)) + assert isnan(beta(-3,inf)) + assert isnan(beta(0,inf)) + assert beta(inf,0.5) == beta(0.5,inf) == 0 + assert beta(inf,-1.5) == inf + assert beta(inf,-0.5) == -inf + assert beta(1+2j,-1-j/2).ae(1.16396542451069943086+0.08511695947832914640j) + assert beta(-0.5,0.5) == 0 + assert beta(-3,3).ae(-1/3.) + assert beta('-255.5815971722918','-0.5119253100282322').ae('18.157330562703710339') # issue 421 + +def test_zeta(): + mp.dps = 15 + assert zeta(2).ae(pi**2 / 6) + assert zeta(2.0).ae(pi**2 / 6) + assert zeta(mpc(2)).ae(pi**2 / 6) + assert zeta(100).ae(1) + assert zeta(0).ae(-0.5) + assert zeta(0.5).ae(-1.46035450880958681) + assert zeta(-1).ae(-mpf(1)/12) + assert zeta(-2) == 0 + assert zeta(-3).ae(mpf(1)/120) + assert zeta(-4) == 0 + assert zeta(-100) == 0 + assert isnan(zeta(nan)) + assert zeta(1e-30).ae(-0.5) + assert zeta(-1e-30).ae(-0.5) + # Zeros in the critical strip + assert zeta(mpc(0.5, 14.1347251417346937904)).ae(0) + assert zeta(mpc(0.5, 21.0220396387715549926)).ae(0) + assert zeta(mpc(0.5, 25.0108575801456887632)).ae(0) + assert zeta(mpc(1e-30,1e-40)).ae(-0.5) + assert zeta(mpc(-1e-30,1e-40)).ae(-0.5) + mp.dps = 50 + im = '236.5242296658162058024755079556629786895294952121891237' + assert zeta(mpc(0.5, im)).ae(0, 1e-46) + mp.dps = 15 + # Complex reflection formula + assert (zeta(-60+3j) / 10**34).ae(8.6270183987866146+15.337398548226238j) + # issue #358 + assert zeta(0,0.5) == 0 + assert zeta(0,0) == 0.5 + assert zeta(0,0.5,1).ae(-0.34657359027997265) + # see issue #390 + assert zeta(-1.5,0.5j).ae(-0.13671400162512768475 + 0.11411333638426559139j) + +def test_altzeta(): + mp.dps = 15 + assert altzeta(-2) == 0 + assert altzeta(-4) == 0 + assert altzeta(-100) == 0 + assert altzeta(0) == 0.5 + assert altzeta(-1) == 0.25 + assert altzeta(-3) == -0.125 + assert altzeta(-5) == 0.25 + assert altzeta(-21) == 1180529130.25 + assert altzeta(1).ae(log(2)) + assert altzeta(2).ae(pi**2/12) + assert altzeta(10).ae(73*pi**10/6842880) + assert altzeta(50) < 1 + assert altzeta(60, rounding='d') < 1 + assert altzeta(60, rounding='u') == 1 + assert altzeta(10000, rounding='d') < 1 + assert altzeta(10000, rounding='u') == 1 + assert altzeta(3+0j) == altzeta(3) + s = 3+4j + assert altzeta(s).ae((1-2**(1-s))*zeta(s)) + s = -3+4j + assert altzeta(s).ae((1-2**(1-s))*zeta(s)) + assert altzeta(-100.5).ae(4.58595480083585913e+108) + assert altzeta(1.3).ae(0.73821404216623045) + assert altzeta(1e-30).ae(0.5) + assert altzeta(-1e-30).ae(0.5) + assert altzeta(mpc(1e-30,1e-40)).ae(0.5) + assert altzeta(mpc(-1e-30,1e-40)).ae(0.5) + +def test_zeta_huge(): + mp.dps = 15 + assert zeta(inf) == 1 + mp.dps = 50 + assert zeta(100).ae('1.0000000000000000000000000000007888609052210118073522') + assert zeta(40*pi).ae('1.0000000000000000000000000000000000000148407238666182') + mp.dps = 10000 + v = zeta(33000) + mp.dps = 15 + assert str(v-1) == '1.02363019598118e-9934' + assert zeta(pi*1000, rounding=round_up) > 1 + assert zeta(3000, rounding=round_up) > 1 + assert zeta(pi*1000) == 1 + assert zeta(3000) == 1 + +def test_zeta_negative(): + mp.dps = 150 + a = -pi*10**40 + mp.dps = 15 + assert str(zeta(a)) == '2.55880492708712e+1233536161668617575553892558646631323374078' + mp.dps = 50 + assert str(zeta(a)) == '2.5588049270871154960875033337384432038436330847333e+1233536161668617575553892558646631323374078' + mp.dps = 15 + +def test_polygamma(): + mp.dps = 15 + psi0 = lambda z: psi(0,z) + psi1 = lambda z: psi(1,z) + assert psi0(3) == psi(0,3) == digamma(3) + #assert psi2(3) == psi(2,3) == tetragamma(3) + #assert psi3(3) == psi(3,3) == pentagamma(3) + assert psi0(pi).ae(0.97721330794200673) + assert psi0(-pi).ae(7.8859523853854902) + assert psi0(-pi+1).ae(7.5676424992016996) + assert psi0(pi+j).ae(1.04224048313859376 + 0.35853686544063749j) + assert psi0(-pi-j).ae(1.3404026194821986 - 2.8824392476809402j) + assert findroot(psi0, 1).ae(1.4616321449683622) + assert psi0(1e-10).ae(-10000000000.57722) + assert psi0(1e-40).ae(-1.000000000000000e+40) + assert psi0(1e-10+1e-10j).ae(-5000000000.577215 + 5000000000.000000j) + assert psi0(1e-40+1e-40j).ae(-5.000000000000000e+39 + 5.000000000000000e+39j) + assert psi0(inf) == inf + assert psi1(inf) == 0 + assert psi(2,inf) == 0 + assert psi1(pi).ae(0.37424376965420049) + assert psi1(-pi).ae(53.030438740085385) + assert psi1(pi+j).ae(0.32935710377142464 - 0.12222163911221135j) + assert psi1(-pi-j).ae(-0.30065008356019703 + 0.01149892486928227j) + assert (10**6*psi(4,1+10*pi*j)).ae(-6.1491803479004446 - 0.3921316371664063j) + assert psi0(1+10*pi*j).ae(3.4473994217222650 + 1.5548808324857071j) + assert isnan(psi0(nan)) + assert isnan(psi0(-inf)) + assert psi0(-100.5).ae(4.615124601338064) + assert psi0(3+0j).ae(psi0(3)) + assert psi0(-100+3j).ae(4.6106071768714086321+3.1117510556817394626j) + assert isnan(psi(2,mpc(0,inf))) + assert isnan(psi(2,mpc(0,nan))) + assert isnan(psi(2,mpc(0,-inf))) + assert isnan(psi(2,mpc(1,inf))) + assert isnan(psi(2,mpc(1,nan))) + assert isnan(psi(2,mpc(1,-inf))) + assert isnan(psi(2,mpc(inf,inf))) + assert isnan(psi(2,mpc(nan,nan))) + assert isnan(psi(2,mpc(-inf,-inf))) + mp.dps = 30 + # issue #534 + assert digamma(-0.75+1j).ae(mpc('0.46317279488182026118963809283042317', '2.4821070143037957102007677817351115')) + mp.dps = 15 + +def test_polygamma_high_prec(): + mp.dps = 100 + assert str(psi(0,pi)) == "0.9772133079420067332920694864061823436408346099943256380095232865318105924777141317302075654362928734" + assert str(psi(10,pi)) == "-12.98876181434889529310283769414222588307175962213707170773803550518307617769657562747174101900659238" + +def test_polygamma_identities(): + mp.dps = 15 + psi0 = lambda z: psi(0,z) + psi1 = lambda z: psi(1,z) + psi2 = lambda z: psi(2,z) + assert psi0(0.5).ae(-euler-2*log(2)) + assert psi0(1).ae(-euler) + assert psi1(0.5).ae(0.5*pi**2) + assert psi1(1).ae(pi**2/6) + assert psi1(0.25).ae(pi**2 + 8*catalan) + assert psi2(1).ae(-2*apery) + mp.dps = 20 + u = -182*apery+4*sqrt(3)*pi**3 + mp.dps = 15 + assert psi(2,5/6.).ae(u) + assert psi(3,0.5).ae(pi**4) + +def test_foxtrot_identity(): + # A test of the complex digamma function. + # See http://mathworld.wolfram.com/FoxTrotSeries.html and + # http://mathworld.wolfram.com/DigammaFunction.html + psi0 = lambda z: psi(0,z) + mp.dps = 50 + a = (-1)**fraction(1,3) + b = (-1)**fraction(2,3) + x = -psi0(0.5*a) - psi0(-0.5*b) + psi0(0.5*(1+a)) + psi0(0.5*(1-b)) + y = 2*pi*sech(0.5*sqrt(3)*pi) + assert x.ae(y) + mp.dps = 15 + +def test_polygamma_high_order(): + mp.dps = 100 + assert str(psi(50, pi)) == "-1344100348958402765749252447726432491812.641985273160531055707095989227897753035823152397679626136483" + assert str(psi(50, pi + 14*e)) == "-0.00000000000000000189793739550804321623512073101895801993019919886375952881053090844591920308111549337295143780341396" + assert str(psi(50, pi + 14*e*j)) == ("(-0.0000000000000000522516941152169248975225472155683565752375889510631513244785" + "9377385233700094871256507814151956624433 - 0.00000000000000001813157041407010184" + "702414110218205348527862196327980417757665282244728963891298080199341480881811613j)") + mp.dps = 15 + assert str(psi(50, pi)) == "-1.34410034895841e+39" + assert str(psi(50, pi + 14*e)) == "-1.89793739550804e-18" + assert str(psi(50, pi + 14*e*j)) == "(-5.2251694115217e-17 - 1.81315704140701e-17j)" + +def test_harmonic(): + mp.dps = 15 + assert harmonic(0) == 0 + assert harmonic(1) == 1 + assert harmonic(2) == 1.5 + assert harmonic(3).ae(1. + 1./2 + 1./3) + assert harmonic(10**10).ae(23.603066594891989701) + assert harmonic(10**1000).ae(2303.162308658947) + assert harmonic(0.5).ae(2-2*log(2)) + assert harmonic(inf) == inf + assert harmonic(2+0j) == 1.5+0j + assert harmonic(1+2j).ae(1.4918071802755104+0.92080728264223022j) + +def test_gamma_huge_1(): + mp.dps = 500 + x = mpf(10**10) / 7 + mp.dps = 15 + assert str(gamma(x)) == "6.26075321389519e+12458010678" + mp.dps = 50 + assert str(gamma(x)) == "6.2607532138951929201303779291707455874010420783933e+12458010678" + mp.dps = 15 + +def test_gamma_huge_2(): + mp.dps = 500 + x = mpf(10**100) / 19 + mp.dps = 15 + assert str(gamma(x)) == (\ + "1.82341134776679e+5172997469323364168990133558175077136829182824042201886051511" + "9656908623426021308685461258226190190661") + mp.dps = 50 + assert str(gamma(x)) == (\ + "1.82341134776678875374414910350027596939980412984e+5172997469323364168990133558" + "1750771368291828240422018860515119656908623426021308685461258226190190661") + +def test_gamma_huge_3(): + mp.dps = 500 + x = 10**80 // 3 + 10**70*j / 7 + mp.dps = 15 + y = gamma(x) + assert str(y.real) == (\ + "-6.82925203918106e+2636286142112569524501781477865238132302397236429627932441916" + "056964386399485392600") + assert str(y.imag) == (\ + "8.54647143678418e+26362861421125695245017814778652381323023972364296279324419160" + "56964386399485392600") + mp.dps = 50 + y = gamma(x) + assert str(y.real) == (\ + "-6.8292520391810548460682736226799637356016538421817e+26362861421125695245017814" + "77865238132302397236429627932441916056964386399485392600") + assert str(y.imag) == (\ + "8.5464714367841748507479306948130687511711420234015e+263628614211256952450178147" + "7865238132302397236429627932441916056964386399485392600") + +def test_gamma_huge_4(): + x = 3200+11500j + mp.dps = 15 + assert str(gamma(x)) == \ + "(8.95783268539713e+5164 - 1.94678798329735e+5164j)" + mp.dps = 50 + assert str(gamma(x)) == (\ + "(8.9578326853971339570292952697675570822206567327092e+5164" + " - 1.9467879832973509568895402139429643650329524144794e+51" + "64j)") + mp.dps = 15 + +def test_gamma_huge_5(): + mp.dps = 500 + x = 10**60 * j / 3 + mp.dps = 15 + y = gamma(x) + assert str(y.real) == "-3.27753899634941e-227396058973640224580963937571892628368354580620654233316839" + assert str(y.imag) == "-7.1519888950416e-227396058973640224580963937571892628368354580620654233316841" + mp.dps = 50 + y = gamma(x) + assert str(y.real) == (\ + "-3.2775389963494132168950056995974690946983219123935e-22739605897364022458096393" + "7571892628368354580620654233316839") + assert str(y.imag) == (\ + "-7.1519888950415979749736749222530209713136588885897e-22739605897364022458096393" + "7571892628368354580620654233316841") + mp.dps = 15 + +def test_gamma_huge_7(): + mp.dps = 100 + a = 3 + j/mpf(10)**1000 + mp.dps = 15 + y = gamma(a) + assert str(y.real) == "2.0" + # wrong + #assert str(y.imag) == "2.16735365342606e-1000" + assert str(y.imag) == "1.84556867019693e-1000" + mp.dps = 50 + y = gamma(a) + assert str(y.real) == "2.0" + #assert str(y.imag) == "2.1673536534260596065418805612488708028522563689298e-1000" + assert str(y.imag) == "1.8455686701969342787869758198351951379156813281202e-1000" + +def test_stieltjes(): + mp.dps = 15 + assert stieltjes(0).ae(+euler) + mp.dps = 25 + assert stieltjes(1).ae('-0.07281584548367672486058637587') + assert stieltjes(2).ae('-0.009690363192872318484530386035') + assert stieltjes(3).ae('0.002053834420303345866160046543') + assert stieltjes(4).ae('0.002325370065467300057468170178') + mp.dps = 15 + assert stieltjes(1).ae(-0.07281584548367672486058637587) + assert stieltjes(2).ae(-0.009690363192872318484530386035) + assert stieltjes(3).ae(0.002053834420303345866160046543) + assert stieltjes(4).ae(0.0023253700654673000574681701775) + +def test_barnesg(): + mp.dps = 15 + assert barnesg(0) == barnesg(-1) == 0 + assert [superfac(i) for i in range(8)] == [1, 1, 2, 12, 288, 34560, 24883200, 125411328000] + assert str(superfac(1000)) == '3.24570818422368e+1177245' + assert isnan(barnesg(nan)) + assert isnan(superfac(nan)) + assert isnan(hyperfac(nan)) + assert barnesg(inf) == inf + assert superfac(inf) == inf + assert hyperfac(inf) == inf + assert isnan(superfac(-inf)) + assert barnesg(0.7).ae(0.8068722730141471) + assert barnesg(2+3j).ae(-0.17810213864082169+0.04504542715447838j) + assert [hyperfac(n) for n in range(7)] == [1, 1, 4, 108, 27648, 86400000, 4031078400000] + assert [hyperfac(n) for n in range(0,-7,-1)] == [1,1,-1,-4,108,27648,-86400000] + a = barnesg(-3+0j) + assert a == 0 and isinstance(a, mpc) + a = hyperfac(-3+0j) + assert a == -4 and isinstance(a, mpc) + +def test_polylog(): + mp.dps = 15 + zs = [mpmathify(z) for z in [0, 0.5, 0.99, 4, -0.5, -4, 1j, 3+4j]] + for z in zs: assert polylog(1, z).ae(-log(1-z)) + for z in zs: assert polylog(0, z).ae(z/(1-z)) + for z in zs: assert polylog(-1, z).ae(z/(1-z)**2) + for z in zs: assert polylog(-2, z).ae(z*(1+z)/(1-z)**3) + for z in zs: assert polylog(-3, z).ae(z*(1+4*z+z**2)/(1-z)**4) + assert polylog(3, 7).ae(5.3192579921456754382-5.9479244480803301023j) + assert polylog(3, -7).ae(-4.5693548977219423182) + assert polylog(2, 0.9).ae(1.2997147230049587252) + assert polylog(2, -0.9).ae(-0.75216317921726162037) + assert polylog(2, 0.9j).ae(-0.17177943786580149299+0.83598828572550503226j) + assert polylog(2, 1.1).ae(1.9619991013055685931-0.2994257606855892575j) + assert polylog(2, -1.1).ae(-0.89083809026228260587) + assert polylog(2, 1.1*sqrt(j)).ae(0.58841571107611387722+1.09962542118827026011j) + assert polylog(-2, 0.9).ae(1710) + assert polylog(-2, -0.9).ae(-90/6859.) + assert polylog(3, 0.9).ae(1.0496589501864398696) + assert polylog(-3, 0.9).ae(48690) + assert polylog(-3, -4).ae(-0.0064) + assert polylog(0.5+j/3, 0.5+j/2).ae(0.31739144796565650535 + 0.99255390416556261437j) + assert polylog(3+4j,1).ae(zeta(3+4j)) + assert polylog(3+4j,-1).ae(-altzeta(3+4j)) + # issue 390 + assert polylog(1.5, -48.910886523731889).ae(-6.272992229311817) + assert polylog(1.5, 200).ae(-8.349608319033686529 - 8.159694826434266042j) + assert polylog(-2+0j, -2).ae(mpf(1)/13.5) + assert polylog(-2+0j, 1.25).ae(-180) + +def test_bell_polyexp(): + mp.dps = 15 + # TODO: more tests for polyexp + assert (polyexp(0,1e-10)*10**10).ae(1.00000000005) + assert (polyexp(1,1e-10)*10**10).ae(1.0000000001) + assert polyexp(5,3j).ae(-607.7044517476176454+519.962786482001476087j) + assert polyexp(-1,3.5).ae(12.09537536175543444) + # bell(0,x) = 1 + assert bell(0,0) == 1 + assert bell(0,1) == 1 + assert bell(0,2) == 1 + assert bell(0,inf) == 1 + assert bell(0,-inf) == 1 + assert isnan(bell(0,nan)) + # bell(1,x) = x + assert bell(1,4) == 4 + assert bell(1,0) == 0 + assert bell(1,inf) == inf + assert bell(1,-inf) == -inf + assert isnan(bell(1,nan)) + # bell(2,x) = x*(1+x) + assert bell(2,-1) == 0 + assert bell(2,0) == 0 + # large orders / arguments + assert bell(10) == 115975 + assert bell(10,1) == 115975 + assert bell(10, -8) == 11054008 + assert bell(5,-50) == -253087550 + assert bell(50,-50).ae('3.4746902914629720259e74') + mp.dps = 80 + assert bell(50,-50) == 347469029146297202586097646631767227177164818163463279814268368579055777450 + assert bell(40,50) == 5575520134721105844739265207408344706846955281965031698187656176321717550 + assert bell(74) == 5006908024247925379707076470957722220463116781409659160159536981161298714301202 + mp.dps = 15 + assert bell(10,20j) == 7504528595600+15649605360020j + # continuity of the generalization + assert bell(0.5,0).ae(sinc(pi*0.5)) + +def test_primezeta(): + mp.dps = 15 + assert primezeta(0.9).ae(1.8388316154446882243 + 3.1415926535897932385j) + assert primezeta(4).ae(0.076993139764246844943) + assert primezeta(1) == inf + assert primezeta(inf) == 0 + assert isnan(primezeta(nan)) + +def test_rs_zeta(): + mp.dps = 15 + assert zeta(0.5+100000j).ae(1.0730320148577531321 + 5.7808485443635039843j) + assert zeta(0.75+100000j).ae(1.837852337251873704 + 1.9988492668661145358j) + assert zeta(0.5+1000000j, derivative=3).ae(1647.7744105852674733 - 1423.1270943036622097j) + assert zeta(1+1000000j, derivative=3).ae(3.4085866124523582894 - 18.179184721525947301j) + assert zeta(1+1000000j, derivative=1).ae(-0.10423479366985452134 - 0.74728992803359056244j) + assert zeta(0.5-1000000j, derivative=1).ae(11.636804066002521459 + 17.127254072212996004j) + # Additional sanity tests using fp arithmetic. + # Some more high-precision tests are found in the docstrings + def ae(x, y, tol=1e-6): + return abs(x-y) < tol*abs(y) + assert ae(fp.zeta(0.5-100000j), 1.0730320148577531321 - 5.7808485443635039843j) + assert ae(fp.zeta(0.75-100000j), 1.837852337251873704 - 1.9988492668661145358j) + assert ae(fp.zeta(0.5+1e6j), 0.076089069738227100006 + 2.8051021010192989554j) + assert ae(fp.zeta(0.5+1e6j, derivative=1), 11.636804066002521459 - 17.127254072212996004j) + assert ae(fp.zeta(1+1e6j), 0.94738726251047891048 + 0.59421999312091832833j) + assert ae(fp.zeta(1+1e6j, derivative=1), -0.10423479366985452134 - 0.74728992803359056244j) + assert ae(fp.zeta(0.5+100000j, derivative=1), 10.766962036817482375 - 30.92705282105996714j) + assert ae(fp.zeta(0.5+100000j, derivative=2), -119.40515625740538429 + 217.14780631141830251j) + assert ae(fp.zeta(0.5+100000j, derivative=3), 1129.7550282628460881 - 1685.4736895169690346j) + assert ae(fp.zeta(0.5+100000j, derivative=4), -10407.160819314958615 + 13777.786698628045085j) + assert ae(fp.zeta(0.75+100000j, derivative=1), -0.41742276699594321475 - 6.4453816275049955949j) + assert ae(fp.zeta(0.75+100000j, derivative=2), -9.214314279161977266 + 35.07290795337967899j) + assert ae(fp.zeta(0.75+100000j, derivative=3), 110.61331857820103469 - 236.87847130518129926j) + assert ae(fp.zeta(0.75+100000j, derivative=4), -1054.334275898559401 + 1769.9177890161596383j) + +def test_siegelz(): + mp.dps = 15 + assert siegelz(100000).ae(5.87959246868176504171) + assert siegelz(100000, derivative=2).ae(-54.1172711010126452832) + assert siegelz(100000, derivative=3).ae(-278.930831343966552538) + assert siegelz(100000+j,derivative=1).ae(678.214511857070283307-379.742160779916375413j) + + + +def test_zeta_near_1(): + # Test for a former bug in mpf_zeta and mpc_zeta + mp.dps = 15 + s1 = fadd(1, '1e-10', exact=True) + s2 = fadd(1, '-1e-10', exact=True) + s3 = fadd(1, '1e-10j', exact=True) + assert zeta(s1).ae(1.000000000057721566490881444e10) + assert zeta(s2).ae(-9.99999999942278433510574872e9) + z = zeta(s3) + assert z.real.ae(0.57721566490153286060) + assert z.imag.ae(-9.9999999999999999999927184e9) + mp.dps = 30 + s1 = fadd(1, '1e-50', exact=True) + s2 = fadd(1, '-1e-50', exact=True) + s3 = fadd(1, '1e-50j', exact=True) + assert zeta(s1).ae('1e50') + assert zeta(s2).ae('-1e50') + z = zeta(s3) + assert z.real.ae('0.57721566490153286060651209008240243104215933593992') + assert z.imag.ae('-1e50') diff --git a/lib/python3.12/site-packages/mpmath/tests/test_hp.py b/lib/python3.12/site-packages/mpmath/tests/test_hp.py new file mode 100644 index 0000000000000000000000000000000000000000..9eba0af798f64ac3f8d464e2d3bf231567a48c9b --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/tests/test_hp.py @@ -0,0 +1,291 @@ +""" +Check that the output from irrational functions is accurate for +high-precision input, from 5 to 200 digits. The reference values were +verified with Mathematica. +""" + +import time +from mpmath import * + +precs = [5, 15, 28, 35, 57, 80, 100, 150, 200] + +# sqrt(3) + pi/2 +a = \ +"3.302847134363773912758768033145623809041389953497933538543279275605"\ +"841220051904536395163599428307109666700184672047856353516867399774243594"\ +"67433521615861420725323528325327484262075464241255915238845599752675" + +# e + 1/euler**2 +b = \ +"5.719681166601007617111261398629939965860873957353320734275716220045750"\ +"31474116300529519620938123730851145473473708966080207482581266469342214"\ +"824842256999042984813905047895479210702109260221361437411947323431" + +# sqrt(a) +sqrt_a = \ +"1.817373691447021556327498239690365674922395036495564333152483422755"\ +"144321726165582817927383239308173567921345318453306994746434073691275094"\ +"484777905906961689902608644112196725896908619756404253109722911487" + +# sqrt(a+b*i).real +sqrt_abi_real = \ +"2.225720098415113027729407777066107959851146508557282707197601407276"\ +"89160998185797504198062911768240808839104987021515555650875977724230130"\ +"3584116233925658621288393930286871862273400475179312570274423840384" + +# sqrt(a+b*i).imag +sqrt_abi_imag = \ +"1.2849057639084690902371581529110949983261182430040898147672052833653668"\ +"0629534491275114877090834296831373498336559849050755848611854282001250"\ +"1924311019152914021365263161630765255610885489295778894976075186" + +# log(a) +log_a = \ +"1.194784864491089550288313512105715261520511949410072046160598707069"\ +"4336653155025770546309137440687056366757650909754708302115204338077595203"\ +"83005773986664564927027147084436553262269459110211221152925732612" + +# log(a+b*i).real +log_abi_real = \ +"1.8877985921697018111624077550443297276844736840853590212962006811663"\ +"04949387789489704203167470111267581371396245317618589339274243008242708"\ +"014251531496104028712866224020066439049377679709216784954509456421" + +# log(a+b*i).imag +log_abi_imag = \ +"1.0471204952840802663567714297078763189256357109769672185219334169734948"\ +"4265809854092437285294686651806426649541504240470168212723133326542181"\ +"8300136462287639956713914482701017346851009323172531601894918640" + +# exp(a) +exp_a = \ +"27.18994224087168661137253262213293847994194869430518354305430976149"\ +"382792035050358791398632888885200049857986258414049540376323785711941636"\ +"100358982497583832083513086941635049329804685212200507288797531143" + +# exp(a+b*i).real +exp_abi_real = \ +"22.98606617170543596386921087657586890620262522816912505151109385026"\ +"40160179326569526152851983847133513990281518417211964710397233157168852"\ +"4963130831190142571659948419307628119985383887599493378056639916701" + +# exp(a+b*i).imag +exp_abi_imag = \ +"-14.523557450291489727214750571590272774669907424478129280902375851196283"\ +"3377162379031724734050088565710975758824441845278120105728824497308303"\ +"6065619788140201636218705414429933685889542661364184694108251449" + +# a**b +pow_a_b = \ +"928.7025342285568142947391505837660251004990092821305668257284426997"\ +"361966028275685583421197860603126498884545336686124793155581311527995550"\ +"580229264427202446131740932666832138634013168125809402143796691154" + +# (a**(a+b*i)).real +pow_a_abi_real = \ +"44.09156071394489511956058111704382592976814280267142206420038656267"\ +"67707916510652790502399193109819563864568986234654864462095231138500505"\ +"8197456514795059492120303477512711977915544927440682508821426093455" + +# (a**(a+b*i)).imag +pow_a_abi_imag = \ +"27.069371511573224750478105146737852141664955461266218367212527612279886"\ +"9322304536553254659049205414427707675802193810711302947536332040474573"\ +"8166261217563960235014674118610092944307893857862518964990092301" + +# ((a+b*i)**(a+b*i)).real +pow_abi_abi_real = \ +"-0.15171310677859590091001057734676423076527145052787388589334350524"\ +"8084195882019497779202452975350579073716811284169068082670778986235179"\ +"0813026562962084477640470612184016755250592698408112493759742219150452"\ + +# ((a+b*i)**(a+b*i)).imag +pow_abi_abi_imag = \ +"1.2697592504953448936553147870155987153192995316950583150964099070426"\ +"4736837932577176947632535475040521749162383347758827307504526525647759"\ +"97547638617201824468382194146854367480471892602963428122896045019902" + +# sin(a) +sin_a = \ +"-0.16055653857469062740274792907968048154164433772938156243509084009"\ +"38437090841460493108570147191289893388608611542655654723437248152535114"\ +"528368009465836614227575701220612124204622383149391870684288862269631" + +# sin(1000*a) +sin_1000a = \ +"-0.85897040577443833776358106803777589664322997794126153477060795801"\ +"09151695416961724733492511852267067419573754315098042850381158563024337"\ +"216458577140500488715469780315833217177634490142748614625281171216863" + +# sin(a+b*i) +sin_abi_real = \ +"-24.4696999681556977743346798696005278716053366404081910969773939630"\ +"7149215135459794473448465734589287491880563183624997435193637389884206"\ +"02151395451271809790360963144464736839412254746645151672423256977064" + +sin_abi_imag = \ +"-150.42505378241784671801405965872972765595073690984080160750785565810981"\ +"8314482499135443827055399655645954830931316357243750839088113122816583"\ +"7169201254329464271121058839499197583056427233866320456505060735" + +# cos +cos_a = \ +"-0.98702664499035378399332439243967038895709261414476495730788864004"\ +"05406821549361039745258003422386169330787395654908532996287293003581554"\ +"257037193284199198069707141161341820684198547572456183525659969145501" + +cos_1000a = \ +"-0.51202523570982001856195696460663971099692261342827540426136215533"\ +"52686662667660613179619804463250686852463876088694806607652218586060613"\ +"951310588158830695735537073667299449753951774916401887657320950496820" + +# tan +tan_a = \ +"0.162666873675188117341401059858835168007137819495998960250142156848"\ +"639654718809412181543343168174807985559916643549174530459883826451064966"\ +"7996119428949951351938178809444268785629011625179962457123195557310" + +tan_abi_real = \ +"6.822696615947538488826586186310162599974827139564433912601918442911"\ +"1026830824380070400102213741875804368044342309515353631134074491271890"\ +"467615882710035471686578162073677173148647065131872116479947620E-6" + +tan_abi_imag = \ +"0.9999795833048243692245661011298447587046967777739649018690797625964167"\ +"1446419978852235960862841608081413169601038230073129482874832053357571"\ +"62702259309150715669026865777947502665936317953101462202542168429" + + +def test_hp(): + for dps in precs: + mp.dps = dps + 8 + aa = mpf(a) + bb = mpf(b) + a1000 = 1000*mpf(a) + abi = mpc(aa, bb) + mp.dps = dps + assert (sqrt(3) + pi/2).ae(aa) + assert (e + 1/euler**2).ae(bb) + + assert sqrt(aa).ae(mpf(sqrt_a)) + assert sqrt(abi).ae(mpc(sqrt_abi_real, sqrt_abi_imag)) + + assert log(aa).ae(mpf(log_a)) + assert log(abi).ae(mpc(log_abi_real, log_abi_imag)) + + assert exp(aa).ae(mpf(exp_a)) + assert exp(abi).ae(mpc(exp_abi_real, exp_abi_imag)) + + assert (aa**bb).ae(mpf(pow_a_b)) + assert (aa**abi).ae(mpc(pow_a_abi_real, pow_a_abi_imag)) + assert (abi**abi).ae(mpc(pow_abi_abi_real, pow_abi_abi_imag)) + + assert sin(a).ae(mpf(sin_a)) + assert sin(a1000).ae(mpf(sin_1000a)) + assert sin(abi).ae(mpc(sin_abi_real, sin_abi_imag)) + + assert cos(a).ae(mpf(cos_a)) + assert cos(a1000).ae(mpf(cos_1000a)) + + assert tan(a).ae(mpf(tan_a)) + assert tan(abi).ae(mpc(tan_abi_real, tan_abi_imag)) + + # check that complex cancellation is avoided so that both + # real and imaginary parts have high relative accuracy. + # abs_eps should be 0, but has to be set to 1e-205 to pass the + # 200-digit case, probably due to slight inaccuracy in the + # precomputed input + assert (tan(abi).real).ae(mpf(tan_abi_real), abs_eps=1e-205) + assert (tan(abi).imag).ae(mpf(tan_abi_imag), abs_eps=1e-205) + mp.dps = 460 + assert str(log(3))[-20:] == '02166121184001409826' + mp.dps = 15 + +# Since str(a) can differ in the last digit from rounded a, and I want +# to compare the last digits of big numbers with the results in Mathematica, +# I made this hack to get the last 20 digits of rounded a + +def last_digits(a): + r = repr(a) + s = str(a) + #dps = mp.dps + #mp.dps += 3 + m = 10 + r = r.replace(s[:-m],'') + r = r.replace("mpf('",'').replace("')",'') + num0 = 0 + for c in r: + if c == '0': + num0 += 1 + else: + break + b = float(int(r))/10**(len(r) - m) + if b >= 10**m - 0.5: # pragma: no cover + raise NotImplementedError + n = int(round(b)) + sn = str(n) + s = s[:-m] + '0'*num0 + sn + return s[-20:] + +# values checked with Mathematica +def test_log_hp(): + mp.dps = 2000 + a = mpf(10)**15000/3 + r = log(a) + res = last_digits(r) + # Mathematica N[Log[10^15000/3], 2000] + # ...7443804441768333470331 + assert res == '43804441768333470331' + + # see issue 145 + r = log(mpf(3)/2) + # Mathematica N[Log[3/2], 2000] + # ...69653749808140753263288 + res = last_digits(r) + assert res == '53749808140753263288' + + mp.dps = 10000 + r = log(2) + res = last_digits(r) + # Mathematica N[Log[2], 10000] + # ...695615913401856601359655561 + assert res == '13401856601359655561' + r = log(mpf(10)**10/3) + res = last_digits(r) + # Mathematica N[Log[10^10/3], 10000] + # ...587087654020631943060007154 + assert res == '54020631943060007154', res + r = log(mpf(10)**100/3) + res = last_digits(r) + # Mathematica N[Log[10^100/3], 10000] + # ,,,59246336539088351652334666 + assert res == '36539088351652334666', res + mp.dps += 10 + a = 1 - mpf(1)/10**10 + mp.dps -= 10 + r = log(a) + res = last_digits(r) + # ...3310334360482956137216724048322957404 + # 372167240483229574038733026370 + # Mathematica N[Log[1 - 10^-10]*10^10, 10000] + # ...60482956137216724048322957404 + assert res == '37216724048322957404', res + mp.dps = 10000 + mp.dps += 100 + a = 1 + mpf(1)/10**100 + mp.dps -= 100 + + r = log(a) + res = last_digits(+r) + # Mathematica N[Log[1 + 10^-100]*10^10, 10030] + # ...3994733877377412241546890854692521568292338268273 10^-91 + assert res == '39947338773774122415', res + + mp.dps = 15 + +def test_exp_hp(): + mp.dps = 4000 + r = exp(mpf(1)/10) + # IntegerPart[N[Exp[1/10] * 10^4000, 4000]] + # ...92167105162069688129 + assert int(r * 10**mp.dps) % 10**20 == 92167105162069688129 diff --git a/lib/python3.12/site-packages/mpmath/tests/test_identify.py b/lib/python3.12/site-packages/mpmath/tests/test_identify.py new file mode 100644 index 0000000000000000000000000000000000000000..f75ab0bc4f04ecb614011e7f4599989465cab785 --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/tests/test_identify.py @@ -0,0 +1,19 @@ +from mpmath import * + +def test_pslq(): + mp.dps = 15 + assert pslq([3*pi+4*e/7, pi, e, log(2)]) == [7, -21, -4, 0] + assert pslq([4.9999999999999991, 1]) == [1, -5] + assert pslq([2,1]) == [1, -2] + +def test_identify(): + mp.dps = 20 + assert identify(zeta(4), ['log(2)', 'pi**4']) == '((1/90)*pi**4)' + mp.dps = 15 + assert identify(exp(5)) == 'exp(5)' + assert identify(exp(4)) == 'exp(4)' + assert identify(log(5)) == 'log(5)' + assert identify(exp(3*pi), ['pi']) == 'exp((3*pi))' + assert identify(3, full=True) == ['3', '3', '1/(1/3)', 'sqrt(9)', + '1/sqrt((1/9))', '(sqrt(12)/2)**2', '1/(sqrt(12)/6)**2'] + assert identify(pi+1, {'a':+pi}) == '(1 + 1*a)' diff --git a/lib/python3.12/site-packages/mpmath/tests/test_interval.py b/lib/python3.12/site-packages/mpmath/tests/test_interval.py new file mode 100644 index 0000000000000000000000000000000000000000..251fd8b7ddb00074e8ae27cce4a01d8f4f8fe151 --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/tests/test_interval.py @@ -0,0 +1,453 @@ +from mpmath import * + +def test_interval_identity(): + iv.dps = 15 + assert mpi(2) == mpi(2, 2) + assert mpi(2) != mpi(-2, 2) + assert not (mpi(2) != mpi(2, 2)) + assert mpi(-1, 1) == mpi(-1, 1) + assert str(mpi('0.1')) == "[0.099999999999999991673, 0.10000000000000000555]" + assert repr(mpi('0.1')) == "mpi('0.099999999999999992', '0.10000000000000001')" + u = mpi(-1, 3) + assert -1 in u + assert 2 in u + assert 3 in u + assert -1.1 not in u + assert 3.1 not in u + assert mpi(-1, 3) in u + assert mpi(0, 1) in u + assert mpi(-1.1, 2) not in u + assert mpi(2.5, 3.1) not in u + w = mpi(-inf, inf) + assert mpi(-5, 5) in w + assert mpi(2, inf) in w + assert mpi(0, 2) in mpi(0, 10) + assert not (3 in mpi(-inf, 0)) + +def test_interval_hash(): + assert hash(mpi(3)) == hash(3) + assert hash(mpi(3.25)) == hash(3.25) + assert hash(mpi(3,4)) == hash(mpi(3,4)) + assert hash(iv.mpc(3)) == hash(3) + assert hash(iv.mpc(3,4)) == hash(3+4j) + assert hash(iv.mpc((1,3),(2,4))) == hash(iv.mpc((1,3),(2,4))) + +def test_interval_arithmetic(): + iv.dps = 15 + assert mpi(2) + mpi(3,4) == mpi(5,6) + assert mpi(1, 2)**2 == mpi(1, 4) + assert mpi(1) + mpi(0, 1e-50) == mpi(1, mpf('1.0000000000000002')) + x = 1 / (1 / mpi(3)) + assert x.a < 3 < x.b + x = mpi(2) ** mpi(0.5) + iv.dps += 5 + sq = iv.sqrt(2) + iv.dps -= 5 + assert x.a < sq < x.b + assert mpi(1) / mpi(1, inf) + assert mpi(2, 3) / inf == mpi(0, 0) + assert mpi(0) / inf == 0 + assert mpi(0) / 0 == mpi(-inf, inf) + assert mpi(inf) / 0 == mpi(-inf, inf) + assert mpi(0) * inf == mpi(-inf, inf) + assert 1 / mpi(2, inf) == mpi(0, 0.5) + assert str((mpi(50, 50) * mpi(-10, -10)) / 3) == \ + '[-166.66666666666668561, -166.66666666666665719]' + assert mpi(0, 4) ** 3 == mpi(0, 64) + assert mpi(2,4).mid == 3 + iv.dps = 30 + a = mpi(iv.pi) + iv.dps = 15 + b = +a + assert b.a < a.a + assert b.b > a.b + a = mpi(iv.pi) + assert a == +a + assert abs(mpi(-1,2)) == mpi(0,2) + assert abs(mpi(0.5,2)) == mpi(0.5,2) + assert abs(mpi(-3,2)) == mpi(0,3) + assert abs(mpi(-3,-0.5)) == mpi(0.5,3) + assert mpi(0) * mpi(2,3) == mpi(0) + assert mpi(2,3) * mpi(0) == mpi(0) + assert mpi(1,3).delta == 2 + assert mpi(1,2) - mpi(3,4) == mpi(-3,-1) + assert mpi(-inf,0) - mpi(0,inf) == mpi(-inf,0) + assert mpi(-inf,0) - mpi(-inf,inf) == mpi(-inf,inf) + assert mpi(0,inf) - mpi(-inf,1) == mpi(-1,inf) + +def test_interval_mul(): + assert mpi(-1, 0) * inf == mpi(-inf, 0) + assert mpi(-1, 0) * -inf == mpi(0, inf) + assert mpi(0, 1) * inf == mpi(0, inf) + assert mpi(0, 1) * mpi(0, inf) == mpi(0, inf) + assert mpi(-1, 1) * inf == mpi(-inf, inf) + assert mpi(-1, 1) * mpi(0, inf) == mpi(-inf, inf) + assert mpi(-1, 1) * mpi(-inf, inf) == mpi(-inf, inf) + assert mpi(-inf, 0) * mpi(0, 1) == mpi(-inf, 0) + assert mpi(-inf, 0) * mpi(0, 0) * mpi(-inf, 0) + assert mpi(-inf, 0) * mpi(-inf, inf) == mpi(-inf, inf) + assert mpi(-5,0)*mpi(-32,28) == mpi(-140,160) + assert mpi(2,3) * mpi(-1,2) == mpi(-3,6) + # Should be undefined? + assert mpi(inf, inf) * 0 == mpi(-inf, inf) + assert mpi(-inf, -inf) * 0 == mpi(-inf, inf) + assert mpi(0) * mpi(-inf,2) == mpi(-inf,inf) + assert mpi(0) * mpi(-2,inf) == mpi(-inf,inf) + assert mpi(-2,inf) * mpi(0) == mpi(-inf,inf) + assert mpi(-inf,2) * mpi(0) == mpi(-inf,inf) + +def test_interval_pow(): + assert mpi(3)**2 == mpi(9, 9) + assert mpi(-3)**2 == mpi(9, 9) + assert mpi(-3, 1)**2 == mpi(0, 9) + assert mpi(-3, -1)**2 == mpi(1, 9) + assert mpi(-3, -1)**3 == mpi(-27, -1) + assert mpi(-3, 1)**3 == mpi(-27, 1) + assert mpi(-2, 3)**2 == mpi(0, 9) + assert mpi(-3, 2)**2 == mpi(0, 9) + assert mpi(4) ** -1 == mpi(0.25, 0.25) + assert mpi(-4) ** -1 == mpi(-0.25, -0.25) + assert mpi(4) ** -2 == mpi(0.0625, 0.0625) + assert mpi(-4) ** -2 == mpi(0.0625, 0.0625) + assert mpi(0, 1) ** inf == mpi(0, 1) + assert mpi(0, 1) ** -inf == mpi(1, inf) + assert mpi(0, inf) ** inf == mpi(0, inf) + assert mpi(0, inf) ** -inf == mpi(0, inf) + assert mpi(1, inf) ** inf == mpi(1, inf) + assert mpi(1, inf) ** -inf == mpi(0, 1) + assert mpi(2, 3) ** 1 == mpi(2, 3) + assert mpi(2, 3) ** 0 == 1 + assert mpi(1,3) ** mpi(2) == mpi(1,9) + +def test_interval_sqrt(): + assert mpi(4) ** 0.5 == mpi(2) + +def test_interval_div(): + assert mpi(0.5, 1) / mpi(-1, 0) == mpi(-inf, -0.5) + assert mpi(0, 1) / mpi(0, 1) == mpi(0, inf) + assert mpi(inf, inf) / mpi(inf, inf) == mpi(0, inf) + assert mpi(inf, inf) / mpi(2, inf) == mpi(0, inf) + assert mpi(inf, inf) / mpi(2, 2) == mpi(inf, inf) + assert mpi(0, inf) / mpi(2, inf) == mpi(0, inf) + assert mpi(0, inf) / mpi(2, 2) == mpi(0, inf) + assert mpi(2, inf) / mpi(2, 2) == mpi(1, inf) + assert mpi(2, inf) / mpi(2, inf) == mpi(0, inf) + assert mpi(-4, 8) / mpi(1, inf) == mpi(-4, 8) + assert mpi(-4, 8) / mpi(0.5, inf) == mpi(-8, 16) + assert mpi(-inf, 8) / mpi(0.5, inf) == mpi(-inf, 16) + assert mpi(-inf, inf) / mpi(0.5, inf) == mpi(-inf, inf) + assert mpi(8, inf) / mpi(0.5, inf) == mpi(0, inf) + assert mpi(-8, inf) / mpi(0.5, inf) == mpi(-16, inf) + assert mpi(-4, 8) / mpi(inf, inf) == mpi(0, 0) + assert mpi(0, 8) / mpi(inf, inf) == mpi(0, 0) + assert mpi(0, 0) / mpi(inf, inf) == mpi(0, 0) + assert mpi(-inf, 0) / mpi(inf, inf) == mpi(-inf, 0) + assert mpi(-inf, 8) / mpi(inf, inf) == mpi(-inf, 0) + assert mpi(-inf, inf) / mpi(inf, inf) == mpi(-inf, inf) + assert mpi(-8, inf) / mpi(inf, inf) == mpi(0, inf) + assert mpi(0, inf) / mpi(inf, inf) == mpi(0, inf) + assert mpi(8, inf) / mpi(inf, inf) == mpi(0, inf) + assert mpi(inf, inf) / mpi(inf, inf) == mpi(0, inf) + assert mpi(-1, 2) / mpi(0, 1) == mpi(-inf, +inf) + assert mpi(0, 1) / mpi(0, 1) == mpi(0.0, +inf) + assert mpi(-1, 0) / mpi(0, 1) == mpi(-inf, 0.0) + assert mpi(-0.5, -0.25) / mpi(0, 1) == mpi(-inf, -0.25) + assert mpi(0.5, 1) / mpi(0, 1) == mpi(0.5, +inf) + assert mpi(0.5, 4) / mpi(0, 1) == mpi(0.5, +inf) + assert mpi(-1, -0.5) / mpi(0, 1) == mpi(-inf, -0.5) + assert mpi(-4, -0.5) / mpi(0, 1) == mpi(-inf, -0.5) + assert mpi(-1, 2) / mpi(-2, 0.5) == mpi(-inf, +inf) + assert mpi(0, 1) / mpi(-2, 0.5) == mpi(-inf, +inf) + assert mpi(-1, 0) / mpi(-2, 0.5) == mpi(-inf, +inf) + assert mpi(-0.5, -0.25) / mpi(-2, 0.5) == mpi(-inf, +inf) + assert mpi(0.5, 1) / mpi(-2, 0.5) == mpi(-inf, +inf) + assert mpi(0.5, 4) / mpi(-2, 0.5) == mpi(-inf, +inf) + assert mpi(-1, -0.5) / mpi(-2, 0.5) == mpi(-inf, +inf) + assert mpi(-4, -0.5) / mpi(-2, 0.5) == mpi(-inf, +inf) + assert mpi(-1, 2) / mpi(-1, 0) == mpi(-inf, +inf) + assert mpi(0, 1) / mpi(-1, 0) == mpi(-inf, 0.0) + assert mpi(-1, 0) / mpi(-1, 0) == mpi(0.0, +inf) + assert mpi(-0.5, -0.25) / mpi(-1, 0) == mpi(0.25, +inf) + assert mpi(0.5, 1) / mpi(-1, 0) == mpi(-inf, -0.5) + assert mpi(0.5, 4) / mpi(-1, 0) == mpi(-inf, -0.5) + assert mpi(-1, -0.5) / mpi(-1, 0) == mpi(0.5, +inf) + assert mpi(-4, -0.5) / mpi(-1, 0) == mpi(0.5, +inf) + assert mpi(-1, 2) / mpi(0.5, 1) == mpi(-2.0, 4.0) + assert mpi(0, 1) / mpi(0.5, 1) == mpi(0.0, 2.0) + assert mpi(-1, 0) / mpi(0.5, 1) == mpi(-2.0, 0.0) + assert mpi(-0.5, -0.25) / mpi(0.5, 1) == mpi(-1.0, -0.25) + assert mpi(0.5, 1) / mpi(0.5, 1) == mpi(0.5, 2.0) + assert mpi(0.5, 4) / mpi(0.5, 1) == mpi(0.5, 8.0) + assert mpi(-1, -0.5) / mpi(0.5, 1) == mpi(-2.0, -0.5) + assert mpi(-4, -0.5) / mpi(0.5, 1) == mpi(-8.0, -0.5) + assert mpi(-1, 2) / mpi(-2, -0.5) == mpi(-4.0, 2.0) + assert mpi(0, 1) / mpi(-2, -0.5) == mpi(-2.0, 0.0) + assert mpi(-1, 0) / mpi(-2, -0.5) == mpi(0.0, 2.0) + assert mpi(-0.5, -0.25) / mpi(-2, -0.5) == mpi(0.125, 1.0) + assert mpi(0.5, 1) / mpi(-2, -0.5) == mpi(-2.0, -0.25) + assert mpi(0.5, 4) / mpi(-2, -0.5) == mpi(-8.0, -0.25) + assert mpi(-1, -0.5) / mpi(-2, -0.5) == mpi(0.25, 2.0) + assert mpi(-4, -0.5) / mpi(-2, -0.5) == mpi(0.25, 8.0) + # Should be undefined? + assert mpi(0, 0) / mpi(0, 0) == mpi(-inf, inf) + assert mpi(0, 0) / mpi(0, 1) == mpi(-inf, inf) + +def test_interval_cos_sin(): + iv.dps = 15 + cos = iv.cos + sin = iv.sin + tan = iv.tan + pi = iv.pi + # Around 0 + assert cos(mpi(0)) == 1 + assert sin(mpi(0)) == 0 + assert cos(mpi(0,1)) == mpi(0.54030230586813965399, 1.0) + assert sin(mpi(0,1)) == mpi(0, 0.8414709848078966159) + assert cos(mpi(1,2)) == mpi(-0.4161468365471424069, 0.54030230586813976501) + assert sin(mpi(1,2)) == mpi(0.84147098480789650488, 1.0) + assert sin(mpi(1,2.5)) == mpi(0.59847214410395643824, 1.0) + assert cos(mpi(-1, 1)) == mpi(0.54030230586813965399, 1.0) + assert cos(mpi(-1, 0.5)) == mpi(0.54030230586813965399, 1.0) + assert cos(mpi(-1, 1.5)) == mpi(0.070737201667702906405, 1.0) + assert sin(mpi(-1,1)) == mpi(-0.8414709848078966159, 0.8414709848078966159) + assert sin(mpi(-1,0.5)) == mpi(-0.8414709848078966159, 0.47942553860420300538) + assert mpi(-0.8414709848078966159, 1.00000000000000002e-100) in sin(mpi(-1,1e-100)) + assert mpi(-2.00000000000000004e-100, 1.00000000000000002e-100) in sin(mpi(-2e-100,1e-100)) + # Same interval + assert cos(mpi(2, 2.5)) + assert cos(mpi(3.5, 4)) == mpi(-0.93645668729079634129, -0.65364362086361182946) + assert cos(mpi(5, 5.5)) == mpi(0.28366218546322624627, 0.70866977429126010168) + assert mpi(0.59847214410395654927, 0.90929742682568170942) in sin(mpi(2, 2.5)) + assert sin(mpi(3.5, 4)) == mpi(-0.75680249530792831347, -0.35078322768961983646) + assert sin(mpi(5, 5.5)) == mpi(-0.95892427466313856499, -0.70554032557039181306) + # Higher roots + iv.dps = 55 + w = 4*10**50 + mpi(0.5) + for p in [15, 40, 80]: + iv.dps = p + assert 0 in sin(4*mpi(pi)) + assert 0 in sin(4*10**50*mpi(pi)) + assert 0 in cos((4+0.5)*mpi(pi)) + assert 0 in cos(w*mpi(pi)) + assert 1 in cos(4*mpi(pi)) + assert 1 in cos(4*10**50*mpi(pi)) + iv.dps = 15 + assert cos(mpi(2,inf)) == mpi(-1,1) + assert sin(mpi(2,inf)) == mpi(-1,1) + assert cos(mpi(-inf,2)) == mpi(-1,1) + assert sin(mpi(-inf,2)) == mpi(-1,1) + u = tan(mpi(0.5,1)) + assert mpf(u.a).ae(mp.tan(0.5)) + assert mpf(u.b).ae(mp.tan(1)) + v = iv.cot(mpi(0.5,1)) + assert mpf(v.a).ae(mp.cot(1)) + assert mpf(v.b).ae(mp.cot(0.5)) + # Sanity check of evaluation at n*pi and (n+1/2)*pi + for n in range(-5,7,2): + x = iv.cos(n*iv.pi) + assert -1 in x + assert x >= -1 + assert x != -1 + x = iv.sin((n+0.5)*iv.pi) + assert -1 in x + assert x >= -1 + assert x != -1 + for n in range(-6,8,2): + x = iv.cos(n*iv.pi) + assert 1 in x + assert x <= 1 + if n: + assert x != 1 + x = iv.sin((n+0.5)*iv.pi) + assert 1 in x + assert x <= 1 + assert x != 1 + for n in range(-6,7): + x = iv.cos((n+0.5)*iv.pi) + assert x.a < 0 < x.b + x = iv.sin(n*iv.pi) + if n: + assert x.a < 0 < x.b + +def test_interval_complex(): + # TODO: many more tests + iv.dps = 15 + mp.dps = 15 + assert iv.mpc(2,3) == 2+3j + assert iv.mpc(2,3) != 2+4j + assert iv.mpc(2,3) != 1+3j + assert 1+3j in iv.mpc([1,2],[3,4]) + assert 2+5j not in iv.mpc([1,2],[3,4]) + assert iv.mpc(1,2) + 1j == 1+3j + assert iv.mpc([1,2],[2,3]) + 2+3j == iv.mpc([3,4],[5,6]) + assert iv.mpc([2,4],[4,8]) / 2 == iv.mpc([1,2],[2,4]) + assert iv.mpc([1,2],[2,4]) * 2j == iv.mpc([-8,-4],[2,4]) + assert iv.mpc([2,4],[4,8]) / 2j == iv.mpc([2,4],[-2,-1]) + assert iv.exp(2+3j).ae(mp.exp(2+3j)) + assert iv.log(2+3j).ae(mp.log(2+3j)) + assert (iv.mpc(2,3) ** iv.mpc(0.5,2)).ae(mp.mpc(2,3) ** mp.mpc(0.5,2)) + assert 1j in (iv.mpf(-1) ** 0.5) + assert 1j in (iv.mpc(-1) ** 0.5) + assert abs(iv.mpc(0)) == 0 + assert abs(iv.mpc(inf)) == inf + assert abs(iv.mpc(3,4)) == 5 + assert abs(iv.mpc(4)) == 4 + assert abs(iv.mpc(0,4)) == 4 + assert abs(iv.mpc(0,[2,3])) == iv.mpf([2,3]) + assert abs(iv.mpc(0,[-3,2])) == iv.mpf([0,3]) + assert abs(iv.mpc([3,5],[4,12])) == iv.mpf([5,13]) + assert abs(iv.mpc([3,5],[-4,12])) == iv.mpf([3,13]) + assert iv.mpc(2,3) ** 0 == 1 + assert iv.mpc(2,3) ** 1 == (2+3j) + assert iv.mpc(2,3) ** 2 == (2+3j)**2 + assert iv.mpc(2,3) ** 3 == (2+3j)**3 + assert iv.mpc(2,3) ** 4 == (2+3j)**4 + assert iv.mpc(2,3) ** 5 == (2+3j)**5 + assert iv.mpc(2,2) ** (-1) == (2+2j) ** (-1) + assert iv.mpc(2,2) ** (-2) == (2+2j) ** (-2) + assert iv.cos(2).ae(mp.cos(2)) + assert iv.sin(2).ae(mp.sin(2)) + assert iv.cos(2+3j).ae(mp.cos(2+3j)) + assert iv.sin(2+3j).ae(mp.sin(2+3j)) + +def test_interval_complex_arg(): + mp.dps = 15 + iv.dps = 15 + assert iv.arg(3) == 0 + assert iv.arg(0) == 0 + assert iv.arg([0,3]) == 0 + assert iv.arg(-3).ae(pi) + assert iv.arg(2+3j).ae(iv.arg(2+3j)) + z = iv.mpc([-2,-1],[3,4]) + t = iv.arg(z) + assert t.a.ae(mp.arg(-1+4j)) + assert t.b.ae(mp.arg(-2+3j)) + z = iv.mpc([-2,1],[3,4]) + t = iv.arg(z) + assert t.a.ae(mp.arg(1+3j)) + assert t.b.ae(mp.arg(-2+3j)) + z = iv.mpc([1,2],[3,4]) + t = iv.arg(z) + assert t.a.ae(mp.arg(2+3j)) + assert t.b.ae(mp.arg(1+4j)) + z = iv.mpc([1,2],[-2,3]) + t = iv.arg(z) + assert t.a.ae(mp.arg(1-2j)) + assert t.b.ae(mp.arg(1+3j)) + z = iv.mpc([1,2],[-4,-3]) + t = iv.arg(z) + assert t.a.ae(mp.arg(1-4j)) + assert t.b.ae(mp.arg(2-3j)) + z = iv.mpc([-1,2],[-4,-3]) + t = iv.arg(z) + assert t.a.ae(mp.arg(-1-3j)) + assert t.b.ae(mp.arg(2-3j)) + z = iv.mpc([-2,-1],[-4,-3]) + t = iv.arg(z) + assert t.a.ae(mp.arg(-2-3j)) + assert t.b.ae(mp.arg(-1-4j)) + z = iv.mpc([-2,-1],[-3,3]) + t = iv.arg(z) + assert t.a.ae(-mp.pi) + assert t.b.ae(mp.pi) + z = iv.mpc([-2,2],[-3,3]) + t = iv.arg(z) + assert t.a.ae(-mp.pi) + assert t.b.ae(mp.pi) + +def test_interval_ae(): + iv.dps = 15 + x = iv.mpf([1,2]) + assert x.ae(1) is None + assert x.ae(1.5) is None + assert x.ae(2) is None + assert x.ae(2.01) is False + assert x.ae(0.99) is False + x = iv.mpf(3.5) + assert x.ae(3.5) is True + assert x.ae(3.5+1e-15) is True + assert x.ae(3.5-1e-15) is True + assert x.ae(3.501) is False + assert x.ae(3.499) is False + assert x.ae(iv.mpf([3.5,3.501])) is None + assert x.ae(iv.mpf([3.5,4.5+1e-15])) is None + +def test_interval_nstr(): + iv.dps = n = 30 + x = mpi(1, 2) + # FIXME: error_dps should not be necessary + assert iv.nstr(x, n, mode='plusminus', error_dps=6) == '1.5 +- 0.5' + assert iv.nstr(x, n, mode='plusminus', use_spaces=False, error_dps=6) == '1.5+-0.5' + assert iv.nstr(x, n, mode='percent') == '1.5 (33.33%)' + assert iv.nstr(x, n, mode='brackets', use_spaces=False) == '[1.0,2.0]' + assert iv.nstr(x, n, mode='brackets' , brackets=('<', '>')) == '<1.0, 2.0>' + x = mpi('5.2582327113062393041', '5.2582327113062749951') + assert iv.nstr(x, n, mode='diff') == '5.2582327113062[393041, 749951]' + assert iv.nstr(iv.cos(mpi(1)), n, mode='diff', use_spaces=False) == '0.54030230586813971740093660744[2955,3053]' + assert iv.nstr(mpi('1e123', '1e129'), n, mode='diff') == '[1.0e+123, 1.0e+129]' + exp = iv.exp + assert iv.nstr(iv.exp(mpi('5000.1')), n, mode='diff') == '3.2797365856787867069110487[0926, 1191]e+2171' + iv.dps = 15 + +def test_mpi_from_str(): + iv.dps = 15 + assert iv.convert('1.5 +- 0.5') == mpi(mpf('1.0'), mpf('2.0')) + assert mpi(1, 2) in iv.convert('1.5 (33.33333333333333333333333333333%)') + assert iv.convert('[1, 2]') == mpi(1, 2) + assert iv.convert('1[2, 3]') == mpi(12, 13) + assert iv.convert('1.[23,46]e-8') == mpi('1.23e-8', '1.46e-8') + assert iv.convert('12[3.4,5.9]e4') == mpi('123.4e+4', '125.9e4') + +def test_interval_gamma(): + mp.dps = 15 + iv.dps = 15 + # TODO: need many more tests + assert iv.rgamma(0) == 0 + assert iv.fac(0) == 1 + assert iv.fac(1) == 1 + assert iv.fac(2) == 2 + assert iv.fac(3) == 6 + assert iv.gamma(0) == [-inf,inf] + assert iv.gamma(1) == 1 + assert iv.gamma(2) == 1 + assert iv.gamma(3) == 2 + assert -3.5449077018110320546 in iv.gamma(-0.5) + assert iv.loggamma(1) == 0 + assert iv.loggamma(2) == 0 + assert 0.69314718055994530942 in iv.loggamma(3) + # Test tight log-gamma endpoints based on monotonicity + xs = [iv.mpc([2,3],[1,4]), + iv.mpc([2,3],[-4,-1]), + iv.mpc([2,3],[-1,4]), + iv.mpc([2,3],[-4,1]), + iv.mpc([2,3],[-4,4]), + iv.mpc([-3,-2],[2,4]), + iv.mpc([-3,-2],[-4,-2])] + for x in xs: + ys = [mp.loggamma(mp.mpc(x.a,x.c)), + mp.loggamma(mp.mpc(x.b,x.c)), + mp.loggamma(mp.mpc(x.a,x.d)), + mp.loggamma(mp.mpc(x.b,x.d))] + if 0 in x.imag: + ys += [mp.loggamma(x.a), mp.loggamma(x.b)] + min_real = min([y.real for y in ys]) + max_real = max([y.real for y in ys]) + min_imag = min([y.imag for y in ys]) + max_imag = max([y.imag for y in ys]) + z = iv.loggamma(x) + assert z.a.ae(min_real) + assert z.b.ae(max_real) + assert z.c.ae(min_imag) + assert z.d.ae(max_imag) + +def test_interval_conversions(): + mp.dps = 15 + iv.dps = 15 + for a, b in ((-0.0, 0), (0.0, 0.5), (1.0, 1), \ + ('-inf', 20.5), ('-inf', float(sqrt(2)))): + r = mpi(a, b) + assert int(r.b) == int(b) + assert float(r.a) == float(a) + assert float(r.b) == float(b) + assert complex(r.a) == complex(a) + assert complex(r.b) == complex(b) diff --git a/lib/python3.12/site-packages/mpmath/tests/test_levin.py b/lib/python3.12/site-packages/mpmath/tests/test_levin.py new file mode 100644 index 0000000000000000000000000000000000000000..b14855df4de1a45da27080dcd239267842a4ac7a --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/tests/test_levin.py @@ -0,0 +1,153 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +from mpmath import mp +from mpmath import libmp + +xrange = libmp.backend.xrange + +# Attention: +# These tests run with 15-20 decimal digits precision. For higher precision the +# working precision must be raised. + +def test_levin_0(): + mp.dps = 17 + eps = mp.mpf(mp.eps) + with mp.extraprec(2 * mp.prec): + L = mp.levin(method = "levin", variant = "u") + S, s, n = [], 0, 1 + while 1: + s += mp.one / (n * n) + n += 1 + S.append(s) + v, e = L.update_psum(S) + if e < eps: + break + if n > 1000: raise RuntimeError("iteration limit exceeded") + eps = mp.exp(0.9 * mp.log(eps)) + err = abs(v - mp.pi ** 2 / 6) + assert err < eps + w = mp.nsum(lambda n: 1/(n * n), [1, mp.inf], method = "levin", levin_variant = "u") + err = abs(v - w) + assert err < eps + +def test_levin_1(): + mp.dps = 17 + eps = mp.mpf(mp.eps) + with mp.extraprec(2 * mp.prec): + L = mp.levin(method = "levin", variant = "v") + A, n = [], 1 + while 1: + s = mp.mpf(n) ** (2 + 3j) + n += 1 + A.append(s) + v, e = L.update(A) + if e < eps: + break + if n > 1000: raise RuntimeError("iteration limit exceeded") + eps = mp.exp(0.9 * mp.log(eps)) + err = abs(v - mp.zeta(-2-3j)) + assert err < eps + w = mp.nsum(lambda n: n ** (2 + 3j), [1, mp.inf], method = "levin", levin_variant = "v") + err = abs(v - w) + assert err < eps + +def test_levin_2(): + # [2] A. Sidi - "Pratical Extrapolation Methods" p.373 + mp.dps = 17 + z=mp.mpf(10) + eps = mp.mpf(mp.eps) + with mp.extraprec(2 * mp.prec): + L = mp.levin(method = "sidi", variant = "t") + n = 0 + while 1: + s = (-1)**n * mp.fac(n) * z ** (-n) + v, e = L.step(s) + n += 1 + if e < eps: + break + if n > 1000: raise RuntimeError("iteration limit exceeded") + eps = mp.exp(0.9 * mp.log(eps)) + exact = mp.quad(lambda x: mp.exp(-x)/(1+x/z),[0,mp.inf]) + # there is also a symbolic expression for the integral: + # exact = z * mp.exp(z) * mp.expint(1,z) + err = abs(v - exact) + assert err < eps + w = mp.nsum(lambda n: (-1) ** n * mp.fac(n) * z ** (-n), [0, mp.inf], method = "sidi", levin_variant = "t") + assert err < eps + +def test_levin_3(): + mp.dps = 17 + z=mp.mpf(2) + eps = mp.mpf(mp.eps) + with mp.extraprec(7*mp.prec): # we need copious amount of precision to sum this highly divergent series + L = mp.levin(method = "levin", variant = "t") + n, s = 0, 0 + while 1: + s += (-z)**n * mp.fac(4 * n) / (mp.fac(n) * mp.fac(2 * n) * (4 ** n)) + n += 1 + v, e = L.step_psum(s) + if e < eps: + break + if n > 1000: raise RuntimeError("iteration limit exceeded") + eps = mp.exp(0.8 * mp.log(eps)) + exact = mp.quad(lambda x: mp.exp( -x * x / 2 - z * x ** 4), [0,mp.inf]) * 2 / mp.sqrt(2 * mp.pi) + # there is also a symbolic expression for the integral: + # exact = mp.exp(mp.one / (32 * z)) * mp.besselk(mp.one / 4, mp.one / (32 * z)) / (4 * mp.sqrt(z * mp.pi)) + err = abs(v - exact) + assert err < eps + w = mp.nsum(lambda n: (-z)**n * mp.fac(4 * n) / (mp.fac(n) * mp.fac(2 * n) * (4 ** n)), [0, mp.inf], method = "levin", levin_variant = "t", workprec = 8*mp.prec, steps = [2] + [1 for x in xrange(1000)]) + err = abs(v - w) + assert err < eps + +def test_levin_nsum(): + mp.dps = 17 + + with mp.extraprec(mp.prec): + z = mp.mpf(10) ** (-10) + a = mp.nsum(lambda n: n**(-(1+z)), [1, mp.inf], method = "l") - 1 / z + assert abs(a - mp.euler) < 1e-10 + + eps = mp.exp(0.8 * mp.log(mp.eps)) + + a = mp.nsum(lambda n: (-1)**(n-1) / n, [1, mp.inf], method = "sidi") + assert abs(a - mp.log(2)) < eps + + z = 2 + 1j + f = lambda n: mp.rf(2 / mp.mpf(3), n) * mp.rf(4 / mp.mpf(3), n) * z**n / (mp.rf(1 / mp.mpf(3), n) * mp.fac(n)) + v = mp.nsum(f, [0, mp.inf], method = "levin", steps = [10 for x in xrange(1000)]) + exact = mp.hyp2f1(2 / mp.mpf(3), 4 / mp.mpf(3), 1 / mp.mpf(3), z) + assert abs(exact - v) < eps + +def test_cohen_alt_0(): + mp.dps = 17 + AC = mp.cohen_alt() + S, s, n = [], 0, 1 + while 1: + s += -((-1) ** n) * mp.one / (n * n) + n += 1 + S.append(s) + v, e = AC.update_psum(S) + if e < mp.eps: + break + if n > 1000: raise RuntimeError("iteration limit exceeded") + eps = mp.exp(0.9 * mp.log(mp.eps)) + err = abs(v - mp.pi ** 2 / 12) + assert err < eps + +def test_cohen_alt_1(): + mp.dps = 17 + A = [] + AC = mp.cohen_alt() + n = 1 + while 1: + A.append( mp.loggamma(1 + mp.one / (2 * n - 1))) + A.append(-mp.loggamma(1 + mp.one / (2 * n))) + n += 1 + v, e = AC.update(A) + if e < mp.eps: + break + if n > 1000: raise RuntimeError("iteration limit exceeded") + v = mp.exp(v) + err = abs(v - 1.06215090557106) + assert err < 1e-12 diff --git a/lib/python3.12/site-packages/mpmath/tests/test_linalg.py b/lib/python3.12/site-packages/mpmath/tests/test_linalg.py new file mode 100644 index 0000000000000000000000000000000000000000..14256a79f8953d3e4ef8b296258560d48204f547 --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/tests/test_linalg.py @@ -0,0 +1,332 @@ +# TODO: don't use round + +from __future__ import division + +import pytest +from mpmath import * +xrange = libmp.backend.xrange + +# XXX: these shouldn't be visible(?) +LU_decomp = mp.LU_decomp +L_solve = mp.L_solve +U_solve = mp.U_solve +householder = mp.householder +improve_solution = mp.improve_solution + +A1 = matrix([[3, 1, 6], + [2, 1, 3], + [1, 1, 1]]) +b1 = [2, 7, 4] + +A2 = matrix([[ 2, -1, -1, 2], + [ 6, -2, 3, -1], + [-4, 2, 3, -2], + [ 2, 0, 4, -3]]) +b2 = [3, -3, -2, -1] + +A3 = matrix([[ 1, 0, -1, -1, 0], + [ 0, 1, 1, 0, -1], + [ 4, -5, 2, 0, 0], + [ 0, 0, -2, 9,-12], + [ 0, 5, 0, 0, 12]]) +b3 = [0, 0, 0, 0, 50] + +A4 = matrix([[10.235, -4.56, 0., -0.035, 5.67], + [-2.463, 1.27, 3.97, -8.63, 1.08], + [-6.58, 0.86, -0.257, 9.32, -43.6 ], + [ 9.83, 7.39, -17.25, 0.036, 24.86], + [-9.31, 34.9, 78.56, 1.07, 65.8 ]]) +b4 = [8.95, 20.54, 7.42, 5.60, 58.43] + +A5 = matrix([[ 1, 2, -4], + [-2, -3, 5], + [ 3, 5, -8]]) + +A6 = matrix([[ 1.377360, 2.481400, 5.359190], + [ 2.679280, -1.229560, 25.560210], + [-1.225280+1.e6, 9.910180, -35.049900-1.e6]]) +b6 = [23.500000, -15.760000, 2.340000] + +A7 = matrix([[1, -0.5], + [2, 1], + [-2, 6]]) +b7 = [3, 2, -4] + +A8 = matrix([[1, 2, 3], + [-1, 0, 1], + [-1, -2, -1], + [1, 0, -1]]) +b8 = [1, 2, 3, 4] + +A9 = matrix([[ 4, 2, -2], + [ 2, 5, -4], + [-2, -4, 5.5]]) +b9 = [10, 16, -15.5] + +A10 = matrix([[1.0 + 1.0j, 2.0, 2.0], + [4.0, 5.0, 6.0], + [7.0, 8.0, 9.0]]) +b10 = [1.0, 1.0 + 1.0j, 1.0] + + +def test_LU_decomp(): + A = A3.copy() + b = b3 + A, p = LU_decomp(A) + y = L_solve(A, b, p) + x = U_solve(A, y) + assert p == [2, 1, 2, 3] + assert [round(i, 14) for i in x] == [3.78953107960742, 2.9989094874591098, + -0.081788440567070006, 3.8713195201744801, 2.9171210468920399] + A = A4.copy() + b = b4 + A, p = LU_decomp(A) + y = L_solve(A, b, p) + x = U_solve(A, y) + assert p == [0, 3, 4, 3] + assert [round(i, 14) for i in x] == [2.6383625899619201, 2.6643834462368399, + 0.79208015947958998, -2.5088376454101899, -1.0567657691375001] + A = randmatrix(3) + bak = A.copy() + LU_decomp(A, overwrite=1) + assert A != bak + +def test_inverse(): + for A in [A1, A2, A5]: + inv = inverse(A) + assert mnorm(A*inv - eye(A.rows), 1) < 1.e-14 + +def test_householder(): + mp.dps = 15 + A, b = A8, b8 + H, p, x, r = householder(extend(A, b)) + assert H == matrix( + [[mpf('3.0'), mpf('-2.0'), mpf('-1.0'), 0], + [-1.0,mpf('3.333333333333333'),mpf('-2.9999999999999991'),mpf('2.0')], + [-1.0, mpf('-0.66666666666666674'),mpf('2.8142135623730948'), + mpf('-2.8284271247461898')], + [1.0, mpf('-1.3333333333333333'),mpf('-0.20000000000000018'), + mpf('4.2426406871192857')]]) + assert p == [-2, -2, mpf('-1.4142135623730949')] + assert round(norm(r, 2), 10) == 4.2426406870999998 + + y = [102.102, 58.344, 36.463, 24.310, 17.017, 12.376, 9.282, 7.140, 5.610, + 4.488, 3.6465, 3.003] + + def coeff(n): + # similiar to Hilbert matrix + A = [] + for i in range(1, 13): + A.append([1. / (i + j - 1) for j in range(1, n + 1)]) + return matrix(A) + + residuals = [] + refres = [] + for n in range(2, 7): + A = coeff(n) + H, p, x, r = householder(extend(A, y)) + x = matrix(x) + y = matrix(y) + residuals.append(norm(r, 2)) + refres.append(norm(residual(A, x, y), 2)) + assert [round(res, 10) for res in residuals] == [15.1733888877, + 0.82378073210000002, 0.302645887, 0.0260109244, + 0.00058653999999999998] + assert norm(matrix(residuals) - matrix(refres), inf) < 1.e-13 + + def hilbert_cmplx(n): + # Complexified Hilbert matrix + A = hilbert(2*n,n) + v = randmatrix(2*n, 2, min=-1, max=1) + v = v.apply(lambda x: exp(1J*pi()*x)) + A = diag(v[:,0])*A*diag(v[:n,1]) + return A + + residuals_cmplx = [] + refres_cmplx = [] + for n in range(2, 10): + A = hilbert_cmplx(n) + H, p, x, r = householder(A.copy()) + residuals_cmplx.append(norm(r, 2)) + refres_cmplx.append(norm(residual(A[:,:n-1], x, A[:,n-1]), 2)) + assert norm(matrix(residuals_cmplx) - matrix(refres_cmplx), inf) < 1.e-13 + +def test_factorization(): + A = randmatrix(5) + P, L, U = lu(A) + assert mnorm(P*A - L*U, 1) < 1.e-15 + +def test_solve(): + assert norm(residual(A6, lu_solve(A6, b6), b6), inf) < 1.e-10 + assert norm(residual(A7, lu_solve(A7, b7), b7), inf) < 1.5 + assert norm(residual(A8, lu_solve(A8, b8), b8), inf) <= 3 + 1.e-10 + assert norm(residual(A6, qr_solve(A6, b6)[0], b6), inf) < 1.e-10 + assert norm(residual(A7, qr_solve(A7, b7)[0], b7), inf) < 1.5 + assert norm(residual(A8, qr_solve(A8, b8)[0], b8), 2) <= 4.3 + assert norm(residual(A10, lu_solve(A10, b10), b10), 2) < 1.e-10 + assert norm(residual(A10, qr_solve(A10, b10)[0], b10), 2) < 1.e-10 + +def test_solve_overdet_complex(): + A = matrix([[1, 2j], [3, 4j], [5, 6]]) + b = matrix([1 + j, 2, -j]) + assert norm(residual(A, lu_solve(A, b), b)) < 1.0208 + +def test_singular(): + mp.dps = 15 + A = [[5.6, 1.2], [7./15, .1]] + B = repr(zeros(2)) + b = [1, 2] + for i in ['lu_solve(%s, %s)' % (A, b), 'lu_solve(%s, %s)' % (B, b), + 'qr_solve(%s, %s)' % (A, b), 'qr_solve(%s, %s)' % (B, b)]: + pytest.raises((ZeroDivisionError, ValueError), lambda: eval(i)) + +def test_cholesky(): + assert fp.cholesky(fp.matrix(A9)) == fp.matrix([[2, 0, 0], [1, 2, 0], [-1, -3/2, 3/2]]) + x = fp.cholesky_solve(A9, b9) + assert fp.norm(fp.residual(A9, x, b9), fp.inf) == 0 + +def test_det(): + assert det(A1) == 1 + assert round(det(A2), 14) == 8 + assert round(det(A3)) == 1834 + assert round(det(A4)) == 4443376 + assert det(A5) == 1 + assert round(det(A6)) == 78356463 + assert det(zeros(3)) == 0 + +def test_cond(): + mp.dps = 15 + A = matrix([[1.2969, 0.8648], [0.2161, 0.1441]]) + assert cond(A, lambda x: mnorm(x,1)) == mpf('327065209.73817754') + assert cond(A, lambda x: mnorm(x,inf)) == mpf('327065209.73817754') + assert cond(A, lambda x: mnorm(x,'F')) == mpf('249729266.80008656') + +@extradps(50) +def test_precision(): + A = randmatrix(10, 10) + assert mnorm(inverse(inverse(A)) - A, 1) < 1.e-45 + +def test_interval_matrix(): + mp.dps = 15 + iv.dps = 15 + a = iv.matrix([['0.1','0.3','1.0'],['7.1','5.5','4.8'],['3.2','4.4','5.6']]) + b = iv.matrix(['4','0.6','0.5']) + c = iv.lu_solve(a, b) + assert c[0].delta < 1e-13 + assert c[1].delta < 1e-13 + assert c[2].delta < 1e-13 + assert 5.25823271130625686059275 in c[0] + assert -13.155049396267837541163 in c[1] + assert 7.42069154774972557628979 in c[2] + +def test_LU_cache(): + A = randmatrix(3) + LU = LU_decomp(A) + assert A._LU == LU_decomp(A) + A[0,0] = -1000 + assert A._LU is None + +def test_improve_solution(): + A = randmatrix(5, min=1e-20, max=1e20) + b = randmatrix(5, 1, min=-1000, max=1000) + x1 = lu_solve(A, b) + randmatrix(5, 1, min=-1e-5, max=1.e-5) + x2 = improve_solution(A, x1, b) + assert norm(residual(A, x2, b), 2) < norm(residual(A, x1, b), 2) + +def test_exp_pade(): + for i in range(3): + dps = 15 + extra = 15 + mp.dps = dps + extra + dm = 0 + N = 3 + dg = range(1,N+1) + a = diag(dg) + expa = diag([exp(x) for x in dg]) + # choose a random matrix not close to be singular + # to avoid adding too much extra precision in computing + # m**-1 * M * m + while abs(dm) < 0.01: + m = randmatrix(N) + dm = det(m) + m = m/dm + a1 = m**-1 * a * m + e2 = m**-1 * expa * m + mp.dps = dps + e1 = expm(a1, method='pade') + mp.dps = dps + extra + d = e2 - e1 + #print d + mp.dps = dps + assert norm(d, inf).ae(0) + mp.dps = 15 + +def test_qr(): + mp.dps = 15 # used default value for dps + lowlimit = -9 # lower limit of matrix element value + uplimit = 9 # uppter limit of matrix element value + maxm = 4 # max matrix size + flg = False # toggle to create real vs complex matrix + zero = mpf('0.0') + + for k in xrange(0,10): + exdps = 0 + mode = 'full' + flg = bool(k % 2) + + # generate arbitrary matrix size (2 to maxm) + num1 = nint(maxm*rand()) + num2 = nint(maxm*rand()) + m = int(max(num1, num2)) + n = int(min(num1, num2)) + + # create matrix + A = mp.matrix(m,n) + + # populate matrix values with arbitrary integers + if flg: + flg = False + dtype = 'complex' + for j in xrange(0,n): + for i in xrange(0,m): + val = nint(lowlimit + (uplimit-lowlimit)*rand()) + val2 = nint(lowlimit + (uplimit-lowlimit)*rand()) + A[i,j] = mpc(val, val2) + else: + flg = True + dtype = 'real' + for j in xrange(0,n): + for i in xrange(0,m): + val = nint(lowlimit + (uplimit-lowlimit)*rand()) + A[i,j] = mpf(val) + + # perform A -> QR decomposition + Q, R = qr(A, mode, edps = exdps) + + #print('\n\n A = \n', nstr(A, 4)) + #print('\n Q = \n', nstr(Q, 4)) + #print('\n R = \n', nstr(R, 4)) + #print('\n Q*R = \n', nstr(Q*R, 4)) + + maxnorm = mpf('1.0E-11') + n1 = norm(A - Q * R) + #print '\n Norm of A - Q * R = ', n1 + assert n1 <= maxnorm + + if dtype == 'real': + n1 = norm(eye(m) - Q.T * Q) + #print ' Norm of I - Q.T * Q = ', n1 + assert n1 <= maxnorm + + n1 = norm(eye(m) - Q * Q.T) + #print ' Norm of I - Q * Q.T = ', n1 + assert n1 <= maxnorm + + if dtype == 'complex': + n1 = norm(eye(m) - Q.T * Q.conjugate()) + #print ' Norm of I - Q.T * Q.conjugate() = ', n1 + assert n1 <= maxnorm + + n1 = norm(eye(m) - Q.conjugate() * Q.T) + #print ' Norm of I - Q.conjugate() * Q.T = ', n1 + assert n1 <= maxnorm diff --git a/lib/python3.12/site-packages/mpmath/tests/test_matrices.py b/lib/python3.12/site-packages/mpmath/tests/test_matrices.py new file mode 100644 index 0000000000000000000000000000000000000000..1547b90664dba66a98a7f026a04a4ed1aa1ed3b4 --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/tests/test_matrices.py @@ -0,0 +1,253 @@ +import pytest +import sys +from mpmath import * + +def test_matrix_basic(): + A1 = matrix(3) + for i in range(3): + A1[i,i] = 1 + assert A1 == eye(3) + assert A1 == matrix(A1) + A2 = matrix(3, 2) + assert not A2._matrix__data + A3 = matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) + assert list(A3) == list(range(1, 10)) + A3[1,1] = 0 + assert not (1, 1) in A3._matrix__data + A4 = matrix([[1, 2, 3], [4, 5, 6]]) + A5 = matrix([[6, -1], [3, 2], [0, -3]]) + assert A4 * A5 == matrix([[12, -6], [39, -12]]) + assert A1 * A3 == A3 * A1 == A3 + pytest.raises(ValueError, lambda: A2*A2) + l = [[10, 20, 30], [40, 0, 60], [70, 80, 90]] + A6 = matrix(l) + assert A6.tolist() == l + assert A6 == eval(repr(A6)) + A6 = fp.matrix(A6) + assert A6 == eval(repr(A6)) + assert A6*1j == eval(repr(A6*1j)) + assert A3 * 10 == 10 * A3 == A6 + assert A2.rows == 3 + assert A2.cols == 2 + A3.rows = 2 + A3.cols = 2 + assert len(A3._matrix__data) == 3 + assert A4 + A4 == 2*A4 + pytest.raises(ValueError, lambda: A4 + A2) + assert sum(A1 - A1) == 0 + A7 = matrix([[1, 2], [3, 4], [5, 6], [7, 8]]) + x = matrix([10, -10]) + assert A7*x == matrix([-10, -10, -10, -10]) + A8 = ones(5) + assert sum((A8 + 1) - (2 - zeros(5))) == 0 + assert (1 + ones(4)) / 2 - 1 == zeros(4) + assert eye(3)**10 == eye(3) + pytest.raises(ValueError, lambda: A7**2) + A9 = randmatrix(3) + A10 = matrix(A9) + A9[0,0] = -100 + assert A9 != A10 + assert nstr(A9) + +def test_matmul(): + """ + Test the PEP465 "@" matrix multiplication syntax. + To avoid syntax errors when importing this file in Python 3.5 and below, we have to use exec() - sorry for that. + """ + # TODO remove exec() wrapper as soon as we drop support for Python <= 3.5 + if sys.hexversion < 0x30500f0: + # we are on Python < 3.5 + pytest.skip("'@' (__matmul__) is only supported in Python 3.5 or newer") + A4 = matrix([[1, 2, 3], [4, 5, 6]]) + A5 = matrix([[6, -1], [3, 2], [0, -3]]) + exec("assert A4 @ A5 == A4 * A5") + +def test_matrix_slices(): + A = matrix([ [1, 2, 3], + [4, 5 ,6], + [7, 8 ,9]]) + V = matrix([1,2,3,4,5]) + + # Get slice + assert A[:,:] == A + assert A[:,1] == matrix([[2],[5],[8]]) + assert A[2,:] == matrix([[7, 8 ,9]]) + assert A[1:3,1:3] == matrix([[5,6],[8,9]]) + assert V[2:4] == matrix([3,4]) + pytest.raises(IndexError, lambda: A[:,1:6]) + + # Assign slice with matrix + A1 = matrix(3) + A1[:,:] = A + assert A1[:,:] == matrix([[1, 2, 3], + [4, 5 ,6], + [7, 8 ,9]]) + A1[0,:] = matrix([[10, 11, 12]]) + assert A1 == matrix([ [10, 11, 12], + [4, 5 ,6], + [7, 8 ,9]]) + A1[:,2] = matrix([[13], [14], [15]]) + assert A1 == matrix([ [10, 11, 13], + [4, 5 ,14], + [7, 8 ,15]]) + A1[:2,:2] = matrix([[16, 17], [18 , 19]]) + assert A1 == matrix([ [16, 17, 13], + [18, 19 ,14], + [7, 8 ,15]]) + V[1:3] = 10 + assert V == matrix([1,10,10,4,5]) + with pytest.raises(ValueError): + A1[2,:] = A[:,1] + + with pytest.raises(IndexError): + A1[2,1:20] = A[:,:] + + # Assign slice with scalar + A1[:,2] = 10 + assert A1 == matrix([ [16, 17, 10], + [18, 19 ,10], + [7, 8 ,10]]) + A1[:,:] = 40 + for x in A1: + assert x == 40 + + +def test_matrix_power(): + A = matrix([[1, 2], [3, 4]]) + assert A**2 == A*A + assert A**3 == A*A*A + assert A**-1 == inverse(A) + assert A**-2 == inverse(A*A) + +def test_matrix_transform(): + A = matrix([[1, 2], [3, 4], [5, 6]]) + assert A.T == A.transpose() == matrix([[1, 3, 5], [2, 4, 6]]) + swap_row(A, 1, 2) + assert A == matrix([[1, 2], [5, 6], [3, 4]]) + l = [1, 2] + swap_row(l, 0, 1) + assert l == [2, 1] + assert extend(eye(3), [1,2,3]) == matrix([[1,0,0,1],[0,1,0,2],[0,0,1,3]]) + +def test_matrix_conjugate(): + A = matrix([[1 + j, 0], [2, j]]) + assert A.conjugate() == matrix([[mpc(1, -1), 0], [2, mpc(0, -1)]]) + assert A.transpose_conj() == A.H == matrix([[mpc(1, -1), 2], + [0, mpc(0, -1)]]) + +def test_matrix_creation(): + assert diag([1, 2, 3]) == matrix([[1, 0, 0], [0, 2, 0], [0, 0, 3]]) + A1 = ones(2, 3) + assert A1.rows == 2 and A1.cols == 3 + for a in A1: + assert a == 1 + A2 = zeros(3, 2) + assert A2.rows == 3 and A2.cols == 2 + for a in A2: + assert a == 0 + assert randmatrix(10) != randmatrix(10) + one = mpf(1) + assert hilbert(3) == matrix([[one, one/2, one/3], + [one/2, one/3, one/4], + [one/3, one/4, one/5]]) + +def test_norms(): + # matrix norms + A = matrix([[1, -2], [-3, -1], [2, 1]]) + assert mnorm(A,1) == 6 + assert mnorm(A,inf) == 4 + assert mnorm(A,'F') == sqrt(20) + # vector norms + assert norm(-3) == 3 + x = [1, -2, 7, -12] + assert norm(x, 1) == 22 + assert round(norm(x, 2), 10) == 14.0712472795 + assert round(norm(x, 10), 10) == 12.0054633727 + assert norm(x, inf) == 12 + +def test_vector(): + x = matrix([0, 1, 2, 3, 4]) + assert x == matrix([[0], [1], [2], [3], [4]]) + assert x[3] == 3 + assert len(x._matrix__data) == 4 + assert list(x) == list(range(5)) + x[0] = -10 + x[4] = 0 + assert x[0] == -10 + assert len(x) == len(x.T) == 5 + assert x.T*x == matrix([[114]]) + +def test_matrix_copy(): + A = ones(6) + B = A.copy() + C = +A + assert A == B + assert A == C + B[0,0] = 0 + assert A != B + C[0,0] = 42 + assert A != C + +def test_matrix_numpy(): + try: + import numpy + except ImportError: + return + l = [[1, 2], [3, 4], [5, 6]] + a = numpy.array(l) + assert matrix(l) == matrix(a) + +def test_interval_matrix_scalar_mult(): + """Multiplication of iv.matrix and any scalar type""" + a = mpi(-1, 1) + b = a + a * 2j + c = mpf(42) + d = c + c * 2j + e = 1.234 + f = fp.convert(e) + g = e + e * 3j + h = fp.convert(g) + M = iv.ones(1) + for x in [a, b, c, d, e, f, g, h]: + assert x * M == iv.matrix([x]) + assert M * x == iv.matrix([x]) + +@pytest.mark.xfail() +def test_interval_matrix_matrix_mult(): + """Multiplication of iv.matrix and other matrix types""" + A = ones(1) + B = fp.ones(1) + M = iv.ones(1) + for X in [A, B, M]: + assert X * M == iv.matrix(X) + assert X * M == X + assert M * X == iv.matrix(X) + assert M * X == X + +def test_matrix_conversion_to_iv(): + # Test that matrices with foreign datatypes are properly converted + for other_type_eye in [eye(3), fp.eye(3), iv.eye(3)]: + A = iv.matrix(other_type_eye) + B = iv.eye(3) + assert type(A[0,0]) == type(B[0,0]) + assert A.tolist() == B.tolist() + +def test_interval_matrix_mult_bug(): + # regression test for interval matrix multiplication: + # result must be nonzero-width and contain the exact result + x = convert('1.00000000000001') # note: this is implicitly rounded to some near mpf float value + A = matrix([[x]]) + B = iv.matrix(A) + C = iv.matrix([[x]]) + assert B == C + B = B * B + C = C * C + assert B == C + assert B[0, 0].delta > 1e-16 + assert B[0, 0].delta < 3e-16 + assert C[0, 0].delta > 1e-16 + assert C[0, 0].delta < 3e-16 + assert mp.mpf('1.00000000000001998401444325291756783368705994138804689654') in B[0, 0] + assert mp.mpf('1.00000000000001998401444325291756783368705994138804689654') in C[0, 0] + # the following caused an error before the bug was fixed + assert iv.matrix(mp.eye(2)) * (iv.ones(2) + mpi(1, 2)) == iv.matrix([[mpi(2, 3), mpi(2, 3)], [mpi(2, 3), mpi(2, 3)]]) diff --git a/lib/python3.12/site-packages/mpmath/tests/test_mpmath.py b/lib/python3.12/site-packages/mpmath/tests/test_mpmath.py new file mode 100644 index 0000000000000000000000000000000000000000..9f1fe36ae9b1b0feca4677eeb90396bfa7ed8f7a --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/tests/test_mpmath.py @@ -0,0 +1,7 @@ +from mpmath.libmp import * +from mpmath import * + +def test_newstyle_classes(): + for cls in [mp, fp, iv, mpf, mpc]: + for s in cls.__class__.__mro__: + assert isinstance(s, type) diff --git a/lib/python3.12/site-packages/mpmath/tests/test_ode.py b/lib/python3.12/site-packages/mpmath/tests/test_ode.py new file mode 100644 index 0000000000000000000000000000000000000000..6b6dbffa79cfd4ca6dbf14f8591296ee48b16682 --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/tests/test_ode.py @@ -0,0 +1,73 @@ +#from mpmath.calculus import ODE_step_euler, ODE_step_rk4, odeint, arange +from mpmath import odefun, cos, sin, mpf, sinc, mp + +''' +solvers = [ODE_step_euler, ODE_step_rk4] + +def test_ode1(): + """ + Let's solve: + + x'' + w**2 * x = 0 + + i.e. x1 = x, x2 = x1': + + x1' = x2 + x2' = -x1 + """ + def derivs((x1, x2), t): + return x2, -x1 + + for solver in solvers: + t = arange(0, 3.1415926, 0.005) + sol = odeint(derivs, (0., 1.), t, solver) + x1 = [a[0] for a in sol] + x2 = [a[1] for a in sol] + # the result is x1 = sin(t), x2 = cos(t) + # let's just check the end points for t = pi + assert abs(x1[-1]) < 1e-2 + assert abs(x2[-1] - (-1)) < 1e-2 + +def test_ode2(): + """ + Let's solve: + + x' - x = 0 + + i.e. x = exp(x) + + """ + def derivs((x), t): + return x + + for solver in solvers: + t = arange(0, 1, 1e-3) + sol = odeint(derivs, (1.,), t, solver) + x = [a[0] for a in sol] + # the result is x = exp(t) + # let's just check the end point for t = 1, i.e. x = e + assert abs(x[-1] - 2.718281828) < 1e-2 +''' + +def test_odefun_rational(): + mp.dps = 15 + # A rational function + f = lambda t: 1/(1+mpf(t)**2) + g = odefun(lambda x, y: [-2*x*y[0]**2], 0, [f(0)]) + assert f(2).ae(g(2)[0]) + +def test_odefun_sinc_large(): + mp.dps = 15 + # Sinc function; test for large x + f = sinc + g = odefun(lambda x, y: [(cos(x)-y[0])/x], 1, [f(1)], tol=0.01, degree=5) + assert abs(f(100) - g(100)[0])/f(100) < 0.01 + +def test_odefun_harmonic(): + mp.dps = 15 + # Harmonic oscillator + f = odefun(lambda x, y: [-y[1], y[0]], 0, [1, 0]) + for x in [0, 1, 2.5, 8, 3.7]: # we go back to 3.7 to check caching + c, s = f(x) + assert c.ae(cos(x)) + assert s.ae(sin(x)) diff --git a/lib/python3.12/site-packages/mpmath/tests/test_pickle.py b/lib/python3.12/site-packages/mpmath/tests/test_pickle.py new file mode 100644 index 0000000000000000000000000000000000000000..c3d96e73a53603e0fa3f9525c5c0059725bdffb7 --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/tests/test_pickle.py @@ -0,0 +1,27 @@ +import os +import tempfile +import pickle + +from mpmath import * + +def pickler(obj): + fn = tempfile.mktemp() + + f = open(fn, 'wb') + pickle.dump(obj, f) + f.close() + + f = open(fn, 'rb') + obj2 = pickle.load(f) + f.close() + os.remove(fn) + + return obj2 + +def test_pickle(): + + obj = mpf('0.5') + assert obj == pickler(obj) + + obj = mpc('0.5','0.2') + assert obj == pickler(obj) diff --git a/lib/python3.12/site-packages/mpmath/tests/test_power.py b/lib/python3.12/site-packages/mpmath/tests/test_power.py new file mode 100644 index 0000000000000000000000000000000000000000..7a2447a62c36f9e02df79b9a40a8603f8a69b1d8 --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/tests/test_power.py @@ -0,0 +1,156 @@ +from mpmath import * +from mpmath.libmp import * + +import random + +def test_fractional_pow(): + mp.dps = 15 + assert mpf(16) ** 2.5 == 1024 + assert mpf(64) ** 0.5 == 8 + assert mpf(64) ** -0.5 == 0.125 + assert mpf(16) ** -2.5 == 0.0009765625 + assert (mpf(10) ** 0.5).ae(3.1622776601683791) + assert (mpf(10) ** 2.5).ae(316.2277660168379) + assert (mpf(10) ** -0.5).ae(0.31622776601683794) + assert (mpf(10) ** -2.5).ae(0.0031622776601683794) + assert (mpf(10) ** 0.3).ae(1.9952623149688795) + assert (mpf(10) ** -0.3).ae(0.50118723362727224) + +def test_pow_integer_direction(): + """ + Test that inexact integer powers are rounded in the right + direction. + """ + random.seed(1234) + for prec in [10, 53, 200]: + for i in range(50): + a = random.randint(1<<(prec-1), 1< ab + + +def test_pow_epsilon_rounding(): + """ + Stress test directed rounding for powers with integer exponents. + Basically, we look at the following cases: + + >>> 1.0001 ** -5 # doctest: +SKIP + 0.99950014996500702 + >>> 0.9999 ** -5 # doctest: +SKIP + 1.000500150035007 + >>> (-1.0001) ** -5 # doctest: +SKIP + -0.99950014996500702 + >>> (-0.9999) ** -5 # doctest: +SKIP + -1.000500150035007 + + >>> 1.0001 ** -6 # doctest: +SKIP + 0.99940020994401269 + >>> 0.9999 ** -6 # doctest: +SKIP + 1.0006002100560125 + >>> (-1.0001) ** -6 # doctest: +SKIP + 0.99940020994401269 + >>> (-0.9999) ** -6 # doctest: +SKIP + 1.0006002100560125 + + etc. + + We run the tests with values a very small epsilon away from 1: + small enough that the result is indistinguishable from 1 when + rounded to nearest at the output precision. We check that the + result is not erroneously rounded to 1 in cases where the + rounding should be done strictly away from 1. + """ + + def powr(x, n, r): + return make_mpf(mpf_pow_int(x._mpf_, n, mp.prec, r)) + + for (inprec, outprec) in [(100, 20), (5000, 3000)]: + + mp.prec = inprec + + pos10001 = mpf(1) + mpf(2)**(-inprec+5) + pos09999 = mpf(1) - mpf(2)**(-inprec+5) + neg10001 = -pos10001 + neg09999 = -pos09999 + + mp.prec = outprec + r = round_up + assert powr(pos10001, 5, r) > 1 + assert powr(pos09999, 5, r) == 1 + assert powr(neg10001, 5, r) < -1 + assert powr(neg09999, 5, r) == -1 + assert powr(pos10001, 6, r) > 1 + assert powr(pos09999, 6, r) == 1 + assert powr(neg10001, 6, r) > 1 + assert powr(neg09999, 6, r) == 1 + + assert powr(pos10001, -5, r) == 1 + assert powr(pos09999, -5, r) > 1 + assert powr(neg10001, -5, r) == -1 + assert powr(neg09999, -5, r) < -1 + assert powr(pos10001, -6, r) == 1 + assert powr(pos09999, -6, r) > 1 + assert powr(neg10001, -6, r) == 1 + assert powr(neg09999, -6, r) > 1 + + r = round_down + assert powr(pos10001, 5, r) == 1 + assert powr(pos09999, 5, r) < 1 + assert powr(neg10001, 5, r) == -1 + assert powr(neg09999, 5, r) > -1 + assert powr(pos10001, 6, r) == 1 + assert powr(pos09999, 6, r) < 1 + assert powr(neg10001, 6, r) == 1 + assert powr(neg09999, 6, r) < 1 + + assert powr(pos10001, -5, r) < 1 + assert powr(pos09999, -5, r) == 1 + assert powr(neg10001, -5, r) > -1 + assert powr(neg09999, -5, r) == -1 + assert powr(pos10001, -6, r) < 1 + assert powr(pos09999, -6, r) == 1 + assert powr(neg10001, -6, r) < 1 + assert powr(neg09999, -6, r) == 1 + + r = round_ceiling + assert powr(pos10001, 5, r) > 1 + assert powr(pos09999, 5, r) == 1 + assert powr(neg10001, 5, r) == -1 + assert powr(neg09999, 5, r) > -1 + assert powr(pos10001, 6, r) > 1 + assert powr(pos09999, 6, r) == 1 + assert powr(neg10001, 6, r) > 1 + assert powr(neg09999, 6, r) == 1 + + assert powr(pos10001, -5, r) == 1 + assert powr(pos09999, -5, r) > 1 + assert powr(neg10001, -5, r) > -1 + assert powr(neg09999, -5, r) == -1 + assert powr(pos10001, -6, r) == 1 + assert powr(pos09999, -6, r) > 1 + assert powr(neg10001, -6, r) == 1 + assert powr(neg09999, -6, r) > 1 + + r = round_floor + assert powr(pos10001, 5, r) == 1 + assert powr(pos09999, 5, r) < 1 + assert powr(neg10001, 5, r) < -1 + assert powr(neg09999, 5, r) == -1 + assert powr(pos10001, 6, r) == 1 + assert powr(pos09999, 6, r) < 1 + assert powr(neg10001, 6, r) == 1 + assert powr(neg09999, 6, r) < 1 + + assert powr(pos10001, -5, r) < 1 + assert powr(pos09999, -5, r) == 1 + assert powr(neg10001, -5, r) == -1 + assert powr(neg09999, -5, r) < -1 + assert powr(pos10001, -6, r) < 1 + assert powr(pos09999, -6, r) == 1 + assert powr(neg10001, -6, r) < 1 + assert powr(neg09999, -6, r) == 1 + + mp.dps = 15 diff --git a/lib/python3.12/site-packages/mpmath/tests/test_quad.py b/lib/python3.12/site-packages/mpmath/tests/test_quad.py new file mode 100644 index 0000000000000000000000000000000000000000..fc71c5f5ef9c0ecd876c988e7d033b321f065cdc --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/tests/test_quad.py @@ -0,0 +1,95 @@ +import pytest +from mpmath import * + +def ae(a, b): + return abs(a-b) < 10**(-mp.dps+5) + +def test_basic_integrals(): + for prec in [15, 30, 100]: + mp.dps = prec + assert ae(quadts(lambda x: x**3 - 3*x**2, [-2, 4]), -12) + assert ae(quadgl(lambda x: x**3 - 3*x**2, [-2, 4]), -12) + assert ae(quadts(sin, [0, pi]), 2) + assert ae(quadts(sin, [0, 2*pi]), 0) + assert ae(quadts(exp, [-inf, -1]), 1/e) + assert ae(quadts(lambda x: exp(-x), [0, inf]), 1) + assert ae(quadts(lambda x: exp(-x*x), [-inf, inf]), sqrt(pi)) + assert ae(quadts(lambda x: 1/(1+x*x), [-1, 1]), pi/2) + assert ae(quadts(lambda x: 1/(1+x*x), [-inf, inf]), pi) + assert ae(quadts(lambda x: 2*sqrt(1-x*x), [-1, 1]), pi) + mp.dps = 15 + +def test_multiple_intervals(): + y,err = quad(lambda x: sign(x), [-0.5, 0.9, 1], maxdegree=2, error=True) + assert abs(y-0.5) < 2*err + +def test_quad_symmetry(): + assert quadts(sin, [-1, 1]) == 0 + assert quadgl(sin, [-1, 1]) == 0 + +def test_quad_infinite_mirror(): + # Check mirrored infinite interval + assert ae(quad(lambda x: exp(-x*x), [inf,-inf]), -sqrt(pi)) + assert ae(quad(lambda x: exp(x), [0,-inf]), -1) + +def test_quadgl_linear(): + assert quadgl(lambda x: x, [0, 1], maxdegree=1).ae(0.5) + +def test_complex_integration(): + assert quadts(lambda x: x, [0, 1+j]).ae(j) + +def test_quadosc(): + mp.dps = 15 + assert quadosc(lambda x: sin(x)/x, [0, inf], period=2*pi).ae(pi/2) + +# Double integrals +def test_double_trivial(): + assert ae(quadts(lambda x, y: x, [0, 1], [0, 1]), 0.5) + assert ae(quadts(lambda x, y: x, [-1, 1], [-1, 1]), 0.0) + +def test_double_1(): + assert ae(quadts(lambda x, y: cos(x+y/2), [-pi/2, pi/2], [0, pi]), 4) + +def test_double_2(): + assert ae(quadts(lambda x, y: (x-1)/((1-x*y)*log(x*y)), [0, 1], [0, 1]), euler) + +def test_double_3(): + assert ae(quadts(lambda x, y: 1/sqrt(1+x*x+y*y), [-1, 1], [-1, 1]), 4*log(2+sqrt(3))-2*pi/3) + +def test_double_4(): + assert ae(quadts(lambda x, y: 1/(1-x*x * y*y), [0, 1], [0, 1]), pi**2 / 8) + +def test_double_5(): + assert ae(quadts(lambda x, y: 1/(1-x*y), [0, 1], [0, 1]), pi**2 / 6) + +def test_double_6(): + assert ae(quadts(lambda x, y: exp(-(x+y)), [0, inf], [0, inf]), 1) + +def test_double_7(): + assert ae(quadts(lambda x, y: exp(-x*x-y*y), [-inf, inf], [-inf, inf]), pi) + + +# Test integrals from "Experimentation in Mathematics" by Borwein, +# Bailey & Girgensohn +def test_expmath_integrals(): + for prec in [15, 30, 50]: + mp.dps = prec + assert ae(quadts(lambda x: x/sinh(x), [0, inf]), pi**2 / 4) + assert ae(quadts(lambda x: log(x)**2 / (1+x**2), [0, inf]), pi**3 / 8) + assert ae(quadts(lambda x: (1+x**2)/(1+x**4), [0, inf]), pi/sqrt(2)) + assert ae(quadts(lambda x: log(x)/cosh(x)**2, [0, inf]), log(pi)-2*log(2)-euler) + assert ae(quadts(lambda x: log(1+x**3)/(1-x+x**2), [0, inf]), 2*pi*log(3)/sqrt(3)) + assert ae(quadts(lambda x: log(x)**2 / (x**2+x+1), [0, 1]), 8*pi**3 / (81*sqrt(3))) + assert ae(quadts(lambda x: log(cos(x))**2, [0, pi/2]), pi/2 * (log(2)**2+pi**2/12)) + assert ae(quadts(lambda x: x**2 / sin(x)**2, [0, pi/2]), pi*log(2)) + assert ae(quadts(lambda x: x**2/sqrt(exp(x)-1), [0, inf]), 4*pi*(log(2)**2 + pi**2/12)) + assert ae(quadts(lambda x: x*exp(-x)*sqrt(1-exp(-2*x)), [0, inf]), pi*(1+2*log(2))/8) + mp.dps = 15 + +# Do not reach full accuracy +@pytest.mark.xfail +def test_expmath_fail(): + assert ae(quadts(lambda x: sqrt(tan(x)), [0, pi/2]), pi*sqrt(2)/2) + assert ae(quadts(lambda x: atan(x)/(x*sqrt(1-x**2)), [0, 1]), pi*log(1+sqrt(2))/2) + assert ae(quadts(lambda x: log(1+x**2)/x**2, [0, 1]), pi/2-log(2)) + assert ae(quadts(lambda x: x**2/((1+x**4)*sqrt(1-x**4)), [0, 1]), pi/8) diff --git a/lib/python3.12/site-packages/mpmath/tests/test_rootfinding.py b/lib/python3.12/site-packages/mpmath/tests/test_rootfinding.py new file mode 100644 index 0000000000000000000000000000000000000000..7c3c06463682eb1fd60efeb75b809bbb932a241c --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/tests/test_rootfinding.py @@ -0,0 +1,91 @@ +import pytest +from mpmath import * +from mpmath.calculus.optimization import Secant, Muller, Bisection, Illinois, \ + Pegasus, Anderson, Ridder, ANewton, Newton, MNewton, MDNewton + +def test_findroot(): + # old tests, assuming secant + mp.dps = 15 + assert findroot(lambda x: 4*x-3, mpf(5)).ae(0.75) + assert findroot(sin, mpf(3)).ae(pi) + assert findroot(sin, (mpf(3), mpf(3.14))).ae(pi) + assert findroot(lambda x: x*x+1, mpc(2+2j)).ae(1j) + # test all solvers with 1 starting point + f = lambda x: cos(x) + for solver in [Newton, Secant, MNewton, Muller, ANewton]: + x = findroot(f, 2., solver=solver) + assert abs(f(x)) < eps + # test all solvers with interval of 2 points + for solver in [Secant, Muller, Bisection, Illinois, Pegasus, Anderson, + Ridder]: + x = findroot(f, (1., 2.), solver=solver) + assert abs(f(x)) < eps + # test types + f = lambda x: (x - 2)**2 + + assert isinstance(findroot(f, 1, tol=1e-10), mpf) + assert isinstance(iv.findroot(f, 1., tol=1e-10), iv.mpf) + assert isinstance(fp.findroot(f, 1, tol=1e-10), float) + assert isinstance(fp.findroot(f, 1+0j, tol=1e-10), complex) + + # issue 401 + with pytest.raises(ValueError): + with workprec(2): + findroot(lambda x: x**2 - 4456178*x + 60372201703370, + mpc(real='5.278e+13', imag='-5.278e+13')) + + # issue 192 + with pytest.raises(ValueError): + findroot(lambda x: -1, 0) + + # issue 387 + with pytest.raises(ValueError): + findroot(lambda p: (1 - p)**30 - 1, 0.9) + +def test_bisection(): + # issue 273 + assert findroot(lambda x: x**2-1,(0,2),solver='bisect') == 1 + +def test_mnewton(): + f = lambda x: polyval([1,3,3,1],x) + x = findroot(f, -0.9, solver='mnewton') + assert abs(f(x)) < eps + +def test_anewton(): + f = lambda x: (x - 2)**100 + x = findroot(f, 1., solver=ANewton) + assert abs(f(x)) < eps + +def test_muller(): + f = lambda x: (2 + x)**3 + 2 + x = findroot(f, 1., solver=Muller) + assert abs(f(x)) < eps + +def test_multiplicity(): + for i in range(1, 5): + assert multiplicity(lambda x: (x - 1)**i, 1) == i + assert multiplicity(lambda x: x**2, 1) == 0 + +def test_multidimensional(): + def f(*x): + return [3*x[0]**2-2*x[1]**2-1, x[0]**2-2*x[0]+x[1]**2+2*x[1]-8] + assert mnorm(jacobian(f, (1,-2)) - matrix([[6,8],[0,-2]]),1) < 1.e-7 + for x, error in MDNewton(mp, f, (1,-2), verbose=0, + norm=lambda x: norm(x, inf)): + pass + assert norm(f(*x), 2) < 1e-14 + # The Chinese mathematician Zhu Shijie was the very first to solve this + # nonlinear system 700 years ago + f1 = lambda x, y: -x + 2*y + f2 = lambda x, y: (x**2 + x*(y**2 - 2) - 4*y) / (x + 4) + f3 = lambda x, y: sqrt(x**2 + y**2) + def f(x, y): + f1x = f1(x, y) + return (f2(x, y) - f1x, f3(x, y) - f1x) + x = findroot(f, (10, 10)) + assert [int(round(i)) for i in x] == [3, 4] + +def test_trivial(): + assert findroot(lambda x: 0, 1) == 1 + assert findroot(lambda x: x, 0) == 0 + #assert findroot(lambda x, y: x + y, (1, -1)) == (1, -1) diff --git a/lib/python3.12/site-packages/mpmath/tests/test_special.py b/lib/python3.12/site-packages/mpmath/tests/test_special.py new file mode 100644 index 0000000000000000000000000000000000000000..30825abd89ada00f937260cb51ef649546be7021 --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/tests/test_special.py @@ -0,0 +1,113 @@ +from mpmath import * + +def test_special(): + assert inf == inf + assert inf != -inf + assert -inf == -inf + assert inf != nan + assert nan != nan + assert isnan(nan) + assert --inf == inf + assert abs(inf) == inf + assert abs(-inf) == inf + assert abs(nan) != abs(nan) + + assert isnan(inf - inf) + assert isnan(inf + (-inf)) + assert isnan(-inf - (-inf)) + + assert isnan(inf + nan) + assert isnan(-inf + nan) + + assert mpf(2) + inf == inf + assert 2 + inf == inf + assert mpf(2) - inf == -inf + assert 2 - inf == -inf + + assert inf > 3 + assert 3 < inf + assert 3 > -inf + assert -inf < 3 + assert inf > mpf(3) + assert mpf(3) < inf + assert mpf(3) > -inf + assert -inf < mpf(3) + + assert not (nan < 3) + assert not (nan > 3) + + assert isnan(inf * 0) + assert isnan(-inf * 0) + assert inf * 3 == inf + assert inf * -3 == -inf + assert -inf * 3 == -inf + assert -inf * -3 == inf + assert inf * inf == inf + assert -inf * -inf == inf + + assert isnan(nan / 3) + assert inf / -3 == -inf + assert inf / 3 == inf + assert 3 / inf == 0 + assert -3 / inf == 0 + assert 0 / inf == 0 + assert isnan(inf / inf) + assert isnan(inf / -inf) + assert isnan(inf / nan) + + assert mpf('inf') == mpf('+inf') == inf + assert mpf('-inf') == -inf + assert isnan(mpf('nan')) + + assert isinf(inf) + assert isinf(-inf) + assert not isinf(mpf(0)) + assert not isinf(nan) + +def test_special_powers(): + assert inf**3 == inf + assert isnan(inf**0) + assert inf**-3 == 0 + assert (-inf)**2 == inf + assert (-inf)**3 == -inf + assert isnan((-inf)**0) + assert (-inf)**-2 == 0 + assert (-inf)**-3 == 0 + assert isnan(nan**5) + assert isnan(nan**0) + +def test_functions_special(): + assert exp(inf) == inf + assert exp(-inf) == 0 + assert isnan(exp(nan)) + assert log(inf) == inf + assert isnan(log(nan)) + assert isnan(sin(inf)) + assert isnan(sin(nan)) + assert atan(inf).ae(pi/2) + assert atan(-inf).ae(-pi/2) + assert isnan(sqrt(nan)) + assert sqrt(inf) == inf + +def test_convert_special(): + float_inf = 1e300 * 1e300 + float_ninf = -float_inf + float_nan = float_inf/float_ninf + assert mpf(3) * float_inf == inf + assert mpf(3) * float_ninf == -inf + assert isnan(mpf(3) * float_nan) + assert not (mpf(3) < float_nan) + assert not (mpf(3) > float_nan) + assert not (mpf(3) <= float_nan) + assert not (mpf(3) >= float_nan) + assert float(mpf('1e1000')) == float_inf + assert float(mpf('-1e1000')) == float_ninf + assert float(mpf('1e100000000000000000')) == float_inf + assert float(mpf('-1e100000000000000000')) == float_ninf + assert float(mpf('1e-100000000000000000')) == 0.0 + +def test_div_bug(): + assert isnan(nan/1) + assert isnan(nan/2) + assert inf/2 == inf + assert (-inf)/2 == -inf diff --git a/lib/python3.12/site-packages/mpmath/tests/test_str.py b/lib/python3.12/site-packages/mpmath/tests/test_str.py new file mode 100644 index 0000000000000000000000000000000000000000..569244f252c057ec1029b7efbd8b0ffbfbc47522 --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/tests/test_str.py @@ -0,0 +1,14 @@ +from mpmath import nstr, matrix, inf + +def test_nstr(): + m = matrix([[0.75, 0.190940654, -0.0299195971], + [0.190940654, 0.65625, 0.205663228], + [-0.0299195971, 0.205663228, 0.64453125e-20]]) + assert nstr(m, 4, min_fixed=-inf) == \ + '''[ 0.75 0.1909 -0.02992] +[ 0.1909 0.6563 0.2057] +[-0.02992 0.2057 0.000000000000000000006445]''' + assert nstr(m, 4) == \ + '''[ 0.75 0.1909 -0.02992] +[ 0.1909 0.6563 0.2057] +[-0.02992 0.2057 6.445e-21]''' diff --git a/lib/python3.12/site-packages/mpmath/tests/test_summation.py b/lib/python3.12/site-packages/mpmath/tests/test_summation.py new file mode 100644 index 0000000000000000000000000000000000000000..04ffd29f994e1e6310678eec292c0e03f2d6c725 --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/tests/test_summation.py @@ -0,0 +1,53 @@ +from mpmath import * + +def test_sumem(): + mp.dps = 15 + assert sumem(lambda k: 1/k**2.5, [50, 100]).ae(0.0012524505324784962) + assert sumem(lambda k: k**4 + 3*k + 1, [10, 100]).ae(2050333103) + +def test_nsum(): + mp.dps = 15 + assert nsum(lambda x: x**2, [1, 3]) == 14 + assert nsum(lambda k: 1/factorial(k), [0, inf]).ae(e) + assert nsum(lambda k: (-1)**(k+1) / k, [1, inf]).ae(log(2)) + assert nsum(lambda k: (-1)**(k+1) / k**2, [1, inf]).ae(pi**2 / 12) + assert nsum(lambda k: (-1)**k / log(k), [2, inf]).ae(0.9242998972229388) + assert nsum(lambda k: 1/k**2, [1, inf]).ae(pi**2 / 6) + assert nsum(lambda k: 2**k/fac(k), [0, inf]).ae(exp(2)) + assert nsum(lambda k: 1/k**2, [4, inf], method='e').ae(0.2838229557371153) + assert abs(fp.nsum(lambda k: 1/k**4, [1, fp.inf]) - 1.082323233711138) < 1e-5 + assert abs(fp.nsum(lambda k: 1/k**4, [1, fp.inf], method='e') - 1.082323233711138) < 1e-4 + +def test_nprod(): + mp.dps = 15 + assert nprod(lambda k: exp(1/k**2), [1,inf], method='r').ae(exp(pi**2/6)) + assert nprod(lambda x: x**2, [1, 3]) == 36 + +def test_fsum(): + mp.dps = 15 + assert fsum([]) == 0 + assert fsum([-4]) == -4 + assert fsum([2,3]) == 5 + assert fsum([1e-100,1]) == 1 + assert fsum([1,1e-100]) == 1 + assert fsum([1e100,1]) == 1e100 + assert fsum([1,1e100]) == 1e100 + assert fsum([1e-100,0]) == 1e-100 + assert fsum([1e-100,1e100,1e-100]) == 1e100 + assert fsum([2,1+1j,1]) == 4+1j + assert fsum([2,inf,3]) == inf + assert fsum([2,-1], absolute=1) == 3 + assert fsum([2,-1], squared=1) == 5 + assert fsum([1,1+j], squared=1) == 1+2j + assert fsum([1,3+4j], absolute=1) == 6 + assert fsum([1,2+3j], absolute=1, squared=1) == 14 + assert isnan(fsum([inf,-inf])) + assert fsum([inf,-inf], absolute=1) == inf + assert fsum([inf,-inf], squared=1) == inf + assert fsum([inf,-inf], absolute=1, squared=1) == inf + assert iv.fsum([1,mpi(2,3)]) == mpi(3,4) + +def test_fprod(): + mp.dps = 15 + assert fprod([]) == 1 + assert fprod([2,3]) == 6 diff --git a/lib/python3.12/site-packages/mpmath/tests/test_trig.py b/lib/python3.12/site-packages/mpmath/tests/test_trig.py new file mode 100644 index 0000000000000000000000000000000000000000..c70a2a0ff4c44c784404ecdb15357d5b91a992d6 --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/tests/test_trig.py @@ -0,0 +1,136 @@ +from mpmath import * +from mpmath.libmp import * + +def test_trig_misc_hard(): + mp.prec = 53 + # Worst-case input for an IEEE double, from a paper by Kahan + x = ldexp(6381956970095103,797) + assert cos(x) == mpf('-4.6871659242546277e-19') + assert sin(x) == 1 + + mp.prec = 150 + a = mpf(10**50) + mp.prec = 53 + assert sin(a).ae(-0.7896724934293100827) + assert cos(a).ae(-0.6135286082336635622) + + # Check relative accuracy close to x = zero + assert sin(1e-100) == 1e-100 # when rounding to nearest + assert sin(1e-6).ae(9.999999999998333e-007, rel_eps=2e-15, abs_eps=0) + assert sin(1e-6j).ae(1.0000000000001666e-006j, rel_eps=2e-15, abs_eps=0) + assert sin(-1e-6j).ae(-1.0000000000001666e-006j, rel_eps=2e-15, abs_eps=0) + assert cos(1e-100) == 1 + assert cos(1e-6).ae(0.9999999999995) + assert cos(-1e-6j).ae(1.0000000000005) + assert tan(1e-100) == 1e-100 + assert tan(1e-6).ae(1.0000000000003335e-006, rel_eps=2e-15, abs_eps=0) + assert tan(1e-6j).ae(9.9999999999966644e-007j, rel_eps=2e-15, abs_eps=0) + assert tan(-1e-6j).ae(-9.9999999999966644e-007j, rel_eps=2e-15, abs_eps=0) + +def test_trig_near_zero(): + mp.dps = 15 + + for r in [round_nearest, round_down, round_up, round_floor, round_ceiling]: + assert sin(0, rounding=r) == 0 + assert cos(0, rounding=r) == 1 + + a = mpf('1e-100') + b = mpf('-1e-100') + + assert sin(a, rounding=round_nearest) == a + assert sin(a, rounding=round_down) < a + assert sin(a, rounding=round_floor) < a + assert sin(a, rounding=round_up) >= a + assert sin(a, rounding=round_ceiling) >= a + assert sin(b, rounding=round_nearest) == b + assert sin(b, rounding=round_down) > b + assert sin(b, rounding=round_floor) <= b + assert sin(b, rounding=round_up) <= b + assert sin(b, rounding=round_ceiling) > b + + assert cos(a, rounding=round_nearest) == 1 + assert cos(a, rounding=round_down) < 1 + assert cos(a, rounding=round_floor) < 1 + assert cos(a, rounding=round_up) == 1 + assert cos(a, rounding=round_ceiling) == 1 + assert cos(b, rounding=round_nearest) == 1 + assert cos(b, rounding=round_down) < 1 + assert cos(b, rounding=round_floor) < 1 + assert cos(b, rounding=round_up) == 1 + assert cos(b, rounding=round_ceiling) == 1 + + +def test_trig_near_n_pi(): + + mp.dps = 15 + a = [n*pi for n in [1, 2, 6, 11, 100, 1001, 10000, 100001]] + mp.dps = 135 + a.append(10**100 * pi) + mp.dps = 15 + + assert sin(a[0]) == mpf('1.2246467991473531772e-16') + assert sin(a[1]) == mpf('-2.4492935982947063545e-16') + assert sin(a[2]) == mpf('-7.3478807948841190634e-16') + assert sin(a[3]) == mpf('4.8998251578625894243e-15') + assert sin(a[4]) == mpf('1.9643867237284719452e-15') + assert sin(a[5]) == mpf('-8.8632615209684813458e-15') + assert sin(a[6]) == mpf('-4.8568235395684898392e-13') + assert sin(a[7]) == mpf('3.9087342299491231029e-11') + assert sin(a[8]) == mpf('-1.369235466754566993528e-36') + + r = round_nearest + assert cos(a[0], rounding=r) == -1 + assert cos(a[1], rounding=r) == 1 + assert cos(a[2], rounding=r) == 1 + assert cos(a[3], rounding=r) == -1 + assert cos(a[4], rounding=r) == 1 + assert cos(a[5], rounding=r) == -1 + assert cos(a[6], rounding=r) == 1 + assert cos(a[7], rounding=r) == -1 + assert cos(a[8], rounding=r) == 1 + + r = round_up + assert cos(a[0], rounding=r) == -1 + assert cos(a[1], rounding=r) == 1 + assert cos(a[2], rounding=r) == 1 + assert cos(a[3], rounding=r) == -1 + assert cos(a[4], rounding=r) == 1 + assert cos(a[5], rounding=r) == -1 + assert cos(a[6], rounding=r) == 1 + assert cos(a[7], rounding=r) == -1 + assert cos(a[8], rounding=r) == 1 + + r = round_down + assert cos(a[0], rounding=r) > -1 + assert cos(a[1], rounding=r) < 1 + assert cos(a[2], rounding=r) < 1 + assert cos(a[3], rounding=r) > -1 + assert cos(a[4], rounding=r) < 1 + assert cos(a[5], rounding=r) > -1 + assert cos(a[6], rounding=r) < 1 + assert cos(a[7], rounding=r) > -1 + assert cos(a[8], rounding=r) < 1 + + r = round_floor + assert cos(a[0], rounding=r) == -1 + assert cos(a[1], rounding=r) < 1 + assert cos(a[2], rounding=r) < 1 + assert cos(a[3], rounding=r) == -1 + assert cos(a[4], rounding=r) < 1 + assert cos(a[5], rounding=r) == -1 + assert cos(a[6], rounding=r) < 1 + assert cos(a[7], rounding=r) == -1 + assert cos(a[8], rounding=r) < 1 + + r = round_ceiling + assert cos(a[0], rounding=r) > -1 + assert cos(a[1], rounding=r) == 1 + assert cos(a[2], rounding=r) == 1 + assert cos(a[3], rounding=r) > -1 + assert cos(a[4], rounding=r) == 1 + assert cos(a[5], rounding=r) > -1 + assert cos(a[6], rounding=r) == 1 + assert cos(a[7], rounding=r) > -1 + assert cos(a[8], rounding=r) == 1 + + mp.dps = 15 diff --git a/lib/python3.12/site-packages/mpmath/tests/test_visualization.py b/lib/python3.12/site-packages/mpmath/tests/test_visualization.py new file mode 100644 index 0000000000000000000000000000000000000000..81ffd05194322f00e4c75dc02bc862b383468bff --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/tests/test_visualization.py @@ -0,0 +1,32 @@ +""" +Limited tests of the visualization module. Right now it just makes +sure that passing custom Axes works. + +""" + +from mpmath import mp, fp + +def test_axes(): + try: + import matplotlib + version = matplotlib.__version__.split("-")[0] + version = version.split(".")[:2] + if [int(_) for _ in version] < [0,99]: + raise ImportError + import pylab + except ImportError: + print("\nSkipping test (pylab not available or too old version)\n") + return + fig = pylab.figure() + axes = fig.add_subplot(111) + for ctx in [mp, fp]: + ctx.plot(lambda x: x**2, [0, 3], axes=axes) + assert axes.get_xlabel() == 'x' + assert axes.get_ylabel() == 'f(x)' + + fig = pylab.figure() + axes = fig.add_subplot(111) + for ctx in [mp, fp]: + ctx.cplot(lambda z: z, [-2, 2], [-10, 10], axes=axes) + assert axes.get_xlabel() == 'Re(z)' + assert axes.get_ylabel() == 'Im(z)' diff --git a/lib/python3.12/site-packages/mpmath/tests/torture.py b/lib/python3.12/site-packages/mpmath/tests/torture.py new file mode 100644 index 0000000000000000000000000000000000000000..845d5c6d7d017e51e1ed9a8fe3106cfa32fd967f --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/tests/torture.py @@ -0,0 +1,224 @@ +""" +Torture tests for asymptotics and high precision evaluation of +special functions. + +(Other torture tests may also be placed here.) + +Running this file (gmpy recommended!) takes several CPU minutes. +With Python 2.6+, multiprocessing is used automatically to run tests +in parallel if many cores are available. (A single test may take between +a second and several minutes; possibly more.) + +The idea: + +* We evaluate functions at positive, negative, imaginary, 45- and 135-degree + complex values with magnitudes between 10^-20 to 10^20, at precisions between + 5 and 150 digits (we can go even higher for fast functions). + +* Comparing the result from two different precision levels provides + a strong consistency check (particularly for functions that use + different algorithms at different precision levels). + +* That the computation finishes at all (without failure), within reasonable + time, provides a check that evaluation works at all: that the code runs, + that it doesn't get stuck in an infinite loop, and that it doesn't use + some extremely slowly algorithm where it could use a faster one. + +TODO: + +* Speed up those functions that take long to finish! +* Generalize to test more cases; more options. +* Implement a timeout mechanism. +* Some functions are notably absent, including the following: + * inverse trigonometric functions (some become inaccurate for complex arguments) + * ci, si (not implemented properly for large complex arguments) + * zeta functions (need to modify test not to try too large imaginary values) + * and others... + +""" + + +import sys, os +from timeit import default_timer as clock + +if "-nogmpy" in sys.argv: + sys.argv.remove('-nogmpy') + os.environ['MPMATH_NOGMPY'] = 'Y' + +filt = '' +if not sys.argv[-1].endswith(".py"): + filt = sys.argv[-1] + +from mpmath import * +from mpmath.libmp.backend import exec_ + +def test_asymp(f, maxdps=150, verbose=False, huge_range=False): + dps = [5,15,25,50,90,150,500,1500,5000,10000] + dps = [p for p in dps if p <= maxdps] + def check(x,y,p,inpt): + if abs(x-y)/abs(y) < workprec(20)(power)(10, -p+1): + return + print() + print("Error!") + print("Input:", inpt) + print("dps =", p) + print("Result 1:", x) + print("Result 2:", y) + print("Absolute error:", abs(x-y)) + print("Relative error:", abs(x-y)/abs(y)) + raise AssertionError + exponents = range(-20,20) + if huge_range: + exponents += [-1000, -100, -50, 50, 100, 1000] + for n in exponents: + if verbose: + sys.stdout.write(". ") + mp.dps = 25 + xpos = mpf(10)**n / 1.1287 + xneg = -xpos + ximag = xpos*j + xcomplex1 = xpos*(1+j) + xcomplex2 = xpos*(-1+j) + for i in range(len(dps)): + if verbose: + print("Testing dps = %s" % dps[i]) + mp.dps = dps[i] + new = f(xpos), f(xneg), f(ximag), f(xcomplex1), f(xcomplex2) + if i != 0: + p = dps[i-1] + check(prev[0], new[0], p, xpos) + check(prev[1], new[1], p, xneg) + check(prev[2], new[2], p, ximag) + check(prev[3], new[3], p, xcomplex1) + check(prev[4], new[4], p, xcomplex2) + prev = new + if verbose: + print() + +a1, a2, a3, a4, a5 = 1.5, -2.25, 3.125, 4, 2 + +def test_bernoulli_huge(): + p, q = bernfrac(9000) + assert p % 10**10 == 9636701091 + assert q == 4091851784687571609141381951327092757255270 + mp.dps = 15 + assert str(bernoulli(10**100)) == '-2.58183325604736e+987675256497386331227838638980680030172857347883537824464410652557820800494271520411283004120790908623' + mp.dps = 50 + assert str(bernoulli(10**100)) == '-2.5818332560473632073252488656039475548106223822913e+987675256497386331227838638980680030172857347883537824464410652557820800494271520411283004120790908623' + mp.dps = 15 + +cases = """\ +test_bernoulli_huge() +test_asymp(lambda z: +pi, maxdps=10000) +test_asymp(lambda z: +e, maxdps=10000) +test_asymp(lambda z: +ln2, maxdps=10000) +test_asymp(lambda z: +ln10, maxdps=10000) +test_asymp(lambda z: +phi, maxdps=10000) +test_asymp(lambda z: +catalan, maxdps=5000) +test_asymp(lambda z: +euler, maxdps=5000) +test_asymp(lambda z: +glaisher, maxdps=1000) +test_asymp(lambda z: +khinchin, maxdps=1000) +test_asymp(lambda z: +twinprime, maxdps=150) +test_asymp(lambda z: stieltjes(2), maxdps=150) +test_asymp(lambda z: +mertens, maxdps=150) +test_asymp(lambda z: +apery, maxdps=5000) +test_asymp(sqrt, maxdps=10000, huge_range=True) +test_asymp(cbrt, maxdps=5000, huge_range=True) +test_asymp(lambda z: root(z,4), maxdps=5000, huge_range=True) +test_asymp(lambda z: root(z,-5), maxdps=5000, huge_range=True) +test_asymp(exp, maxdps=5000, huge_range=True) +test_asymp(expm1, maxdps=1500) +test_asymp(ln, maxdps=5000, huge_range=True) +test_asymp(cosh, maxdps=5000) +test_asymp(sinh, maxdps=5000) +test_asymp(tanh, maxdps=1500) +test_asymp(sin, maxdps=5000, huge_range=True) +test_asymp(cos, maxdps=5000, huge_range=True) +test_asymp(tan, maxdps=1500) +test_asymp(agm, maxdps=1500, huge_range=True) +test_asymp(ellipk, maxdps=1500) +test_asymp(ellipe, maxdps=1500) +test_asymp(lambertw, huge_range=True) +test_asymp(lambda z: lambertw(z,-1)) +test_asymp(lambda z: lambertw(z,1)) +test_asymp(lambda z: lambertw(z,4)) +test_asymp(gamma) +test_asymp(loggamma) # huge_range=True ? +test_asymp(ei) +test_asymp(e1) +test_asymp(li, huge_range=True) +test_asymp(ci) +test_asymp(si) +test_asymp(chi) +test_asymp(shi) +test_asymp(erf) +test_asymp(erfc) +test_asymp(erfi) +test_asymp(lambda z: besselj(2, z)) +test_asymp(lambda z: bessely(2, z)) +test_asymp(lambda z: besseli(2, z)) +test_asymp(lambda z: besselk(2, z)) +test_asymp(lambda z: besselj(-2.25, z)) +test_asymp(lambda z: bessely(-2.25, z)) +test_asymp(lambda z: besseli(-2.25, z)) +test_asymp(lambda z: besselk(-2.25, z)) +test_asymp(airyai) +test_asymp(airybi) +test_asymp(lambda z: hyp0f1(a1, z)) +test_asymp(lambda z: hyp1f1(a1, a2, z)) +test_asymp(lambda z: hyp1f2(a1, a2, a3, z)) +test_asymp(lambda z: hyp2f0(a1, a2, z)) +test_asymp(lambda z: hyperu(a1, a2, z)) +test_asymp(lambda z: hyp2f1(a1, a2, a3, z)) +test_asymp(lambda z: hyp2f2(a1, a2, a3, a4, z)) +test_asymp(lambda z: hyp2f3(a1, a2, a3, a4, a5, z)) +test_asymp(lambda z: coulombf(a1, a2, z)) +test_asymp(lambda z: coulombg(a1, a2, z)) +test_asymp(lambda z: polylog(2,z)) +test_asymp(lambda z: polylog(3,z)) +test_asymp(lambda z: polylog(-2,z)) +test_asymp(lambda z: expint(4, z)) +test_asymp(lambda z: expint(-4, z)) +test_asymp(lambda z: expint(2.25, z)) +test_asymp(lambda z: gammainc(2.5, z, 5)) +test_asymp(lambda z: gammainc(2.5, 5, z)) +test_asymp(lambda z: hermite(3, z)) +test_asymp(lambda z: hermite(2.5, z)) +test_asymp(lambda z: legendre(3, z)) +test_asymp(lambda z: legendre(4, z)) +test_asymp(lambda z: legendre(2.5, z)) +test_asymp(lambda z: legenp(a1, a2, z)) +test_asymp(lambda z: legenq(a1, a2, z), maxdps=90) # abnormally slow +test_asymp(lambda z: jtheta(1, z, 0.5)) +test_asymp(lambda z: jtheta(2, z, 0.5)) +test_asymp(lambda z: jtheta(3, z, 0.5)) +test_asymp(lambda z: jtheta(4, z, 0.5)) +test_asymp(lambda z: jtheta(1, z, 0.5, 1)) +test_asymp(lambda z: jtheta(2, z, 0.5, 1)) +test_asymp(lambda z: jtheta(3, z, 0.5, 1)) +test_asymp(lambda z: jtheta(4, z, 0.5, 1)) +test_asymp(barnesg, maxdps=90) +""" + +def testit(line): + if filt in line: + print(line) + t1 = clock() + exec_(line, globals(), locals()) + t2 = clock() + elapsed = t2-t1 + print("Time:", elapsed, "for", line, "(OK)") + +if __name__ == '__main__': + try: + from multiprocessing import Pool + mapf = Pool(None).map + print("Running tests with multiprocessing") + except ImportError: + print("Not using multiprocessing") + mapf = map + t1 = clock() + tasks = cases.splitlines() + mapf(testit, tasks) + t2 = clock() + print("Cumulative wall time:", t2-t1) diff --git a/lib/python3.12/site-packages/mpmath/usertools.py b/lib/python3.12/site-packages/mpmath/usertools.py new file mode 100644 index 0000000000000000000000000000000000000000..8028a4c46f1c635a6857f1f2de48ac6675d3c6d3 --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/usertools.py @@ -0,0 +1,93 @@ + +def monitor(f, input='print', output='print'): + """ + Returns a wrapped copy of *f* that monitors evaluation by calling + *input* with every input (*args*, *kwargs*) passed to *f* and + *output* with every value returned from *f*. The default action + (specify using the special string value ``'print'``) is to print + inputs and outputs to stdout, along with the total evaluation + count:: + + >>> from mpmath import * + >>> mp.dps = 5; mp.pretty = False + >>> diff(monitor(exp), 1) # diff will eval f(x-h) and f(x+h) + in 0 (mpf('0.99999999906867742538452148'),) {} + out 0 mpf('2.7182818259274480055282064') + in 1 (mpf('1.0000000009313225746154785'),) {} + out 1 mpf('2.7182818309906424675501024') + mpf('2.7182808') + + To disable either the input or the output handler, you may + pass *None* as argument. + + Custom input and output handlers may be used e.g. to store + results for later analysis:: + + >>> mp.dps = 15 + >>> input = [] + >>> output = [] + >>> findroot(monitor(sin, input.append, output.append), 3.0) + mpf('3.1415926535897932') + >>> len(input) # Count number of evaluations + 9 + >>> print(input[3]); print(output[3]) + ((mpf('3.1415076583334066'),), {}) + 8.49952562843408e-5 + >>> print(input[4]); print(output[4]) + ((mpf('3.1415928201669122'),), {}) + -1.66577118985331e-7 + + """ + if not input: + input = lambda v: None + elif input == 'print': + incount = [0] + def input(value): + args, kwargs = value + print("in %s %r %r" % (incount[0], args, kwargs)) + incount[0] += 1 + if not output: + output = lambda v: None + elif output == 'print': + outcount = [0] + def output(value): + print("out %s %r" % (outcount[0], value)) + outcount[0] += 1 + def f_monitored(*args, **kwargs): + input((args, kwargs)) + v = f(*args, **kwargs) + output(v) + return v + return f_monitored + +def timing(f, *args, **kwargs): + """ + Returns time elapsed for evaluating ``f()``. Optionally arguments + may be passed to time the execution of ``f(*args, **kwargs)``. + + If the first call is very quick, ``f`` is called + repeatedly and the best time is returned. + """ + once = kwargs.get('once') + if 'once' in kwargs: + del kwargs['once'] + if args or kwargs: + if len(args) == 1 and not kwargs: + arg = args[0] + g = lambda: f(arg) + else: + g = lambda: f(*args, **kwargs) + else: + g = f + from timeit import default_timer as clock + t1=clock(); v=g(); t2=clock(); t=t2-t1 + if t > 0.05 or once: + return t + for i in range(3): + t1=clock(); + # Evaluate multiple times because the timer function + # has a significant overhead + g();g();g();g();g();g();g();g();g();g() + t2=clock() + t=min(t,(t2-t1)/10) + return t diff --git a/lib/python3.12/site-packages/mpmath/visualization.py b/lib/python3.12/site-packages/mpmath/visualization.py new file mode 100644 index 0000000000000000000000000000000000000000..17e12e97bead4f2977b59361a4de7672f0e9b75f --- /dev/null +++ b/lib/python3.12/site-packages/mpmath/visualization.py @@ -0,0 +1,313 @@ +""" +Plotting (requires matplotlib) +""" + +from colorsys import hsv_to_rgb, hls_to_rgb +from .libmp import NoConvergence +from .libmp.backend import xrange + +class VisualizationMethods(object): + plot_ignore = (ValueError, ArithmeticError, ZeroDivisionError, NoConvergence) + +def plot(ctx, f, xlim=[-5,5], ylim=None, points=200, file=None, dpi=None, + singularities=[], axes=None): + r""" + Shows a simple 2D plot of a function `f(x)` or list of functions + `[f_0(x), f_1(x), \ldots, f_n(x)]` over a given interval + specified by *xlim*. Some examples:: + + plot(lambda x: exp(x)*li(x), [1, 4]) + plot([cos, sin], [-4, 4]) + plot([fresnels, fresnelc], [-4, 4]) + plot([sqrt, cbrt], [-4, 4]) + plot(lambda t: zeta(0.5+t*j), [-20, 20]) + plot([floor, ceil, abs, sign], [-5, 5]) + + Points where the function raises a numerical exception or + returns an infinite value are removed from the graph. + Singularities can also be excluded explicitly + as follows (useful for removing erroneous vertical lines):: + + plot(cot, ylim=[-5, 5]) # bad + plot(cot, ylim=[-5, 5], singularities=[-pi, 0, pi]) # good + + For parts where the function assumes complex values, the + real part is plotted with dashes and the imaginary part + is plotted with dots. + + .. note :: This function requires matplotlib (pylab). + """ + if file: + axes = None + fig = None + if not axes: + import pylab + fig = pylab.figure() + axes = fig.add_subplot(111) + if not isinstance(f, (tuple, list)): + f = [f] + a, b = xlim + colors = ['b', 'r', 'g', 'm', 'k'] + for n, func in enumerate(f): + x = ctx.arange(a, b, (b-a)/float(points)) + segments = [] + segment = [] + in_complex = False + for i in xrange(len(x)): + try: + if i != 0: + for sing in singularities: + if x[i-1] <= sing and x[i] >= sing: + raise ValueError + v = func(x[i]) + if ctx.isnan(v) or abs(v) > 1e300: + raise ValueError + if hasattr(v, "imag") and v.imag: + re = float(v.real) + im = float(v.imag) + if not in_complex: + in_complex = True + segments.append(segment) + segment = [] + segment.append((float(x[i]), re, im)) + else: + if in_complex: + in_complex = False + segments.append(segment) + segment = [] + if hasattr(v, "real"): + v = v.real + segment.append((float(x[i]), v)) + except ctx.plot_ignore: + if segment: + segments.append(segment) + segment = [] + if segment: + segments.append(segment) + for segment in segments: + x = [s[0] for s in segment] + y = [s[1] for s in segment] + if not x: + continue + c = colors[n % len(colors)] + if len(segment[0]) == 3: + z = [s[2] for s in segment] + axes.plot(x, y, '--'+c, linewidth=3) + axes.plot(x, z, ':'+c, linewidth=3) + else: + axes.plot(x, y, c, linewidth=3) + axes.set_xlim([float(_) for _ in xlim]) + if ylim: + axes.set_ylim([float(_) for _ in ylim]) + axes.set_xlabel('x') + axes.set_ylabel('f(x)') + axes.grid(True) + if fig: + if file: + pylab.savefig(file, dpi=dpi) + else: + pylab.show() + +def default_color_function(ctx, z): + if ctx.isinf(z): + return (1.0, 1.0, 1.0) + if ctx.isnan(z): + return (0.5, 0.5, 0.5) + pi = 3.1415926535898 + a = (float(ctx.arg(z)) + ctx.pi) / (2*ctx.pi) + a = (a + 0.5) % 1.0 + b = 1.0 - float(1/(1.0+abs(z)**0.3)) + return hls_to_rgb(a, b, 0.8) + +blue_orange_colors = [ + (-1.0, (0.0, 0.0, 0.0)), + (-0.95, (0.1, 0.2, 0.5)), # dark blue + (-0.5, (0.0, 0.5, 1.0)), # blueish + (-0.05, (0.4, 0.8, 0.8)), # cyanish + ( 0.0, (1.0, 1.0, 1.0)), + ( 0.05, (1.0, 0.9, 0.3)), # yellowish + ( 0.5, (0.9, 0.5, 0.0)), # orangeish + ( 0.95, (0.7, 0.1, 0.0)), # redish + ( 1.0, (0.0, 0.0, 0.0)), + ( 2.0, (0.0, 0.0, 0.0)), +] + +def phase_color_function(ctx, z): + if ctx.isinf(z): + return (1.0, 1.0, 1.0) + if ctx.isnan(z): + return (0.5, 0.5, 0.5) + pi = 3.1415926535898 + w = float(ctx.arg(z)) / pi + w = max(min(w, 1.0), -1.0) + for i in range(1,len(blue_orange_colors)): + if blue_orange_colors[i][0] > w: + a, (ra, ga, ba) = blue_orange_colors[i-1] + b, (rb, gb, bb) = blue_orange_colors[i] + s = (w-a) / (b-a) + return ra+(rb-ra)*s, ga+(gb-ga)*s, ba+(bb-ba)*s + +def cplot(ctx, f, re=[-5,5], im=[-5,5], points=2000, color=None, + verbose=False, file=None, dpi=None, axes=None): + """ + Plots the given complex-valued function *f* over a rectangular part + of the complex plane specified by the pairs of intervals *re* and *im*. + For example:: + + cplot(lambda z: z, [-2, 2], [-10, 10]) + cplot(exp) + cplot(zeta, [0, 1], [0, 50]) + + By default, the complex argument (phase) is shown as color (hue) and + the magnitude is show as brightness. You can also supply a + custom color function (*color*). This function should take a + complex number as input and return an RGB 3-tuple containing + floats in the range 0.0-1.0. + + Alternatively, you can select a builtin color function by passing + a string as *color*: + + * "default" - default color scheme + * "phase" - a color scheme that only renders the phase of the function, + with white for positive reals, black for negative reals, gold in the + upper half plane, and blue in the lower half plane. + + To obtain a sharp image, the number of points may need to be + increased to 100,000 or thereabout. Since evaluating the + function that many times is likely to be slow, the 'verbose' + option is useful to display progress. + + .. note :: This function requires matplotlib (pylab). + """ + if color is None or color == "default": + color = ctx.default_color_function + if color == "phase": + color = ctx.phase_color_function + import pylab + if file: + axes = None + fig = None + if not axes: + fig = pylab.figure() + axes = fig.add_subplot(111) + rea, reb = re + ima, imb = im + dre = reb - rea + dim = imb - ima + M = int(ctx.sqrt(points*dre/dim)+1) + N = int(ctx.sqrt(points*dim/dre)+1) + x = pylab.linspace(rea, reb, M) + y = pylab.linspace(ima, imb, N) + # Note: we have to be careful to get the right rotation. + # Test with these plots: + # cplot(lambda z: z if z.real < 0 else 0) + # cplot(lambda z: z if z.imag < 0 else 0) + w = pylab.zeros((N, M, 3)) + for n in xrange(N): + for m in xrange(M): + z = ctx.mpc(x[m], y[n]) + try: + v = color(f(z)) + except ctx.plot_ignore: + v = (0.5, 0.5, 0.5) + w[n,m] = v + if verbose: + print(str(n) + ' of ' + str(N)) + rea, reb, ima, imb = [float(_) for _ in [rea, reb, ima, imb]] + axes.imshow(w, extent=(rea, reb, ima, imb), origin='lower') + axes.set_xlabel('Re(z)') + axes.set_ylabel('Im(z)') + if fig: + if file: + pylab.savefig(file, dpi=dpi) + else: + pylab.show() + +def splot(ctx, f, u=[-5,5], v=[-5,5], points=100, keep_aspect=True, \ + wireframe=False, file=None, dpi=None, axes=None): + """ + Plots the surface defined by `f`. + + If `f` returns a single component, then this plots the surface + defined by `z = f(x,y)` over the rectangular domain with + `x = u` and `y = v`. + + If `f` returns three components, then this plots the parametric + surface `x, y, z = f(u,v)` over the pairs of intervals `u` and `v`. + + For example, to plot a simple function:: + + >>> from mpmath import * + >>> f = lambda x, y: sin(x+y)*cos(y) + >>> splot(f, [-pi,pi], [-pi,pi]) # doctest: +SKIP + + Plotting a donut:: + + >>> r, R = 1, 2.5 + >>> f = lambda u, v: [r*cos(u), (R+r*sin(u))*cos(v), (R+r*sin(u))*sin(v)] + >>> splot(f, [0, 2*pi], [0, 2*pi]) # doctest: +SKIP + + .. note :: This function requires matplotlib (pylab) 0.98.5.3 or higher. + """ + import pylab + import mpl_toolkits.mplot3d as mplot3d + if file: + axes = None + fig = None + if not axes: + fig = pylab.figure() + axes = mplot3d.axes3d.Axes3D(fig) + ua, ub = u + va, vb = v + du = ub - ua + dv = vb - va + if not isinstance(points, (list, tuple)): + points = [points, points] + M, N = points + u = pylab.linspace(ua, ub, M) + v = pylab.linspace(va, vb, N) + x, y, z = [pylab.zeros((M, N)) for i in xrange(3)] + xab, yab, zab = [[0, 0] for i in xrange(3)] + for n in xrange(N): + for m in xrange(M): + fdata = f(ctx.convert(u[m]), ctx.convert(v[n])) + try: + x[m,n], y[m,n], z[m,n] = fdata + except TypeError: + x[m,n], y[m,n], z[m,n] = u[m], v[n], fdata + for c, cab in [(x[m,n], xab), (y[m,n], yab), (z[m,n], zab)]: + if c < cab[0]: + cab[0] = c + if c > cab[1]: + cab[1] = c + if wireframe: + axes.plot_wireframe(x, y, z, rstride=4, cstride=4) + else: + axes.plot_surface(x, y, z, rstride=4, cstride=4) + axes.set_xlabel('x') + axes.set_ylabel('y') + axes.set_zlabel('z') + if keep_aspect: + dx, dy, dz = [cab[1] - cab[0] for cab in [xab, yab, zab]] + maxd = max(dx, dy, dz) + if dx < maxd: + delta = maxd - dx + axes.set_xlim3d(xab[0] - delta / 2.0, xab[1] + delta / 2.0) + if dy < maxd: + delta = maxd - dy + axes.set_ylim3d(yab[0] - delta / 2.0, yab[1] + delta / 2.0) + if dz < maxd: + delta = maxd - dz + axes.set_zlim3d(zab[0] - delta / 2.0, zab[1] + delta / 2.0) + if fig: + if file: + pylab.savefig(file, dpi=dpi) + else: + pylab.show() + + +VisualizationMethods.plot = plot +VisualizationMethods.default_color_function = default_color_function +VisualizationMethods.phase_color_function = phase_color_function +VisualizationMethods.cplot = cplot +VisualizationMethods.splot = splot diff --git a/lib/python3.12/site-packages/msgpack/__init__.py b/lib/python3.12/site-packages/msgpack/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..f3266b701fb408b481a686845bb4d0d5335c51be --- /dev/null +++ b/lib/python3.12/site-packages/msgpack/__init__.py @@ -0,0 +1,55 @@ +# ruff: noqa: F401 +import os + +from .exceptions import * # noqa: F403 +from .ext import ExtType, Timestamp + +version = (1, 1, 2) +__version__ = "1.1.2" + + +if os.environ.get("MSGPACK_PUREPYTHON"): + from .fallback import Packer, Unpacker, unpackb +else: + try: + from ._cmsgpack import Packer, Unpacker, unpackb + except ImportError: + from .fallback import Packer, Unpacker, unpackb + + +def pack(o, stream, **kwargs): + """ + Pack object `o` and write it to `stream` + + See :class:`Packer` for options. + """ + packer = Packer(**kwargs) + stream.write(packer.pack(o)) + + +def packb(o, **kwargs): + """ + Pack object `o` and return packed bytes + + See :class:`Packer` for options. + """ + return Packer(**kwargs).pack(o) + + +def unpack(stream, **kwargs): + """ + Unpack an object from `stream`. + + Raises `ExtraData` when `stream` contains extra bytes. + See :class:`Unpacker` for options. + """ + data = stream.read() + return unpackb(data, **kwargs) + + +# alias for compatibility to simplejson/marshal/pickle. +load = unpack +loads = unpackb + +dump = pack +dumps = packb diff --git a/lib/python3.12/site-packages/msgpack/__pycache__/__init__.cpython-312.pyc b/lib/python3.12/site-packages/msgpack/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fc6fcc4730bd197e4278668120be9a23578ae624 Binary files /dev/null and b/lib/python3.12/site-packages/msgpack/__pycache__/__init__.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/msgpack/__pycache__/exceptions.cpython-312.pyc b/lib/python3.12/site-packages/msgpack/__pycache__/exceptions.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..da95ae78a51d5cd9a0f8caf4c92ff4dabbd62973 Binary files /dev/null and b/lib/python3.12/site-packages/msgpack/__pycache__/exceptions.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/msgpack/__pycache__/ext.cpython-312.pyc b/lib/python3.12/site-packages/msgpack/__pycache__/ext.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..26a90b129ca3149ebb8e867353961b23630bacf3 Binary files /dev/null and b/lib/python3.12/site-packages/msgpack/__pycache__/ext.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/msgpack/__pycache__/fallback.cpython-312.pyc b/lib/python3.12/site-packages/msgpack/__pycache__/fallback.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1ff21430fe1614da91cb4123392e64885f1b2f13 Binary files /dev/null and b/lib/python3.12/site-packages/msgpack/__pycache__/fallback.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/msgpack/exceptions.py b/lib/python3.12/site-packages/msgpack/exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..d6d2615cfdd0b914d064cdf7eecd45761e4bcaf6 --- /dev/null +++ b/lib/python3.12/site-packages/msgpack/exceptions.py @@ -0,0 +1,48 @@ +class UnpackException(Exception): + """Base class for some exceptions raised while unpacking. + + NOTE: unpack may raise exception other than subclass of + UnpackException. If you want to catch all error, catch + Exception instead. + """ + + +class BufferFull(UnpackException): + pass + + +class OutOfData(UnpackException): + pass + + +class FormatError(ValueError, UnpackException): + """Invalid msgpack format""" + + +class StackError(ValueError, UnpackException): + """Too nested""" + + +# Deprecated. Use ValueError instead +UnpackValueError = ValueError + + +class ExtraData(UnpackValueError): + """ExtraData is raised when there is trailing data. + + This exception is raised while only one-shot (not streaming) + unpack. + """ + + def __init__(self, unpacked, extra): + self.unpacked = unpacked + self.extra = extra + + def __str__(self): + return "unpack(b) received extra data." + + +# Deprecated. Use Exception instead to catch all exception during packing. +PackException = Exception +PackValueError = ValueError +PackOverflowError = OverflowError diff --git a/lib/python3.12/site-packages/msgpack/ext.py b/lib/python3.12/site-packages/msgpack/ext.py new file mode 100644 index 0000000000000000000000000000000000000000..9694819a7df1570ccbb41de065cb7052f9af8e79 --- /dev/null +++ b/lib/python3.12/site-packages/msgpack/ext.py @@ -0,0 +1,170 @@ +import datetime +import struct +from collections import namedtuple + + +class ExtType(namedtuple("ExtType", "code data")): + """ExtType represents ext type in msgpack.""" + + def __new__(cls, code, data): + if not isinstance(code, int): + raise TypeError("code must be int") + if not isinstance(data, bytes): + raise TypeError("data must be bytes") + if not 0 <= code <= 127: + raise ValueError("code must be 0~127") + return super().__new__(cls, code, data) + + +class Timestamp: + """Timestamp represents the Timestamp extension type in msgpack. + + When built with Cython, msgpack uses C methods to pack and unpack `Timestamp`. + When using pure-Python msgpack, :func:`to_bytes` and :func:`from_bytes` are used to pack and + unpack `Timestamp`. + + This class is immutable: Do not override seconds and nanoseconds. + """ + + __slots__ = ["seconds", "nanoseconds"] + + def __init__(self, seconds, nanoseconds=0): + """Initialize a Timestamp object. + + :param int seconds: + Number of seconds since the UNIX epoch (00:00:00 UTC Jan 1 1970, minus leap seconds). + May be negative. + + :param int nanoseconds: + Number of nanoseconds to add to `seconds` to get fractional time. + Maximum is 999_999_999. Default is 0. + + Note: Negative times (before the UNIX epoch) are represented as neg. seconds + pos. ns. + """ + if not isinstance(seconds, int): + raise TypeError("seconds must be an integer") + if not isinstance(nanoseconds, int): + raise TypeError("nanoseconds must be an integer") + if not (0 <= nanoseconds < 10**9): + raise ValueError("nanoseconds must be a non-negative integer less than 999999999.") + self.seconds = seconds + self.nanoseconds = nanoseconds + + def __repr__(self): + """String representation of Timestamp.""" + return f"Timestamp(seconds={self.seconds}, nanoseconds={self.nanoseconds})" + + def __eq__(self, other): + """Check for equality with another Timestamp object""" + if type(other) is self.__class__: + return self.seconds == other.seconds and self.nanoseconds == other.nanoseconds + return False + + def __ne__(self, other): + """not-equals method (see :func:`__eq__()`)""" + return not self.__eq__(other) + + def __hash__(self): + return hash((self.seconds, self.nanoseconds)) + + @staticmethod + def from_bytes(b): + """Unpack bytes into a `Timestamp` object. + + Used for pure-Python msgpack unpacking. + + :param b: Payload from msgpack ext message with code -1 + :type b: bytes + + :returns: Timestamp object unpacked from msgpack ext payload + :rtype: Timestamp + """ + if len(b) == 4: + seconds = struct.unpack("!L", b)[0] + nanoseconds = 0 + elif len(b) == 8: + data64 = struct.unpack("!Q", b)[0] + seconds = data64 & 0x00000003FFFFFFFF + nanoseconds = data64 >> 34 + elif len(b) == 12: + nanoseconds, seconds = struct.unpack("!Iq", b) + else: + raise ValueError( + "Timestamp type can only be created from 32, 64, or 96-bit byte objects" + ) + return Timestamp(seconds, nanoseconds) + + def to_bytes(self): + """Pack this Timestamp object into bytes. + + Used for pure-Python msgpack packing. + + :returns data: Payload for EXT message with code -1 (timestamp type) + :rtype: bytes + """ + if (self.seconds >> 34) == 0: # seconds is non-negative and fits in 34 bits + data64 = self.nanoseconds << 34 | self.seconds + if data64 & 0xFFFFFFFF00000000 == 0: + # nanoseconds is zero and seconds < 2**32, so timestamp 32 + data = struct.pack("!L", data64) + else: + # timestamp 64 + data = struct.pack("!Q", data64) + else: + # timestamp 96 + data = struct.pack("!Iq", self.nanoseconds, self.seconds) + return data + + @staticmethod + def from_unix(unix_sec): + """Create a Timestamp from posix timestamp in seconds. + + :param unix_float: Posix timestamp in seconds. + :type unix_float: int or float + """ + seconds = int(unix_sec // 1) + nanoseconds = int((unix_sec % 1) * 10**9) + return Timestamp(seconds, nanoseconds) + + def to_unix(self): + """Get the timestamp as a floating-point value. + + :returns: posix timestamp + :rtype: float + """ + return self.seconds + self.nanoseconds / 1e9 + + @staticmethod + def from_unix_nano(unix_ns): + """Create a Timestamp from posix timestamp in nanoseconds. + + :param int unix_ns: Posix timestamp in nanoseconds. + :rtype: Timestamp + """ + return Timestamp(*divmod(unix_ns, 10**9)) + + def to_unix_nano(self): + """Get the timestamp as a unixtime in nanoseconds. + + :returns: posix timestamp in nanoseconds + :rtype: int + """ + return self.seconds * 10**9 + self.nanoseconds + + def to_datetime(self): + """Get the timestamp as a UTC datetime. + + :rtype: `datetime.datetime` + """ + utc = datetime.timezone.utc + return datetime.datetime.fromtimestamp(0, utc) + datetime.timedelta( + seconds=self.seconds, microseconds=self.nanoseconds // 1000 + ) + + @staticmethod + def from_datetime(dt): + """Create a Timestamp from datetime with tzinfo. + + :rtype: Timestamp + """ + return Timestamp(seconds=int(dt.timestamp()), nanoseconds=dt.microsecond * 1000) diff --git a/lib/python3.12/site-packages/msgpack/fallback.py b/lib/python3.12/site-packages/msgpack/fallback.py new file mode 100644 index 0000000000000000000000000000000000000000..b02e47cfb91a54a7205881ee87a5db2ea8d049a7 --- /dev/null +++ b/lib/python3.12/site-packages/msgpack/fallback.py @@ -0,0 +1,929 @@ +"""Fallback pure Python implementation of msgpack""" + +import struct +import sys +from datetime import datetime as _DateTime + +if hasattr(sys, "pypy_version_info"): + from __pypy__ import newlist_hint + from __pypy__.builders import BytesBuilder + + _USING_STRINGBUILDER = True + + class BytesIO: + def __init__(self, s=b""): + if s: + self.builder = BytesBuilder(len(s)) + self.builder.append(s) + else: + self.builder = BytesBuilder() + + def write(self, s): + if isinstance(s, memoryview): + s = s.tobytes() + elif isinstance(s, bytearray): + s = bytes(s) + self.builder.append(s) + + def getvalue(self): + return self.builder.build() + +else: + from io import BytesIO + + _USING_STRINGBUILDER = False + + def newlist_hint(size): + return [] + + +from .exceptions import BufferFull, ExtraData, FormatError, OutOfData, StackError +from .ext import ExtType, Timestamp + +EX_SKIP = 0 +EX_CONSTRUCT = 1 +EX_READ_ARRAY_HEADER = 2 +EX_READ_MAP_HEADER = 3 + +TYPE_IMMEDIATE = 0 +TYPE_ARRAY = 1 +TYPE_MAP = 2 +TYPE_RAW = 3 +TYPE_BIN = 4 +TYPE_EXT = 5 + +DEFAULT_RECURSE_LIMIT = 511 + + +def _check_type_strict(obj, t, type=type, tuple=tuple): + if type(t) is tuple: + return type(obj) in t + else: + return type(obj) is t + + +def _get_data_from_buffer(obj): + view = memoryview(obj) + if view.itemsize != 1: + raise ValueError("cannot unpack from multi-byte object") + return view + + +def unpackb(packed, **kwargs): + """ + Unpack an object from `packed`. + + Raises ``ExtraData`` when *packed* contains extra bytes. + Raises ``ValueError`` when *packed* is incomplete. + Raises ``FormatError`` when *packed* is not valid msgpack. + Raises ``StackError`` when *packed* contains too nested. + Other exceptions can be raised during unpacking. + + See :class:`Unpacker` for options. + """ + unpacker = Unpacker(None, max_buffer_size=len(packed), **kwargs) + unpacker.feed(packed) + try: + ret = unpacker._unpack() + except OutOfData: + raise ValueError("Unpack failed: incomplete input") + except RecursionError: + raise StackError + if unpacker._got_extradata(): + raise ExtraData(ret, unpacker._get_extradata()) + return ret + + +_NO_FORMAT_USED = "" +_MSGPACK_HEADERS = { + 0xC4: (1, _NO_FORMAT_USED, TYPE_BIN), + 0xC5: (2, ">H", TYPE_BIN), + 0xC6: (4, ">I", TYPE_BIN), + 0xC7: (2, "Bb", TYPE_EXT), + 0xC8: (3, ">Hb", TYPE_EXT), + 0xC9: (5, ">Ib", TYPE_EXT), + 0xCA: (4, ">f"), + 0xCB: (8, ">d"), + 0xCC: (1, _NO_FORMAT_USED), + 0xCD: (2, ">H"), + 0xCE: (4, ">I"), + 0xCF: (8, ">Q"), + 0xD0: (1, "b"), + 0xD1: (2, ">h"), + 0xD2: (4, ">i"), + 0xD3: (8, ">q"), + 0xD4: (1, "b1s", TYPE_EXT), + 0xD5: (2, "b2s", TYPE_EXT), + 0xD6: (4, "b4s", TYPE_EXT), + 0xD7: (8, "b8s", TYPE_EXT), + 0xD8: (16, "b16s", TYPE_EXT), + 0xD9: (1, _NO_FORMAT_USED, TYPE_RAW), + 0xDA: (2, ">H", TYPE_RAW), + 0xDB: (4, ">I", TYPE_RAW), + 0xDC: (2, ">H", TYPE_ARRAY), + 0xDD: (4, ">I", TYPE_ARRAY), + 0xDE: (2, ">H", TYPE_MAP), + 0xDF: (4, ">I", TYPE_MAP), +} + + +class Unpacker: + """Streaming unpacker. + + Arguments: + + :param file_like: + File-like object having `.read(n)` method. + If specified, unpacker reads serialized data from it and `.feed()` is not usable. + + :param int read_size: + Used as `file_like.read(read_size)`. (default: `min(16*1024, max_buffer_size)`) + + :param bool use_list: + If true, unpack msgpack array to Python list. + Otherwise, unpack to Python tuple. (default: True) + + :param bool raw: + If true, unpack msgpack raw to Python bytes. + Otherwise, unpack to Python str by decoding with UTF-8 encoding (default). + + :param int timestamp: + Control how timestamp type is unpacked: + + 0 - Timestamp + 1 - float (Seconds from the EPOCH) + 2 - int (Nanoseconds from the EPOCH) + 3 - datetime.datetime (UTC). + + :param bool strict_map_key: + If true (default), only str or bytes are accepted for map (dict) keys. + + :param object_hook: + When specified, it should be callable. + Unpacker calls it with a dict argument after unpacking msgpack map. + (See also simplejson) + + :param object_pairs_hook: + When specified, it should be callable. + Unpacker calls it with a list of key-value pairs after unpacking msgpack map. + (See also simplejson) + + :param str unicode_errors: + The error handler for decoding unicode. (default: 'strict') + This option should be used only when you have msgpack data which + contains invalid UTF-8 string. + + :param int max_buffer_size: + Limits size of data waiting unpacked. 0 means 2**32-1. + The default value is 100*1024*1024 (100MiB). + Raises `BufferFull` exception when it is insufficient. + You should set this parameter when unpacking data from untrusted source. + + :param int max_str_len: + Deprecated, use *max_buffer_size* instead. + Limits max length of str. (default: max_buffer_size) + + :param int max_bin_len: + Deprecated, use *max_buffer_size* instead. + Limits max length of bin. (default: max_buffer_size) + + :param int max_array_len: + Limits max length of array. + (default: max_buffer_size) + + :param int max_map_len: + Limits max length of map. + (default: max_buffer_size//2) + + :param int max_ext_len: + Deprecated, use *max_buffer_size* instead. + Limits max size of ext type. (default: max_buffer_size) + + Example of streaming deserialize from file-like object:: + + unpacker = Unpacker(file_like) + for o in unpacker: + process(o) + + Example of streaming deserialize from socket:: + + unpacker = Unpacker() + while True: + buf = sock.recv(1024**2) + if not buf: + break + unpacker.feed(buf) + for o in unpacker: + process(o) + + Raises ``ExtraData`` when *packed* contains extra bytes. + Raises ``OutOfData`` when *packed* is incomplete. + Raises ``FormatError`` when *packed* is not valid msgpack. + Raises ``StackError`` when *packed* contains too nested. + Other exceptions can be raised during unpacking. + """ + + def __init__( + self, + file_like=None, + *, + read_size=0, + use_list=True, + raw=False, + timestamp=0, + strict_map_key=True, + object_hook=None, + object_pairs_hook=None, + list_hook=None, + unicode_errors=None, + max_buffer_size=100 * 1024 * 1024, + ext_hook=ExtType, + max_str_len=-1, + max_bin_len=-1, + max_array_len=-1, + max_map_len=-1, + max_ext_len=-1, + ): + if unicode_errors is None: + unicode_errors = "strict" + + if file_like is None: + self._feeding = True + else: + if not callable(file_like.read): + raise TypeError("`file_like.read` must be callable") + self.file_like = file_like + self._feeding = False + + #: array of bytes fed. + self._buffer = bytearray() + #: Which position we currently reads + self._buff_i = 0 + + # When Unpacker is used as an iterable, between the calls to next(), + # the buffer is not "consumed" completely, for efficiency sake. + # Instead, it is done sloppily. To make sure we raise BufferFull at + # the correct moments, we have to keep track of how sloppy we were. + # Furthermore, when the buffer is incomplete (that is: in the case + # we raise an OutOfData) we need to rollback the buffer to the correct + # state, which _buf_checkpoint records. + self._buf_checkpoint = 0 + + if not max_buffer_size: + max_buffer_size = 2**31 - 1 + if max_str_len == -1: + max_str_len = max_buffer_size + if max_bin_len == -1: + max_bin_len = max_buffer_size + if max_array_len == -1: + max_array_len = max_buffer_size + if max_map_len == -1: + max_map_len = max_buffer_size // 2 + if max_ext_len == -1: + max_ext_len = max_buffer_size + + self._max_buffer_size = max_buffer_size + if read_size > self._max_buffer_size: + raise ValueError("read_size must be smaller than max_buffer_size") + self._read_size = read_size or min(self._max_buffer_size, 16 * 1024) + self._raw = bool(raw) + self._strict_map_key = bool(strict_map_key) + self._unicode_errors = unicode_errors + self._use_list = use_list + if not (0 <= timestamp <= 3): + raise ValueError("timestamp must be 0..3") + self._timestamp = timestamp + self._list_hook = list_hook + self._object_hook = object_hook + self._object_pairs_hook = object_pairs_hook + self._ext_hook = ext_hook + self._max_str_len = max_str_len + self._max_bin_len = max_bin_len + self._max_array_len = max_array_len + self._max_map_len = max_map_len + self._max_ext_len = max_ext_len + self._stream_offset = 0 + + if list_hook is not None and not callable(list_hook): + raise TypeError("`list_hook` is not callable") + if object_hook is not None and not callable(object_hook): + raise TypeError("`object_hook` is not callable") + if object_pairs_hook is not None and not callable(object_pairs_hook): + raise TypeError("`object_pairs_hook` is not callable") + if object_hook is not None and object_pairs_hook is not None: + raise TypeError("object_pairs_hook and object_hook are mutually exclusive") + if not callable(ext_hook): + raise TypeError("`ext_hook` is not callable") + + def feed(self, next_bytes): + assert self._feeding + view = _get_data_from_buffer(next_bytes) + if len(self._buffer) - self._buff_i + len(view) > self._max_buffer_size: + raise BufferFull + + # Strip buffer before checkpoint before reading file. + if self._buf_checkpoint > 0: + del self._buffer[: self._buf_checkpoint] + self._buff_i -= self._buf_checkpoint + self._buf_checkpoint = 0 + + # Use extend here: INPLACE_ADD += doesn't reliably typecast memoryview in jython + self._buffer.extend(view) + view.release() + + def _consume(self): + """Gets rid of the used parts of the buffer.""" + self._stream_offset += self._buff_i - self._buf_checkpoint + self._buf_checkpoint = self._buff_i + + def _got_extradata(self): + return self._buff_i < len(self._buffer) + + def _get_extradata(self): + return self._buffer[self._buff_i :] + + def read_bytes(self, n): + ret = self._read(n, raise_outofdata=False) + self._consume() + return ret + + def _read(self, n, raise_outofdata=True): + # (int) -> bytearray + self._reserve(n, raise_outofdata=raise_outofdata) + i = self._buff_i + ret = self._buffer[i : i + n] + self._buff_i = i + len(ret) + return ret + + def _reserve(self, n, raise_outofdata=True): + remain_bytes = len(self._buffer) - self._buff_i - n + + # Fast path: buffer has n bytes already + if remain_bytes >= 0: + return + + if self._feeding: + self._buff_i = self._buf_checkpoint + raise OutOfData + + # Strip buffer before checkpoint before reading file. + if self._buf_checkpoint > 0: + del self._buffer[: self._buf_checkpoint] + self._buff_i -= self._buf_checkpoint + self._buf_checkpoint = 0 + + # Read from file + remain_bytes = -remain_bytes + if remain_bytes + len(self._buffer) > self._max_buffer_size: + raise BufferFull + while remain_bytes > 0: + to_read_bytes = max(self._read_size, remain_bytes) + read_data = self.file_like.read(to_read_bytes) + if not read_data: + break + assert isinstance(read_data, bytes) + self._buffer += read_data + remain_bytes -= len(read_data) + + if len(self._buffer) < n + self._buff_i and raise_outofdata: + self._buff_i = 0 # rollback + raise OutOfData + + def _read_header(self): + typ = TYPE_IMMEDIATE + n = 0 + obj = None + self._reserve(1) + b = self._buffer[self._buff_i] + self._buff_i += 1 + if b & 0b10000000 == 0: + obj = b + elif b & 0b11100000 == 0b11100000: + obj = -1 - (b ^ 0xFF) + elif b & 0b11100000 == 0b10100000: + n = b & 0b00011111 + typ = TYPE_RAW + if n > self._max_str_len: + raise ValueError(f"{n} exceeds max_str_len({self._max_str_len})") + obj = self._read(n) + elif b & 0b11110000 == 0b10010000: + n = b & 0b00001111 + typ = TYPE_ARRAY + if n > self._max_array_len: + raise ValueError(f"{n} exceeds max_array_len({self._max_array_len})") + elif b & 0b11110000 == 0b10000000: + n = b & 0b00001111 + typ = TYPE_MAP + if n > self._max_map_len: + raise ValueError(f"{n} exceeds max_map_len({self._max_map_len})") + elif b == 0xC0: + obj = None + elif b == 0xC2: + obj = False + elif b == 0xC3: + obj = True + elif 0xC4 <= b <= 0xC6: + size, fmt, typ = _MSGPACK_HEADERS[b] + self._reserve(size) + if len(fmt) > 0: + n = struct.unpack_from(fmt, self._buffer, self._buff_i)[0] + else: + n = self._buffer[self._buff_i] + self._buff_i += size + if n > self._max_bin_len: + raise ValueError(f"{n} exceeds max_bin_len({self._max_bin_len})") + obj = self._read(n) + elif 0xC7 <= b <= 0xC9: + size, fmt, typ = _MSGPACK_HEADERS[b] + self._reserve(size) + L, n = struct.unpack_from(fmt, self._buffer, self._buff_i) + self._buff_i += size + if L > self._max_ext_len: + raise ValueError(f"{L} exceeds max_ext_len({self._max_ext_len})") + obj = self._read(L) + elif 0xCA <= b <= 0xD3: + size, fmt = _MSGPACK_HEADERS[b] + self._reserve(size) + if len(fmt) > 0: + obj = struct.unpack_from(fmt, self._buffer, self._buff_i)[0] + else: + obj = self._buffer[self._buff_i] + self._buff_i += size + elif 0xD4 <= b <= 0xD8: + size, fmt, typ = _MSGPACK_HEADERS[b] + if self._max_ext_len < size: + raise ValueError(f"{size} exceeds max_ext_len({self._max_ext_len})") + self._reserve(size + 1) + n, obj = struct.unpack_from(fmt, self._buffer, self._buff_i) + self._buff_i += size + 1 + elif 0xD9 <= b <= 0xDB: + size, fmt, typ = _MSGPACK_HEADERS[b] + self._reserve(size) + if len(fmt) > 0: + (n,) = struct.unpack_from(fmt, self._buffer, self._buff_i) + else: + n = self._buffer[self._buff_i] + self._buff_i += size + if n > self._max_str_len: + raise ValueError(f"{n} exceeds max_str_len({self._max_str_len})") + obj = self._read(n) + elif 0xDC <= b <= 0xDD: + size, fmt, typ = _MSGPACK_HEADERS[b] + self._reserve(size) + (n,) = struct.unpack_from(fmt, self._buffer, self._buff_i) + self._buff_i += size + if n > self._max_array_len: + raise ValueError(f"{n} exceeds max_array_len({self._max_array_len})") + elif 0xDE <= b <= 0xDF: + size, fmt, typ = _MSGPACK_HEADERS[b] + self._reserve(size) + (n,) = struct.unpack_from(fmt, self._buffer, self._buff_i) + self._buff_i += size + if n > self._max_map_len: + raise ValueError(f"{n} exceeds max_map_len({self._max_map_len})") + else: + raise FormatError("Unknown header: 0x%x" % b) + return typ, n, obj + + def _unpack(self, execute=EX_CONSTRUCT): + typ, n, obj = self._read_header() + + if execute == EX_READ_ARRAY_HEADER: + if typ != TYPE_ARRAY: + raise ValueError("Expected array") + return n + if execute == EX_READ_MAP_HEADER: + if typ != TYPE_MAP: + raise ValueError("Expected map") + return n + # TODO should we eliminate the recursion? + if typ == TYPE_ARRAY: + if execute == EX_SKIP: + for i in range(n): + # TODO check whether we need to call `list_hook` + self._unpack(EX_SKIP) + return + ret = newlist_hint(n) + for i in range(n): + ret.append(self._unpack(EX_CONSTRUCT)) + if self._list_hook is not None: + ret = self._list_hook(ret) + # TODO is the interaction between `list_hook` and `use_list` ok? + return ret if self._use_list else tuple(ret) + if typ == TYPE_MAP: + if execute == EX_SKIP: + for i in range(n): + # TODO check whether we need to call hooks + self._unpack(EX_SKIP) + self._unpack(EX_SKIP) + return + if self._object_pairs_hook is not None: + ret = self._object_pairs_hook( + (self._unpack(EX_CONSTRUCT), self._unpack(EX_CONSTRUCT)) for _ in range(n) + ) + else: + ret = {} + for _ in range(n): + key = self._unpack(EX_CONSTRUCT) + if self._strict_map_key and type(key) not in (str, bytes): + raise ValueError("%s is not allowed for map key" % str(type(key))) + if isinstance(key, str): + key = sys.intern(key) + ret[key] = self._unpack(EX_CONSTRUCT) + if self._object_hook is not None: + ret = self._object_hook(ret) + return ret + if execute == EX_SKIP: + return + if typ == TYPE_RAW: + if self._raw: + obj = bytes(obj) + else: + obj = obj.decode("utf_8", self._unicode_errors) + return obj + if typ == TYPE_BIN: + return bytes(obj) + if typ == TYPE_EXT: + if n == -1: # timestamp + ts = Timestamp.from_bytes(bytes(obj)) + if self._timestamp == 1: + return ts.to_unix() + elif self._timestamp == 2: + return ts.to_unix_nano() + elif self._timestamp == 3: + return ts.to_datetime() + else: + return ts + else: + return self._ext_hook(n, bytes(obj)) + assert typ == TYPE_IMMEDIATE + return obj + + def __iter__(self): + return self + + def __next__(self): + try: + ret = self._unpack(EX_CONSTRUCT) + self._consume() + return ret + except OutOfData: + self._consume() + raise StopIteration + except RecursionError: + raise StackError + + next = __next__ + + def skip(self): + self._unpack(EX_SKIP) + self._consume() + + def unpack(self): + try: + ret = self._unpack(EX_CONSTRUCT) + except RecursionError: + raise StackError + self._consume() + return ret + + def read_array_header(self): + ret = self._unpack(EX_READ_ARRAY_HEADER) + self._consume() + return ret + + def read_map_header(self): + ret = self._unpack(EX_READ_MAP_HEADER) + self._consume() + return ret + + def tell(self): + return self._stream_offset + + +class Packer: + """ + MessagePack Packer + + Usage:: + + packer = Packer() + astream.write(packer.pack(a)) + astream.write(packer.pack(b)) + + Packer's constructor has some keyword arguments: + + :param default: + When specified, it should be callable. + Convert user type to builtin type that Packer supports. + See also simplejson's document. + + :param bool use_single_float: + Use single precision float type for float. (default: False) + + :param bool autoreset: + Reset buffer after each pack and return its content as `bytes`. (default: True). + If set this to false, use `bytes()` to get content and `.reset()` to clear buffer. + + :param bool use_bin_type: + Use bin type introduced in msgpack spec 2.0 for bytes. + It also enables str8 type for unicode. (default: True) + + :param bool strict_types: + If set to true, types will be checked to be exact. Derived classes + from serializable types will not be serialized and will be + treated as unsupported type and forwarded to default. + Additionally tuples will not be serialized as lists. + This is useful when trying to implement accurate serialization + for python types. + + :param bool datetime: + If set to true, datetime with tzinfo is packed into Timestamp type. + Note that the tzinfo is stripped in the timestamp. + You can get UTC datetime with `timestamp=3` option of the Unpacker. + + :param str unicode_errors: + The error handler for encoding unicode. (default: 'strict') + DO NOT USE THIS!! This option is kept for very specific usage. + + :param int buf_size: + Internal buffer size. This option is used only for C implementation. + """ + + def __init__( + self, + *, + default=None, + use_single_float=False, + autoreset=True, + use_bin_type=True, + strict_types=False, + datetime=False, + unicode_errors=None, + buf_size=None, + ): + self._strict_types = strict_types + self._use_float = use_single_float + self._autoreset = autoreset + self._use_bin_type = use_bin_type + self._buffer = BytesIO() + self._datetime = bool(datetime) + self._unicode_errors = unicode_errors or "strict" + if default is not None and not callable(default): + raise TypeError("default must be callable") + self._default = default + + def _pack( + self, + obj, + nest_limit=DEFAULT_RECURSE_LIMIT, + check=isinstance, + check_type_strict=_check_type_strict, + ): + default_used = False + if self._strict_types: + check = check_type_strict + list_types = list + else: + list_types = (list, tuple) + while True: + if nest_limit < 0: + raise ValueError("recursion limit exceeded") + if obj is None: + return self._buffer.write(b"\xc0") + if check(obj, bool): + if obj: + return self._buffer.write(b"\xc3") + return self._buffer.write(b"\xc2") + if check(obj, int): + if 0 <= obj < 0x80: + return self._buffer.write(struct.pack("B", obj)) + if -0x20 <= obj < 0: + return self._buffer.write(struct.pack("b", obj)) + if 0x80 <= obj <= 0xFF: + return self._buffer.write(struct.pack("BB", 0xCC, obj)) + if -0x80 <= obj < 0: + return self._buffer.write(struct.pack(">Bb", 0xD0, obj)) + if 0xFF < obj <= 0xFFFF: + return self._buffer.write(struct.pack(">BH", 0xCD, obj)) + if -0x8000 <= obj < -0x80: + return self._buffer.write(struct.pack(">Bh", 0xD1, obj)) + if 0xFFFF < obj <= 0xFFFFFFFF: + return self._buffer.write(struct.pack(">BI", 0xCE, obj)) + if -0x80000000 <= obj < -0x8000: + return self._buffer.write(struct.pack(">Bi", 0xD2, obj)) + if 0xFFFFFFFF < obj <= 0xFFFFFFFFFFFFFFFF: + return self._buffer.write(struct.pack(">BQ", 0xCF, obj)) + if -0x8000000000000000 <= obj < -0x80000000: + return self._buffer.write(struct.pack(">Bq", 0xD3, obj)) + if not default_used and self._default is not None: + obj = self._default(obj) + default_used = True + continue + raise OverflowError("Integer value out of range") + if check(obj, (bytes, bytearray)): + n = len(obj) + if n >= 2**32: + raise ValueError("%s is too large" % type(obj).__name__) + self._pack_bin_header(n) + return self._buffer.write(obj) + if check(obj, str): + obj = obj.encode("utf-8", self._unicode_errors) + n = len(obj) + if n >= 2**32: + raise ValueError("String is too large") + self._pack_raw_header(n) + return self._buffer.write(obj) + if check(obj, memoryview): + n = obj.nbytes + if n >= 2**32: + raise ValueError("Memoryview is too large") + self._pack_bin_header(n) + return self._buffer.write(obj) + if check(obj, float): + if self._use_float: + return self._buffer.write(struct.pack(">Bf", 0xCA, obj)) + return self._buffer.write(struct.pack(">Bd", 0xCB, obj)) + if check(obj, (ExtType, Timestamp)): + if check(obj, Timestamp): + code = -1 + data = obj.to_bytes() + else: + code = obj.code + data = obj.data + assert isinstance(code, int) + assert isinstance(data, bytes) + L = len(data) + if L == 1: + self._buffer.write(b"\xd4") + elif L == 2: + self._buffer.write(b"\xd5") + elif L == 4: + self._buffer.write(b"\xd6") + elif L == 8: + self._buffer.write(b"\xd7") + elif L == 16: + self._buffer.write(b"\xd8") + elif L <= 0xFF: + self._buffer.write(struct.pack(">BB", 0xC7, L)) + elif L <= 0xFFFF: + self._buffer.write(struct.pack(">BH", 0xC8, L)) + else: + self._buffer.write(struct.pack(">BI", 0xC9, L)) + self._buffer.write(struct.pack("b", code)) + self._buffer.write(data) + return + if check(obj, list_types): + n = len(obj) + self._pack_array_header(n) + for i in range(n): + self._pack(obj[i], nest_limit - 1) + return + if check(obj, dict): + return self._pack_map_pairs(len(obj), obj.items(), nest_limit - 1) + + if self._datetime and check(obj, _DateTime) and obj.tzinfo is not None: + obj = Timestamp.from_datetime(obj) + default_used = 1 + continue + + if not default_used and self._default is not None: + obj = self._default(obj) + default_used = 1 + continue + + if self._datetime and check(obj, _DateTime): + raise ValueError(f"Cannot serialize {obj!r} where tzinfo=None") + + raise TypeError(f"Cannot serialize {obj!r}") + + def pack(self, obj): + try: + self._pack(obj) + except: + self._buffer = BytesIO() # force reset + raise + if self._autoreset: + ret = self._buffer.getvalue() + self._buffer = BytesIO() + return ret + + def pack_map_pairs(self, pairs): + self._pack_map_pairs(len(pairs), pairs) + if self._autoreset: + ret = self._buffer.getvalue() + self._buffer = BytesIO() + return ret + + def pack_array_header(self, n): + if n >= 2**32: + raise ValueError + self._pack_array_header(n) + if self._autoreset: + ret = self._buffer.getvalue() + self._buffer = BytesIO() + return ret + + def pack_map_header(self, n): + if n >= 2**32: + raise ValueError + self._pack_map_header(n) + if self._autoreset: + ret = self._buffer.getvalue() + self._buffer = BytesIO() + return ret + + def pack_ext_type(self, typecode, data): + if not isinstance(typecode, int): + raise TypeError("typecode must have int type.") + if not 0 <= typecode <= 127: + raise ValueError("typecode should be 0-127") + if not isinstance(data, bytes): + raise TypeError("data must have bytes type") + L = len(data) + if L > 0xFFFFFFFF: + raise ValueError("Too large data") + if L == 1: + self._buffer.write(b"\xd4") + elif L == 2: + self._buffer.write(b"\xd5") + elif L == 4: + self._buffer.write(b"\xd6") + elif L == 8: + self._buffer.write(b"\xd7") + elif L == 16: + self._buffer.write(b"\xd8") + elif L <= 0xFF: + self._buffer.write(b"\xc7" + struct.pack("B", L)) + elif L <= 0xFFFF: + self._buffer.write(b"\xc8" + struct.pack(">H", L)) + else: + self._buffer.write(b"\xc9" + struct.pack(">I", L)) + self._buffer.write(struct.pack("B", typecode)) + self._buffer.write(data) + + def _pack_array_header(self, n): + if n <= 0x0F: + return self._buffer.write(struct.pack("B", 0x90 + n)) + if n <= 0xFFFF: + return self._buffer.write(struct.pack(">BH", 0xDC, n)) + if n <= 0xFFFFFFFF: + return self._buffer.write(struct.pack(">BI", 0xDD, n)) + raise ValueError("Array is too large") + + def _pack_map_header(self, n): + if n <= 0x0F: + return self._buffer.write(struct.pack("B", 0x80 + n)) + if n <= 0xFFFF: + return self._buffer.write(struct.pack(">BH", 0xDE, n)) + if n <= 0xFFFFFFFF: + return self._buffer.write(struct.pack(">BI", 0xDF, n)) + raise ValueError("Dict is too large") + + def _pack_map_pairs(self, n, pairs, nest_limit=DEFAULT_RECURSE_LIMIT): + self._pack_map_header(n) + for k, v in pairs: + self._pack(k, nest_limit - 1) + self._pack(v, nest_limit - 1) + + def _pack_raw_header(self, n): + if n <= 0x1F: + self._buffer.write(struct.pack("B", 0xA0 + n)) + elif self._use_bin_type and n <= 0xFF: + self._buffer.write(struct.pack(">BB", 0xD9, n)) + elif n <= 0xFFFF: + self._buffer.write(struct.pack(">BH", 0xDA, n)) + elif n <= 0xFFFFFFFF: + self._buffer.write(struct.pack(">BI", 0xDB, n)) + else: + raise ValueError("Raw is too large") + + def _pack_bin_header(self, n): + if not self._use_bin_type: + return self._pack_raw_header(n) + elif n <= 0xFF: + return self._buffer.write(struct.pack(">BB", 0xC4, n)) + elif n <= 0xFFFF: + return self._buffer.write(struct.pack(">BH", 0xC5, n)) + elif n <= 0xFFFFFFFF: + return self._buffer.write(struct.pack(">BI", 0xC6, n)) + else: + raise ValueError("Bin is too large") + + def bytes(self): + """Return internal buffer contents as bytes object""" + return self._buffer.getvalue() + + def reset(self): + """Reset internal buffer. + + This method is useful only when autoreset=False. + """ + self._buffer = BytesIO() + + def getbuffer(self): + """Return view of internal buffer.""" + if _USING_STRINGBUILDER: + return memoryview(self.bytes()) + else: + return self._buffer.getbuffer() diff --git a/lib/python3.12/site-packages/networkx-3.6.1.dist-info/INSTALLER b/lib/python3.12/site-packages/networkx-3.6.1.dist-info/INSTALLER new file mode 100644 index 0000000000000000000000000000000000000000..a1b589e38a32041e49332e5e81c2d363dc418d68 --- /dev/null +++ b/lib/python3.12/site-packages/networkx-3.6.1.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/lib/python3.12/site-packages/networkx-3.6.1.dist-info/METADATA b/lib/python3.12/site-packages/networkx-3.6.1.dist-info/METADATA new file mode 100644 index 0000000000000000000000000000000000000000..98acd814861037e65d72b4ababd2e0e8b86555ab --- /dev/null +++ b/lib/python3.12/site-packages/networkx-3.6.1.dist-info/METADATA @@ -0,0 +1,177 @@ +Metadata-Version: 2.4 +Name: networkx +Version: 3.6.1 +Summary: Python package for creating and manipulating graphs and networks +Author-email: Aric Hagberg +Maintainer-email: NetworkX Developers +License-Expression: BSD-3-Clause +Project-URL: Homepage, https://networkx.org/ +Project-URL: Bug Tracker, https://github.com/networkx/networkx/issues +Project-URL: Documentation, https://networkx.org/documentation/stable/ +Project-URL: Source Code, https://github.com/networkx/networkx +Keywords: Networks,Graph Theory,Mathematics,network,graph,discrete mathematics,math +Platform: Linux +Platform: Mac OSX +Platform: Windows +Platform: Unix +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: Intended Audience :: Science/Research +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Classifier: Programming Language :: Python :: 3.14 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Topic :: Scientific/Engineering :: Bio-Informatics +Classifier: Topic :: Scientific/Engineering :: Information Analysis +Classifier: Topic :: Scientific/Engineering :: Mathematics +Classifier: Topic :: Scientific/Engineering :: Physics +Requires-Python: !=3.14.1,>=3.11 +Description-Content-Type: text/x-rst +License-File: LICENSE.txt +Provides-Extra: benchmarking +Requires-Dist: asv; extra == "benchmarking" +Requires-Dist: virtualenv; extra == "benchmarking" +Provides-Extra: default +Requires-Dist: numpy>=1.25; extra == "default" +Requires-Dist: scipy>=1.11.2; extra == "default" +Requires-Dist: matplotlib>=3.8; extra == "default" +Requires-Dist: pandas>=2.0; extra == "default" +Provides-Extra: developer +Requires-Dist: pre-commit>=4.1; extra == "developer" +Requires-Dist: mypy>=1.15; extra == "developer" +Provides-Extra: doc +Requires-Dist: sphinx>=8.0; extra == "doc" +Requires-Dist: pydata-sphinx-theme>=0.16; extra == "doc" +Requires-Dist: sphinx-gallery>=0.18; extra == "doc" +Requires-Dist: numpydoc>=1.8.0; extra == "doc" +Requires-Dist: pillow>=10; extra == "doc" +Requires-Dist: texext>=0.6.7; extra == "doc" +Requires-Dist: myst-nb>=1.1; extra == "doc" +Requires-Dist: intersphinx-registry; extra == "doc" +Provides-Extra: example +Requires-Dist: osmnx>=2.0.0; extra == "example" +Requires-Dist: momepy>=0.7.2; extra == "example" +Requires-Dist: contextily>=1.6; extra == "example" +Requires-Dist: seaborn>=0.13; extra == "example" +Requires-Dist: cairocffi>=1.7; extra == "example" +Requires-Dist: igraph>=0.11; extra == "example" +Requires-Dist: scikit-learn>=1.5; extra == "example" +Requires-Dist: iplotx>=0.9.0; extra == "example" +Provides-Extra: extra +Requires-Dist: lxml>=4.6; extra == "extra" +Requires-Dist: pygraphviz>=1.14; extra == "extra" +Requires-Dist: pydot>=3.0.1; extra == "extra" +Requires-Dist: sympy>=1.10; extra == "extra" +Provides-Extra: release +Requires-Dist: build>=0.10; extra == "release" +Requires-Dist: twine>=4.0; extra == "release" +Requires-Dist: wheel>=0.40; extra == "release" +Requires-Dist: changelist==0.5; extra == "release" +Provides-Extra: test +Requires-Dist: pytest>=7.2; extra == "test" +Requires-Dist: pytest-cov>=4.0; extra == "test" +Requires-Dist: pytest-xdist>=3.0; extra == "test" +Provides-Extra: test-extras +Requires-Dist: pytest-mpl; extra == "test-extras" +Requires-Dist: pytest-randomly; extra == "test-extras" +Dynamic: license-file + +NetworkX +======== + + +.. image:: + https://github.com/networkx/networkx/actions/workflows/test.yml/badge.svg?branch=main + :target: https://github.com/networkx/networkx/actions/workflows/test.yml + +.. image:: + https://img.shields.io/pypi/v/networkx.svg? + :target: https://pypi.python.org/pypi/networkx + +.. image:: + https://img.shields.io/pypi/l/networkx.svg? + :target: https://github.com/networkx/networkx/blob/main/LICENSE.txt + +.. image:: + https://img.shields.io/pypi/pyversions/networkx.svg? + :target: https://pypi.python.org/pypi/networkx + +.. image:: + https://img.shields.io/github/labels/networkx/networkx/good%20first%20issue?color=green&label=contribute + :target: https://github.com/networkx/networkx/contribute + +.. image:: + https://insights.linuxfoundation.org/api/badge/health-score?project=networkx + :target: https://insights.linuxfoundation.org/project/networkx + + +NetworkX is a Python package for the creation, manipulation, +and study of the structure, dynamics, and functions +of complex networks. + +- **Website (including documentation):** https://networkx.org +- **Mailing list:** https://groups.google.com/forum/#!forum/networkx-discuss +- **Source:** https://github.com/networkx/networkx +- **Bug reports:** https://github.com/networkx/networkx/issues +- **Report a security vulnerability:** https://tidelift.com/security +- **Tutorial:** https://networkx.org/documentation/latest/tutorial.html +- **GitHub Discussions:** https://github.com/networkx/networkx/discussions +- **Discord (Scientific Python) invite link:** https://discord.com/invite/vur45CbwMz +- **NetworkX meetings calendar (open to all):** https://scientific-python.org/calendars/networkx.ics + +Simple example +-------------- + +Find the shortest path between two nodes in an undirected graph: + +.. code:: pycon + + >>> import networkx as nx + >>> G = nx.Graph() + >>> G.add_edge("A", "B", weight=4) + >>> G.add_edge("B", "D", weight=2) + >>> G.add_edge("A", "C", weight=3) + >>> G.add_edge("C", "D", weight=4) + >>> nx.shortest_path(G, "A", "D", weight="weight") + ['A', 'B', 'D'] + +Install +------- + +Install the latest released version of NetworkX: + +.. code:: shell + + $ pip install networkx + +Install with all optional dependencies: + +.. code:: shell + + $ pip install networkx[default] + +For additional details, +please see the `installation guide `_. + +Bugs +---- + +Please report any bugs that you find `here `_. +Or, even better, fork the repository on `GitHub `_ +and create a pull request (PR). We welcome all changes, big or small, and we +will help you make the PR if you are new to `git` (just ask on the issue and/or +see the `contributor guide `_). + +License +------- + +Released under the `3-clause BSD license `_:: + + Copyright (c) 2004-2025, NetworkX Developers + Aric Hagberg + Dan Schult + Pieter Swart diff --git a/lib/python3.12/site-packages/networkx-3.6.1.dist-info/RECORD b/lib/python3.12/site-packages/networkx-3.6.1.dist-info/RECORD new file mode 100644 index 0000000000000000000000000000000000000000..cbe1c8435488b1d09df8ee39457814745f52fc2e --- /dev/null +++ b/lib/python3.12/site-packages/networkx-3.6.1.dist-info/RECORD @@ -0,0 +1,1182 @@ +networkx-3.6.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +networkx-3.6.1.dist-info/METADATA,sha256=rKXZSpfR9w8wHQM63bY19uZr6Jc6AKukEnY37tLvMWo,6783 +networkx-3.6.1.dist-info/RECORD,, +networkx-3.6.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91 +networkx-3.6.1.dist-info/entry_points.txt,sha256=H2jZaDsDJ_i9H2SwWpwuFel8BrZ9xHKuvh-DQAWW9lQ,94 +networkx-3.6.1.dist-info/licenses/LICENSE.txt,sha256=PPfDoXnYF7I7i4VoIRdp_v35N5fSB9mEuLL6JNAiCzM,1763 +networkx-3.6.1.dist-info/top_level.txt,sha256=s3Mk-7KOlu-kD39w8Xg_KXoP5Z_MVvgB-upkyuOE4Hk,9 +networkx/__init__.py,sha256=OVN8C-x44LgeJWKcb77v-WwfMV7EYqieafkoQdlAl5E,1625 +networkx/__pycache__/__init__.cpython-312.pyc,, +networkx/__pycache__/conftest.cpython-312.pyc,, +networkx/__pycache__/convert.cpython-312.pyc,, +networkx/__pycache__/convert_matrix.cpython-312.pyc,, +networkx/__pycache__/exception.cpython-312.pyc,, +networkx/__pycache__/lazy_imports.cpython-312.pyc,, +networkx/__pycache__/relabel.cpython-312.pyc,, +networkx/algorithms/__init__.py,sha256=sXdgBEDG0qlsOAymWl9zyT-c_luwOQOhWuus6quro0A,6607 +networkx/algorithms/__pycache__/__init__.cpython-312.pyc,, +networkx/algorithms/__pycache__/asteroidal.cpython-312.pyc,, +networkx/algorithms/__pycache__/boundary.cpython-312.pyc,, +networkx/algorithms/__pycache__/bridges.cpython-312.pyc,, +networkx/algorithms/__pycache__/broadcasting.cpython-312.pyc,, +networkx/algorithms/__pycache__/chains.cpython-312.pyc,, +networkx/algorithms/__pycache__/chordal.cpython-312.pyc,, +networkx/algorithms/__pycache__/clique.cpython-312.pyc,, +networkx/algorithms/__pycache__/cluster.cpython-312.pyc,, +networkx/algorithms/__pycache__/communicability_alg.cpython-312.pyc,, +networkx/algorithms/__pycache__/core.cpython-312.pyc,, +networkx/algorithms/__pycache__/covering.cpython-312.pyc,, +networkx/algorithms/__pycache__/cuts.cpython-312.pyc,, +networkx/algorithms/__pycache__/cycles.cpython-312.pyc,, +networkx/algorithms/__pycache__/d_separation.cpython-312.pyc,, +networkx/algorithms/__pycache__/dag.cpython-312.pyc,, +networkx/algorithms/__pycache__/distance_measures.cpython-312.pyc,, +networkx/algorithms/__pycache__/distance_regular.cpython-312.pyc,, +networkx/algorithms/__pycache__/dominance.cpython-312.pyc,, +networkx/algorithms/__pycache__/dominating.cpython-312.pyc,, +networkx/algorithms/__pycache__/efficiency_measures.cpython-312.pyc,, +networkx/algorithms/__pycache__/euler.cpython-312.pyc,, +networkx/algorithms/__pycache__/graph_hashing.cpython-312.pyc,, +networkx/algorithms/__pycache__/graphical.cpython-312.pyc,, +networkx/algorithms/__pycache__/hierarchy.cpython-312.pyc,, +networkx/algorithms/__pycache__/hybrid.cpython-312.pyc,, +networkx/algorithms/__pycache__/isolate.cpython-312.pyc,, +networkx/algorithms/__pycache__/link_prediction.cpython-312.pyc,, +networkx/algorithms/__pycache__/lowest_common_ancestors.cpython-312.pyc,, +networkx/algorithms/__pycache__/matching.cpython-312.pyc,, +networkx/algorithms/__pycache__/mis.cpython-312.pyc,, +networkx/algorithms/__pycache__/moral.cpython-312.pyc,, +networkx/algorithms/__pycache__/node_classification.cpython-312.pyc,, +networkx/algorithms/__pycache__/non_randomness.cpython-312.pyc,, +networkx/algorithms/__pycache__/perfect_graph.cpython-312.pyc,, +networkx/algorithms/__pycache__/planar_drawing.cpython-312.pyc,, +networkx/algorithms/__pycache__/planarity.cpython-312.pyc,, +networkx/algorithms/__pycache__/polynomials.cpython-312.pyc,, +networkx/algorithms/__pycache__/reciprocity.cpython-312.pyc,, +networkx/algorithms/__pycache__/regular.cpython-312.pyc,, +networkx/algorithms/__pycache__/richclub.cpython-312.pyc,, +networkx/algorithms/__pycache__/similarity.cpython-312.pyc,, +networkx/algorithms/__pycache__/simple_paths.cpython-312.pyc,, +networkx/algorithms/__pycache__/smallworld.cpython-312.pyc,, +networkx/algorithms/__pycache__/smetric.cpython-312.pyc,, +networkx/algorithms/__pycache__/sparsifiers.cpython-312.pyc,, +networkx/algorithms/__pycache__/structuralholes.cpython-312.pyc,, +networkx/algorithms/__pycache__/summarization.cpython-312.pyc,, +networkx/algorithms/__pycache__/swap.cpython-312.pyc,, +networkx/algorithms/__pycache__/threshold.cpython-312.pyc,, +networkx/algorithms/__pycache__/time_dependent.cpython-312.pyc,, +networkx/algorithms/__pycache__/tournament.cpython-312.pyc,, +networkx/algorithms/__pycache__/triads.cpython-312.pyc,, +networkx/algorithms/__pycache__/vitality.cpython-312.pyc,, +networkx/algorithms/__pycache__/voronoi.cpython-312.pyc,, +networkx/algorithms/__pycache__/walks.cpython-312.pyc,, +networkx/algorithms/__pycache__/wiener.cpython-312.pyc,, +networkx/algorithms/approximation/__init__.py,sha256=1W0c3YlSfVQtNI4-WiQdzMfPqmcrCar5hJQmrwcsVoI,1234 +networkx/algorithms/approximation/__pycache__/__init__.cpython-312.pyc,, +networkx/algorithms/approximation/__pycache__/clique.cpython-312.pyc,, +networkx/algorithms/approximation/__pycache__/clustering_coefficient.cpython-312.pyc,, +networkx/algorithms/approximation/__pycache__/connectivity.cpython-312.pyc,, +networkx/algorithms/approximation/__pycache__/density.cpython-312.pyc,, +networkx/algorithms/approximation/__pycache__/distance_measures.cpython-312.pyc,, +networkx/algorithms/approximation/__pycache__/dominating_set.cpython-312.pyc,, +networkx/algorithms/approximation/__pycache__/kcomponents.cpython-312.pyc,, +networkx/algorithms/approximation/__pycache__/matching.cpython-312.pyc,, +networkx/algorithms/approximation/__pycache__/maxcut.cpython-312.pyc,, +networkx/algorithms/approximation/__pycache__/ramsey.cpython-312.pyc,, +networkx/algorithms/approximation/__pycache__/steinertree.cpython-312.pyc,, +networkx/algorithms/approximation/__pycache__/traveling_salesman.cpython-312.pyc,, +networkx/algorithms/approximation/__pycache__/treewidth.cpython-312.pyc,, +networkx/algorithms/approximation/__pycache__/vertex_cover.cpython-312.pyc,, +networkx/algorithms/approximation/clique.py,sha256=b4cnWMJXmmgCyjMI8A_doHZeKS_RQbGqm2L01OpT_Jg,7691 +networkx/algorithms/approximation/clustering_coefficient.py,sha256=SWpSLEhW3DJc1n2fHlSbJSGg3wdoJkN5Y4_tnntn0Ws,2164 +networkx/algorithms/approximation/connectivity.py,sha256=aVXSfUiWEG4gUL0R1u6WZ-h-wheuLP1_suO_pRFB8M4,13118 +networkx/algorithms/approximation/density.py,sha256=_JU9nIH4wneHG6PtEAF_q1oLwy5UUKk--FkTVkkMAMg,15258 +networkx/algorithms/approximation/distance_measures.py,sha256=UEkmKagNw9sj8kiUDdbAeYuzvZ31pgLMXqzliqMkG84,5805 +networkx/algorithms/approximation/dominating_set.py,sha256=5fC90w1CgYR4Xkpqact8iukKY0i57bMmyJW-A9CToUQ,4710 +networkx/algorithms/approximation/kcomponents.py,sha256=MDkoyQbk0gSAm3ZZK35VOsiLJDv7wiDsxfzH5O-ObFs,13285 +networkx/algorithms/approximation/matching.py,sha256=PFof5m9AIq9Xr5Kaa_-mYxI1IBBP7HEkjf-R9wVE3bo,1175 +networkx/algorithms/approximation/maxcut.py,sha256=eTQZqsDQAAUaufni-aDJAY2UzIcajDhRMdj-AcqVkPs,4333 +networkx/algorithms/approximation/ramsey.py,sha256=W5tX7BOQJIM_qNsBeUhCXVWMD8DFdeTycYyk08k4Sqk,1358 +networkx/algorithms/approximation/steinertree.py,sha256=2t58cQQt9OAMRWP1q0RV9QrdNzgasZtPNp6PbA0LFDU,9363 +networkx/algorithms/approximation/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +networkx/algorithms/approximation/tests/__pycache__/__init__.cpython-312.pyc,, +networkx/algorithms/approximation/tests/__pycache__/test_approx_clust_coeff.cpython-312.pyc,, +networkx/algorithms/approximation/tests/__pycache__/test_clique.cpython-312.pyc,, +networkx/algorithms/approximation/tests/__pycache__/test_connectivity.cpython-312.pyc,, +networkx/algorithms/approximation/tests/__pycache__/test_density.cpython-312.pyc,, +networkx/algorithms/approximation/tests/__pycache__/test_distance_measures.cpython-312.pyc,, +networkx/algorithms/approximation/tests/__pycache__/test_dominating_set.cpython-312.pyc,, +networkx/algorithms/approximation/tests/__pycache__/test_kcomponents.cpython-312.pyc,, +networkx/algorithms/approximation/tests/__pycache__/test_matching.cpython-312.pyc,, +networkx/algorithms/approximation/tests/__pycache__/test_maxcut.cpython-312.pyc,, +networkx/algorithms/approximation/tests/__pycache__/test_ramsey.cpython-312.pyc,, +networkx/algorithms/approximation/tests/__pycache__/test_steinertree.cpython-312.pyc,, +networkx/algorithms/approximation/tests/__pycache__/test_traveling_salesman.cpython-312.pyc,, +networkx/algorithms/approximation/tests/__pycache__/test_treewidth.cpython-312.pyc,, +networkx/algorithms/approximation/tests/__pycache__/test_vertex_cover.cpython-312.pyc,, +networkx/algorithms/approximation/tests/test_approx_clust_coeff.py,sha256=PGOVEKf2BcJu1vvjZrgTlBBpwM8V6t7yCANjyS9nWF0,1171 +networkx/algorithms/approximation/tests/test_clique.py,sha256=s6HQB-lK3RAu_ftpe2NvIiMu0Ol8tpAdbGvWzucNL6k,3021 +networkx/algorithms/approximation/tests/test_connectivity.py,sha256=gDG6tsgP3ux7Dgu0x7r0nso7_yknIxicV42Gq0It5pc,5952 +networkx/algorithms/approximation/tests/test_density.py,sha256=EgJsX9z2lxWJCpF5NPbYEbE0GIATazCmgj7wFYeObDM,5298 +networkx/algorithms/approximation/tests/test_distance_measures.py,sha256=axgOojplJIgXdopgkjxjAgvzGTQ1FV1oJ5NG-7ICalo,2023 +networkx/algorithms/approximation/tests/test_dominating_set.py,sha256=l4pBDY7pK7Fxw-S4tOlNcxf-j2j5GpHPJ9f4TrMs1sI,2686 +networkx/algorithms/approximation/tests/test_kcomponents.py,sha256=tTljP1FHzXrUwi-oBz5AQcibRw1NgR4N5UE0a2OrOUA,9346 +networkx/algorithms/approximation/tests/test_matching.py,sha256=nitZncaM0605kaIu1NO6_5TFV2--nohUCO46XTD_lnM,186 +networkx/algorithms/approximation/tests/test_maxcut.py,sha256=U6CDZFSLfYDII-1nX9XB7avSz10kTx88vNazJFoLQ1k,2804 +networkx/algorithms/approximation/tests/test_ramsey.py,sha256=h36Ol39csHbIoTDBxbxMgn4371iVUGZ3a2N6l7d56lI,1143 +networkx/algorithms/approximation/tests/test_steinertree.py,sha256=ifBKzrKjRzfMg2g9BKSXVYLvuEbygQPp9rXR4cK99Q8,10727 +networkx/algorithms/approximation/tests/test_traveling_salesman.py,sha256=odnyk7CgLbMGI2QuHxj3BPRX4cCfWHK5N65rj0jMLW8,32048 +networkx/algorithms/approximation/tests/test_treewidth.py,sha256=b_79ZKiW0XX24-GYaeQJ9Zaq7ZFYQT0DcDTkACII3EY,8868 +networkx/algorithms/approximation/tests/test_vertex_cover.py,sha256=FobHNhG9CAMeB_AOEprUs-7XQdPoc1YvfmXhozDZ8pM,1942 +networkx/algorithms/approximation/traveling_salesman.py,sha256=HZU6dbPo3Hiz2-Z3QHLFm5kdGBQwkXPefkVUuW-IC2A,56210 +networkx/algorithms/approximation/treewidth.py,sha256=hfLiPlheQMN7MG6CGR5w7AscKbLu3pTLNYRS13SO0Xo,8389 +networkx/algorithms/approximation/vertex_cover.py,sha256=oIi_yg5O-IisnfmrSof1P4HD-fsZpW69RpvkR_SM5Og,2803 +networkx/algorithms/assortativity/__init__.py,sha256=ov3HRRbeYB_6Qezvxp1OTl77GBpw-EWkWGUzgfT8G9c,294 +networkx/algorithms/assortativity/__pycache__/__init__.cpython-312.pyc,, +networkx/algorithms/assortativity/__pycache__/connectivity.cpython-312.pyc,, +networkx/algorithms/assortativity/__pycache__/correlation.cpython-312.pyc,, +networkx/algorithms/assortativity/__pycache__/mixing.cpython-312.pyc,, +networkx/algorithms/assortativity/__pycache__/neighbor_degree.cpython-312.pyc,, +networkx/algorithms/assortativity/__pycache__/pairs.cpython-312.pyc,, +networkx/algorithms/assortativity/connectivity.py,sha256=-V0C5MTqtErl86N-gyrZ487MUyiG5x1QFEZKurOpIJA,4220 +networkx/algorithms/assortativity/correlation.py,sha256=0rc4FDi-e8eQRia7gpFrTqjIy-J7V2GtSwOb4QN6WZk,8689 +networkx/algorithms/assortativity/mixing.py,sha256=RRqqkuVwo71LosJLDbeVCVBikqC7I_XZORdsonQsf9Y,7586 +networkx/algorithms/assortativity/neighbor_degree.py,sha256=UMaQWKBkOZ0ZgC8xGt5fXEz8OL1rgwYjt2zKbKEqofI,5282 +networkx/algorithms/assortativity/pairs.py,sha256=w7xnaWxDDteluHoCsqunLlcM6nlcBenO_5Nz87oOEnE,3841 +networkx/algorithms/assortativity/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +networkx/algorithms/assortativity/tests/__pycache__/__init__.cpython-312.pyc,, +networkx/algorithms/assortativity/tests/__pycache__/base_test.cpython-312.pyc,, +networkx/algorithms/assortativity/tests/__pycache__/test_connectivity.cpython-312.pyc,, +networkx/algorithms/assortativity/tests/__pycache__/test_correlation.cpython-312.pyc,, +networkx/algorithms/assortativity/tests/__pycache__/test_mixing.cpython-312.pyc,, +networkx/algorithms/assortativity/tests/__pycache__/test_neighbor_degree.cpython-312.pyc,, +networkx/algorithms/assortativity/tests/__pycache__/test_pairs.cpython-312.pyc,, +networkx/algorithms/assortativity/tests/base_test.py,sha256=MNeQMLA3oBUCM8TSyNbBQ_uW0nDc1GEZYdNdUwePAm4,2651 +networkx/algorithms/assortativity/tests/test_connectivity.py,sha256=Js841GQLYTLWvc6xZhnyqj-JtyrnS0ska1TFYntxyXA,4978 +networkx/algorithms/assortativity/tests/test_correlation.py,sha256=ddx-yqnVcOfx1dKVNUF695hS3Q-zCmFmCGzK64B7YSE,5068 +networkx/algorithms/assortativity/tests/test_mixing.py,sha256=1kkiMoQXslY-VnT1j00mFbRdj75A4d1b6OPTUOJVgaY,6802 +networkx/algorithms/assortativity/tests/test_neighbor_degree.py,sha256=wphbir1e-h-BAq5rjvWBi4WlgWdseyQbh_KLGQvy5Pc,3934 +networkx/algorithms/assortativity/tests/test_pairs.py,sha256=t05qP_-gfkbiR6aTLtE1owYl9otBSsuJcRkuZsa63UQ,3008 +networkx/algorithms/asteroidal.py,sha256=hIGg9zI4vylRXkrY5wHverTSOeK6dt1Gn2T_JYvGNnU,5500 +networkx/algorithms/bipartite/__init__.py,sha256=FZug-pg0FUDgAdysnKXxDpi1ly8ezxf4UxBPRklqjys,3883 +networkx/algorithms/bipartite/__pycache__/__init__.cpython-312.pyc,, +networkx/algorithms/bipartite/__pycache__/basic.cpython-312.pyc,, +networkx/algorithms/bipartite/__pycache__/centrality.cpython-312.pyc,, +networkx/algorithms/bipartite/__pycache__/cluster.cpython-312.pyc,, +networkx/algorithms/bipartite/__pycache__/covering.cpython-312.pyc,, +networkx/algorithms/bipartite/__pycache__/edgelist.cpython-312.pyc,, +networkx/algorithms/bipartite/__pycache__/extendability.cpython-312.pyc,, +networkx/algorithms/bipartite/__pycache__/generators.cpython-312.pyc,, +networkx/algorithms/bipartite/__pycache__/link_analysis.cpython-312.pyc,, +networkx/algorithms/bipartite/__pycache__/matching.cpython-312.pyc,, +networkx/algorithms/bipartite/__pycache__/matrix.cpython-312.pyc,, +networkx/algorithms/bipartite/__pycache__/projection.cpython-312.pyc,, +networkx/algorithms/bipartite/__pycache__/redundancy.cpython-312.pyc,, +networkx/algorithms/bipartite/__pycache__/spectral.cpython-312.pyc,, +networkx/algorithms/bipartite/basic.py,sha256=JPC2gGuPvFA6q2CuI5mqLX_9QUGxrsQ8cIwcS0e9P4U,8375 +networkx/algorithms/bipartite/centrality.py,sha256=G280bAqeyXyCmes5NpRqUv2Tc-EHWrMshJ3_f4uqV9U,9156 +networkx/algorithms/bipartite/cluster.py,sha256=8aZRmlQ3g0XtzHyF1kUBBwnzMSjtduquHDH8MxKNSEI,7346 +networkx/algorithms/bipartite/covering.py,sha256=B3ITc016Kk70NBv-1lb30emXnfjlMIQJ7M-FIPCZip0,2163 +networkx/algorithms/bipartite/edgelist.py,sha256=mmiM2Bvh9CxRKCsspbs-90GZedpt7Gj5AGzvJ-aYDSM,11409 +networkx/algorithms/bipartite/extendability.py,sha256=OrYHlS4ruQST-dlQOuleiqHFKpVVNOvrG5aDNFgfckg,3989 +networkx/algorithms/bipartite/generators.py,sha256=p0xgyuJ5hp52NYPu2ryPEKZ0MEktP5VSW2HMK2VdBCo,20408 +networkx/algorithms/bipartite/link_analysis.py,sha256=eVRRQgwzMcUPPu6ccskPk72yc_lmnF5EGqNIdXe-MxA,12772 +networkx/algorithms/bipartite/matching.py,sha256=xsT048Ok_uM0Zhpdc34qswV1zaCGOlJQnsbGTDsm5oo,21637 +networkx/algorithms/bipartite/matrix.py,sha256=rxmjBNHts4hQ-EIlHLE3vIAeGHaMz8Kg_FJ8je5zFyQ,8320 +networkx/algorithms/bipartite/projection.py,sha256=YIUlreqQQ6IPE37OXF32zNIdzEGeyR8aY-7iUENZYVA,17252 +networkx/algorithms/bipartite/redundancy.py,sha256=eGNVo0qW-3unhGhY5VTs2vbc8QmOGve70UDY3ykjhNo,3340 +networkx/algorithms/bipartite/spectral.py,sha256=fu2grV1the_e_G-e_lUdhk8Y9XFe6_p2tPmx3RKntFw,1902 +networkx/algorithms/bipartite/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +networkx/algorithms/bipartite/tests/__pycache__/__init__.cpython-312.pyc,, +networkx/algorithms/bipartite/tests/__pycache__/test_basic.cpython-312.pyc,, +networkx/algorithms/bipartite/tests/__pycache__/test_centrality.cpython-312.pyc,, +networkx/algorithms/bipartite/tests/__pycache__/test_cluster.cpython-312.pyc,, +networkx/algorithms/bipartite/tests/__pycache__/test_covering.cpython-312.pyc,, +networkx/algorithms/bipartite/tests/__pycache__/test_edgelist.cpython-312.pyc,, +networkx/algorithms/bipartite/tests/__pycache__/test_extendability.cpython-312.pyc,, +networkx/algorithms/bipartite/tests/__pycache__/test_generators.cpython-312.pyc,, +networkx/algorithms/bipartite/tests/__pycache__/test_link_analysis.cpython-312.pyc,, +networkx/algorithms/bipartite/tests/__pycache__/test_matching.cpython-312.pyc,, +networkx/algorithms/bipartite/tests/__pycache__/test_matrix.cpython-312.pyc,, +networkx/algorithms/bipartite/tests/__pycache__/test_project.cpython-312.pyc,, +networkx/algorithms/bipartite/tests/__pycache__/test_redundancy.cpython-312.pyc,, +networkx/algorithms/bipartite/tests/__pycache__/test_spectral_bipartivity.cpython-312.pyc,, +networkx/algorithms/bipartite/tests/test_basic.py,sha256=gzbtsQqPi85BznX5REdGBBJVyr9aH4nO06c3eEI4634,4291 +networkx/algorithms/bipartite/tests/test_centrality.py,sha256=PABPbrIyoAziEEQKXsZLl2jT36N8DZpNRzEO-jeu89Y,6362 +networkx/algorithms/bipartite/tests/test_cluster.py,sha256=O0VsPVt8vcY_E1FjjLJX2xaUbhVViI5MP6_gLTbEpos,2801 +networkx/algorithms/bipartite/tests/test_covering.py,sha256=EGVxYQsyLXE5yY5N5u6D4wZq2NcZe9OwlYpEuY6DF3o,1221 +networkx/algorithms/bipartite/tests/test_edgelist.py,sha256=fK35tSekG_-9Ewr5Bhl1bRdwAy247Z9zZ4dQFFDQ9xw,8471 +networkx/algorithms/bipartite/tests/test_extendability.py,sha256=XgPmg6bWiHAF1iQ75_r2NqUxExOQNZRUeYUPzlCa5-E,7043 +networkx/algorithms/bipartite/tests/test_generators.py,sha256=BehRU6SQnWzKsAFoshrN2vpxcPByLAViofGeq38v23E,13203 +networkx/algorithms/bipartite/tests/test_link_analysis.py,sha256=vJMOtYG0vxYQCif_ztnYTUm_13gApfzFiNxChRefONg,6914 +networkx/algorithms/bipartite/tests/test_matching.py,sha256=3-2DMl3tF-g4_xNHvEuY4fZW7S5cqMTO_GUpcz1gkeQ,11973 +networkx/algorithms/bipartite/tests/test_matrix.py,sha256=TlZRVHCTKO2sqhycKFsefvdqP-Se47b_8iZwjtPpQYs,5063 +networkx/algorithms/bipartite/tests/test_project.py,sha256=WhX_yteTrXOdTZLMXpkW2A5ZFIBkw0WsnJRsNITyYtQ,15294 +networkx/algorithms/bipartite/tests/test_redundancy.py,sha256=utxcrQaTrkcEN3kqtObgKNpLZai8B5sMAqLyXatOuUo,917 +networkx/algorithms/bipartite/tests/test_spectral_bipartivity.py,sha256=1jGDgrIx3-TWOCNMSC4zxmZa7LHyMU69DXh3h12Bjag,2358 +networkx/algorithms/boundary.py,sha256=q3JtWssmn9yCB2mBdkjKZjkaxmBhkG9_dJOzmuJiQos,5339 +networkx/algorithms/bridges.py,sha256=CsxueHDOB9aFM5D8GP83u1ZKGzxF193XBpvmMReAcQk,6066 +networkx/algorithms/broadcasting.py,sha256=Amw1oRpr8pdS67aC_9wjJOQomP1IqPvqtE7c0Mf9L70,4974 +networkx/algorithms/centrality/__init__.py,sha256=Er3YoYoj76UfY4P6I0L-0fCQkO7mMU0b3NLsTT2RGWI,558 +networkx/algorithms/centrality/__pycache__/__init__.cpython-312.pyc,, +networkx/algorithms/centrality/__pycache__/betweenness.cpython-312.pyc,, +networkx/algorithms/centrality/__pycache__/betweenness_subset.cpython-312.pyc,, +networkx/algorithms/centrality/__pycache__/closeness.cpython-312.pyc,, +networkx/algorithms/centrality/__pycache__/current_flow_betweenness.cpython-312.pyc,, +networkx/algorithms/centrality/__pycache__/current_flow_betweenness_subset.cpython-312.pyc,, +networkx/algorithms/centrality/__pycache__/current_flow_closeness.cpython-312.pyc,, +networkx/algorithms/centrality/__pycache__/degree_alg.cpython-312.pyc,, +networkx/algorithms/centrality/__pycache__/dispersion.cpython-312.pyc,, +networkx/algorithms/centrality/__pycache__/eigenvector.cpython-312.pyc,, +networkx/algorithms/centrality/__pycache__/flow_matrix.cpython-312.pyc,, +networkx/algorithms/centrality/__pycache__/group.cpython-312.pyc,, +networkx/algorithms/centrality/__pycache__/harmonic.cpython-312.pyc,, +networkx/algorithms/centrality/__pycache__/katz.cpython-312.pyc,, +networkx/algorithms/centrality/__pycache__/laplacian.cpython-312.pyc,, +networkx/algorithms/centrality/__pycache__/load.cpython-312.pyc,, +networkx/algorithms/centrality/__pycache__/percolation.cpython-312.pyc,, +networkx/algorithms/centrality/__pycache__/reaching.cpython-312.pyc,, +networkx/algorithms/centrality/__pycache__/second_order.cpython-312.pyc,, +networkx/algorithms/centrality/__pycache__/subgraph_alg.cpython-312.pyc,, +networkx/algorithms/centrality/__pycache__/trophic.cpython-312.pyc,, +networkx/algorithms/centrality/__pycache__/voterank_alg.cpython-312.pyc,, +networkx/algorithms/centrality/betweenness.py,sha256=8829b5ep0pilM2Muqk5U0rPVQ02kwwedXaVGuQ_hY_U,22814 +networkx/algorithms/centrality/betweenness_subset.py,sha256=CUX0c96gYIORsdjOxVtif2796gOAcMitYoqEnbXDi5c,8114 +networkx/algorithms/centrality/closeness.py,sha256=ehkntG-gApT9uhWJjGaEZQ-tEQ-hdxDT7luf-uVPNAE,10281 +networkx/algorithms/centrality/current_flow_betweenness.py,sha256=kbq9XsZQvrCOdCAYvF67hRni1aqA1sT93WU2i0WfKKI,12717 +networkx/algorithms/centrality/current_flow_betweenness_subset.py,sha256=2qtLgf_3ft5qdDvHFrfYUt6zeQi42Nw7XBpSZRboJIA,8107 +networkx/algorithms/centrality/current_flow_closeness.py,sha256=IvecI8BZE4SgKayEXhKowIJw7S2fD_dN__N-f9TW-ME,3327 +networkx/algorithms/centrality/degree_alg.py,sha256=EFTA1b_GWUbmBy5R9beRQp7yh1X_NwZtk5L6is-mFGk,3894 +networkx/algorithms/centrality/dispersion.py,sha256=M12L2KiVPrC2-SyCXMF0kvxLelgcmvXJkLT_cBHoCTw,3631 +networkx/algorithms/centrality/eigenvector.py,sha256=LAxVqaT3LmuQw20__t1KrgLKPF1Cz-PkTaiSrgPC1FU,13623 +networkx/algorithms/centrality/flow_matrix.py,sha256=Y65m6VbWyYjNK0CInE_lufyEkKy9-TyPmBeXb-Gkz70,3834 +networkx/algorithms/centrality/group.py,sha256=TLlK2eWlcMX7Lvbe2wAcZrmZ9LLTvbRCz-3RbXF0Zug,27960 +networkx/algorithms/centrality/harmonic.py,sha256=OfrDikASbb0Xejjbd1kJ-lQuGT3Gv-rg3pmTtncAbho,2832 +networkx/algorithms/centrality/katz.py,sha256=uVGHAyjqndSd4y4idHjkv0mUhmKmHU5vaEfNWfiKlzc,11042 +networkx/algorithms/centrality/laplacian.py,sha256=qjyW2WnxMFo_mZqiQ4d8PVHLpXi3xNEX148Yikvk_ls,5554 +networkx/algorithms/centrality/load.py,sha256=M2EdPX4gJEYGjMBIJMFKRWGI9uYHbFOWYxsILeaJuOE,6859 +networkx/algorithms/centrality/percolation.py,sha256=YJB8iYgbpjJ3EYK8pl26iSnjgfFsK31ufytRHnUTYYE,4419 +networkx/algorithms/centrality/reaching.py,sha256=Q9rda_dqXBfVaHOi8FgZSYNHKpJ0SHfCbX_myMsIe2I,7255 +networkx/algorithms/centrality/second_order.py,sha256=4CTboP95B6gUtAtSKLfeeE4s9oq0_3hXsXczxL6c_g8,5012 +networkx/algorithms/centrality/subgraph_alg.py,sha256=YXkuyhhhZHmobNFpKDUpfeqSPyBvk97MlcJy5ESWh1s,10520 +networkx/algorithms/centrality/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +networkx/algorithms/centrality/tests/__pycache__/__init__.cpython-312.pyc,, +networkx/algorithms/centrality/tests/__pycache__/test_betweenness_centrality.cpython-312.pyc,, +networkx/algorithms/centrality/tests/__pycache__/test_betweenness_centrality_subset.cpython-312.pyc,, +networkx/algorithms/centrality/tests/__pycache__/test_closeness_centrality.cpython-312.pyc,, +networkx/algorithms/centrality/tests/__pycache__/test_current_flow_betweenness_centrality.cpython-312.pyc,, +networkx/algorithms/centrality/tests/__pycache__/test_current_flow_betweenness_centrality_subset.cpython-312.pyc,, +networkx/algorithms/centrality/tests/__pycache__/test_current_flow_closeness.cpython-312.pyc,, +networkx/algorithms/centrality/tests/__pycache__/test_degree_centrality.cpython-312.pyc,, +networkx/algorithms/centrality/tests/__pycache__/test_dispersion.cpython-312.pyc,, +networkx/algorithms/centrality/tests/__pycache__/test_eigenvector_centrality.cpython-312.pyc,, +networkx/algorithms/centrality/tests/__pycache__/test_group.cpython-312.pyc,, +networkx/algorithms/centrality/tests/__pycache__/test_harmonic_centrality.cpython-312.pyc,, +networkx/algorithms/centrality/tests/__pycache__/test_katz_centrality.cpython-312.pyc,, +networkx/algorithms/centrality/tests/__pycache__/test_laplacian_centrality.cpython-312.pyc,, +networkx/algorithms/centrality/tests/__pycache__/test_load_centrality.cpython-312.pyc,, +networkx/algorithms/centrality/tests/__pycache__/test_percolation_centrality.cpython-312.pyc,, +networkx/algorithms/centrality/tests/__pycache__/test_reaching.cpython-312.pyc,, +networkx/algorithms/centrality/tests/__pycache__/test_second_order_centrality.cpython-312.pyc,, +networkx/algorithms/centrality/tests/__pycache__/test_subgraph.cpython-312.pyc,, +networkx/algorithms/centrality/tests/__pycache__/test_trophic.cpython-312.pyc,, +networkx/algorithms/centrality/tests/__pycache__/test_voterank.cpython-312.pyc,, +networkx/algorithms/centrality/tests/test_betweenness_centrality.py,sha256=LmHNSaqExAT8kE-eOb_67Z6ckSGD_XqO56OHXhNkbFc,33669 +networkx/algorithms/centrality/tests/test_betweenness_centrality_subset.py,sha256=Y7qigLxQwFJqClJiCrKWGkcOHD467W0b6C5gBb0rFUg,13085 +networkx/algorithms/centrality/tests/test_closeness_centrality.py,sha256=-LtG4ex192Xjgy4JCTfdjdJ3Cd9Op-3XnVvG2GA3FWQ,8728 +networkx/algorithms/centrality/tests/test_current_flow_betweenness_centrality.py,sha256=1sipOadh8bsuzHZ0_7haapKU6Vrn0rbZL5HXvbsto5w,10072 +networkx/algorithms/centrality/tests/test_current_flow_betweenness_centrality_subset.py,sha256=JfRGgPuiF-vJu5fc2_pcJYREEboxcK_dmy-np39c4Aw,5839 +networkx/algorithms/centrality/tests/test_current_flow_closeness.py,sha256=vflQeoNKngrGUiRb3XNlm2X9wR4vKgMSW_sCyMUCQi8,1379 +networkx/algorithms/centrality/tests/test_degree_centrality.py,sha256=Jn_p5lThA3__ZBTDAORwo_EchjXKKkK1NwU_73HHI6M,4101 +networkx/algorithms/centrality/tests/test_dispersion.py,sha256=ROgl_5bGhcNXonNW3ylsvUcA0NCwynsQu_scic371Gw,1959 +networkx/algorithms/centrality/tests/test_eigenvector_centrality.py,sha256=AfUa7GTve0UX6QOdBF6YMipmo-gKhyCu6aT8OCSf8wc,5254 +networkx/algorithms/centrality/tests/test_group.py,sha256=833ME4tGlOGQZz8YANw4MSyeVPpjbyCdYh5X88GOprw,8685 +networkx/algorithms/centrality/tests/test_harmonic_centrality.py,sha256=wI7nStX_kIFJoZQY_i8DXXlZBOJzVnQfOP8yidX0PAU,3867 +networkx/algorithms/centrality/tests/test_katz_centrality.py,sha256=JL0bZZsJe2MQFL6urXgY82wCAwucUvhjaShYZPxpL6U,11240 +networkx/algorithms/centrality/tests/test_laplacian_centrality.py,sha256=9Nd9CfiCn2908BgRZ-cQiMQjpOFDu4Bftod1didWyCE,5898 +networkx/algorithms/centrality/tests/test_load_centrality.py,sha256=Vv3zSW89iELN-8KNbUclmkhOe1LzKdF7U_w34nYovIo,11343 +networkx/algorithms/centrality/tests/test_percolation_centrality.py,sha256=ycQ1fvEZZcWAfqL11urT7yHiEP77usJDSG25OQiDM2s,2591 +networkx/algorithms/centrality/tests/test_reaching.py,sha256=_JVeO1Ri-KybdnGCJ_yNPtJQmT_g77z0DAkU0JYFVGQ,5090 +networkx/algorithms/centrality/tests/test_second_order_centrality.py,sha256=ce0wQ4T33lu23wskzGUnBS7X4BSODlvAX1S5KxlLzOA,1999 +networkx/algorithms/centrality/tests/test_subgraph.py,sha256=vhE9Uh-_Hlk49k-ny6ORHCgqk7LWH8OHIYOEYM96uz0,3729 +networkx/algorithms/centrality/tests/test_trophic.py,sha256=mt--0AUc_8qez2SjauEHVnetC3DMwMAlLME6kgb8Lc0,8796 +networkx/algorithms/centrality/tests/test_voterank.py,sha256=tN5u7pKAnJ_4AiwhPW6EuJZz7FLIG2jYqLKcXFi2urk,1687 +networkx/algorithms/centrality/trophic.py,sha256=7mpFrpgQhwP3Ad1plpJu1WzTGR1YWrIp_SUhM0D8Zww,5328 +networkx/algorithms/centrality/voterank_alg.py,sha256=z_1eq8rSDadEO5W5BbAg1zuOJj2di4FUCkmOwiuK12I,3231 +networkx/algorithms/chains.py,sha256=PPiSq5-GsT1Lsf8fwtGwGDVf1hhv5ZLariWtfzkBbAw,6968 +networkx/algorithms/chordal.py,sha256=L-ILWdVLWE44OkWmEO_4bSo4z6Ro-_zLglfLfTrwdqQ,13411 +networkx/algorithms/clique.py,sha256=SAvORAbZrZ-IpTVuEcarETQihNes-glpNiqoZ7hEgnM,27522 +networkx/algorithms/cluster.py,sha256=sKexvbvRnjuHSAkoJ9_dzCuOWDUDQkT_KjzmkKSoAhE,24851 +networkx/algorithms/coloring/__init__.py,sha256=P1cmqrAjcaCdObkNZ1e6Hp__ZpxBAhQx0iIipOVW8jg,182 +networkx/algorithms/coloring/__pycache__/__init__.cpython-312.pyc,, +networkx/algorithms/coloring/__pycache__/equitable_coloring.cpython-312.pyc,, +networkx/algorithms/coloring/__pycache__/greedy_coloring.cpython-312.pyc,, +networkx/algorithms/coloring/equitable_coloring.py,sha256=uDcza6PD9qbvwVPUX1MBZbopQdrAEKNk6DpCFkc02tU,16315 +networkx/algorithms/coloring/greedy_coloring.py,sha256=GLXbwSvitsQgmxtOsbgUt4DTkURnb2k0X-7-SNsDW9I,20043 +networkx/algorithms/coloring/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +networkx/algorithms/coloring/tests/__pycache__/__init__.cpython-312.pyc,, +networkx/algorithms/coloring/tests/__pycache__/test_coloring.cpython-312.pyc,, +networkx/algorithms/coloring/tests/test_coloring.py,sha256=7v_d1xanjYMZCa3dq2hE2hCcyexwWBTEFV5SoLgQDv4,23697 +networkx/algorithms/communicability_alg.py,sha256=0tZvZKY-_GUUB7GsRILxabS2jEpI51Udg5ADI9ADGZw,4545 +networkx/algorithms/community/__init__.py,sha256=vfw5aY7eoL7UDk42e0JPoVzRViCzk5cQf83zbls5p90,1279 +networkx/algorithms/community/__pycache__/__init__.cpython-312.pyc,, +networkx/algorithms/community/__pycache__/asyn_fluid.cpython-312.pyc,, +networkx/algorithms/community/__pycache__/bipartitions.cpython-312.pyc,, +networkx/algorithms/community/__pycache__/centrality.cpython-312.pyc,, +networkx/algorithms/community/__pycache__/community_utils.cpython-312.pyc,, +networkx/algorithms/community/__pycache__/divisive.cpython-312.pyc,, +networkx/algorithms/community/__pycache__/kclique.cpython-312.pyc,, +networkx/algorithms/community/__pycache__/label_propagation.cpython-312.pyc,, +networkx/algorithms/community/__pycache__/leiden.cpython-312.pyc,, +networkx/algorithms/community/__pycache__/local.cpython-312.pyc,, +networkx/algorithms/community/__pycache__/louvain.cpython-312.pyc,, +networkx/algorithms/community/__pycache__/lukes.cpython-312.pyc,, +networkx/algorithms/community/__pycache__/modularity_max.cpython-312.pyc,, +networkx/algorithms/community/__pycache__/quality.cpython-312.pyc,, +networkx/algorithms/community/asyn_fluid.py,sha256=bsY14UgR5FKLFqhFhHKmPP4-RJsVzH6K8ogsImLnCE8,6010 +networkx/algorithms/community/bipartitions.py,sha256=jqgJyMKOCs8TwI8YM9DO8qzuVZFe4XCnYRGeVAx5P4c,12238 +networkx/algorithms/community/centrality.py,sha256=Yyv5kyf1hf_L7iQ_ZbG8_FAkP638Sc_3N4tCSoB6J1w,6635 +networkx/algorithms/community/community_utils.py,sha256=sUi-AcPYyGrYhnjI9ztt-vrSHLl28lKXxTJPfi5N0c8,908 +networkx/algorithms/community/divisive.py,sha256=yFcKfKkiI6FqEVlBVxLa1fbqI1Yeiqe_A5fpPnYvlAE,6655 +networkx/algorithms/community/kclique.py,sha256=DTr9iUT_XWv0S3Y79KQl6OXefjztNMc9SAHWhdFOxcU,2460 +networkx/algorithms/community/label_propagation.py,sha256=LhzAXSHFCPQ2kG_rPgXb06YKdppO7buApksCC4GI4w8,11878 +networkx/algorithms/community/leiden.py,sha256=dx1N_KEsy30mfJ4729rJ28qaLsnkxoqDnv3UOXV2hWQ,6964 +networkx/algorithms/community/local.py,sha256=w-LK7qlMsQ2YHbZRQP95JvqM2gHGSe4yYZi7CXNkj_M,7316 +networkx/algorithms/community/louvain.py,sha256=jscGGTF6uUnC7yGEZTcE9UFmKsLDFwlZa4ecQyVQrOU,15424 +networkx/algorithms/community/lukes.py,sha256=gzqnup95RR2UzUiPpIt8qkepzZ9dCWqHGQSVPIJDMx8,8115 +networkx/algorithms/community/modularity_max.py,sha256=Qzidmvk2QOIb-S2o4erJvLSeQWcPbLq5TVMrxw15Wc4,18093 +networkx/algorithms/community/quality.py,sha256=oEw-RZBe62janFTTs-ak62APBiF2FpQoBiHv11_4YQY,11943 +networkx/algorithms/community/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +networkx/algorithms/community/tests/__pycache__/__init__.cpython-312.pyc,, +networkx/algorithms/community/tests/__pycache__/test_asyn_fluid.cpython-312.pyc,, +networkx/algorithms/community/tests/__pycache__/test_bipartitions.cpython-312.pyc,, +networkx/algorithms/community/tests/__pycache__/test_centrality.cpython-312.pyc,, +networkx/algorithms/community/tests/__pycache__/test_divisive.cpython-312.pyc,, +networkx/algorithms/community/tests/__pycache__/test_kclique.cpython-312.pyc,, +networkx/algorithms/community/tests/__pycache__/test_label_propagation.cpython-312.pyc,, +networkx/algorithms/community/tests/__pycache__/test_leiden.cpython-312.pyc,, +networkx/algorithms/community/tests/__pycache__/test_local.cpython-312.pyc,, +networkx/algorithms/community/tests/__pycache__/test_louvain.cpython-312.pyc,, +networkx/algorithms/community/tests/__pycache__/test_lukes.cpython-312.pyc,, +networkx/algorithms/community/tests/__pycache__/test_modularity_max.cpython-312.pyc,, +networkx/algorithms/community/tests/__pycache__/test_quality.cpython-312.pyc,, +networkx/algorithms/community/tests/__pycache__/test_utils.cpython-312.pyc,, +networkx/algorithms/community/tests/test_asyn_fluid.py,sha256=XziMTOiEhzX6cvJdLeODUC1zjOLWchhHf9Z_9Eb3TME,3738 +networkx/algorithms/community/tests/test_bipartitions.py,sha256=w7kxfw5WjKzIosCdq3eHsvuYhUoElaO9Q3lK0kVyNg4,4639 +networkx/algorithms/community/tests/test_centrality.py,sha256=s8q4k5aThR0OgO9CDQk_PXMxfllmf5uC1GlvyUc_8EY,2932 +networkx/algorithms/community/tests/test_divisive.py,sha256=-Ee40OR-mPDReTngTEhbpx4_uLtNI7cqFkt8cZT9t5Y,3441 +networkx/algorithms/community/tests/test_kclique.py,sha256=iA0SBqwbDfaD2u7KM6ccs6LfgAQY_xxrnW05UIT_tFA,2413 +networkx/algorithms/community/tests/test_label_propagation.py,sha256=IHidFEv7MI781zsdk7XT848rLvLwDk2wBK1FjL-CRv4,7985 +networkx/algorithms/community/tests/test_leiden.py,sha256=bl4jr-Z0m59AISIAl-OLsnATz1lDacNssc-ICKn72Nw,4803 +networkx/algorithms/community/tests/test_local.py,sha256=c-dy1rs1L0ahhDTQwNZP1zNbffjzRgw5IBFTCBV-fas,1809 +networkx/algorithms/community/tests/test_louvain.py,sha256=TwW1nlSKWGJeIKr9QOJ8xGehSY6R0Nz01xsnFqzt0Oo,8071 +networkx/algorithms/community/tests/test_lukes.py,sha256=f_JU-EzY6PwXEkPN8kk5_3NVg6phlX0nrj1f57M49lk,3961 +networkx/algorithms/community/tests/test_modularity_max.py,sha256=XYyPuDkxL4CYFwnpTdU_qD4GydpqgiRAIJO3CHQN_m4,10617 +networkx/algorithms/community/tests/test_quality.py,sha256=sZEy10hh3zlelUmww5r2pk5LxpZAht06PC5zCHxV1bs,5275 +networkx/algorithms/community/tests/test_utils.py,sha256=gomD6rFgAaywxT1Yjdi4ozY-1rC0ina4jgfvWeCvwGE,704 +networkx/algorithms/components/__init__.py,sha256=Dt74KZWp_cJ_j0lL5hd_S50_hia5DKcC2SjuRnubr6M,173 +networkx/algorithms/components/__pycache__/__init__.cpython-312.pyc,, +networkx/algorithms/components/__pycache__/attracting.cpython-312.pyc,, +networkx/algorithms/components/__pycache__/biconnected.cpython-312.pyc,, +networkx/algorithms/components/__pycache__/connected.cpython-312.pyc,, +networkx/algorithms/components/__pycache__/semiconnected.cpython-312.pyc,, +networkx/algorithms/components/__pycache__/strongly_connected.cpython-312.pyc,, +networkx/algorithms/components/__pycache__/weakly_connected.cpython-312.pyc,, +networkx/algorithms/components/attracting.py,sha256=6az3lgqWhHTXaWUUuOPZfW9t7okliAhooFRotQY5JoM,2712 +networkx/algorithms/components/biconnected.py,sha256=_9GJdPZgqusGKZLzqT9tUSj1XZr2DgohiT6hcHVyil4,12782 +networkx/algorithms/components/connected.py,sha256=s-uvEdHkY8dNxNPS5G8jnk-qTCum-bet_yKAx8_qTXc,7904 +networkx/algorithms/components/semiconnected.py,sha256=BaBMFlQ208vuHOo5y1xeV0PDEI3yDUfH6zFb_jkcVhQ,2030 +networkx/algorithms/components/strongly_connected.py,sha256=oZGBBDzhsrCBQjIfqYlkzvXkE3sF-WTRtYEa5UYy6-k,9911 +networkx/algorithms/components/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +networkx/algorithms/components/tests/__pycache__/__init__.cpython-312.pyc,, +networkx/algorithms/components/tests/__pycache__/test_attracting.cpython-312.pyc,, +networkx/algorithms/components/tests/__pycache__/test_biconnected.cpython-312.pyc,, +networkx/algorithms/components/tests/__pycache__/test_connected.cpython-312.pyc,, +networkx/algorithms/components/tests/__pycache__/test_semiconnected.cpython-312.pyc,, +networkx/algorithms/components/tests/__pycache__/test_strongly_connected.cpython-312.pyc,, +networkx/algorithms/components/tests/__pycache__/test_weakly_connected.cpython-312.pyc,, +networkx/algorithms/components/tests/test_attracting.py,sha256=b3N3ZR9E5gLSQWGgaqhcRfRs4KBW6GnnkVYeAjdxC_o,2243 +networkx/algorithms/components/tests/test_biconnected.py,sha256=N-J-dgBgI77ytYUUrXjduLxtDydH7jS-af98fyPBkYc,6036 +networkx/algorithms/components/tests/test_connected.py,sha256=KMYm55BpbFdGXk_B2WozS9rIagQROd7_k0LT3HFQmr4,4815 +networkx/algorithms/components/tests/test_semiconnected.py,sha256=q860lIxZF5M2JmDwwdzy-SGSXnrillOefMx23GcJpw0,1792 +networkx/algorithms/components/tests/test_strongly_connected.py,sha256=Zm7MgUIZbuPPJu66xZH1zfMZQ_3X1YBl2fLCOjph7NQ,6021 +networkx/algorithms/components/tests/test_weakly_connected.py,sha256=_eUx7226dxme_K2WNmvSIwZXQlKNoCuglWOOC3kFUW4,3083 +networkx/algorithms/components/weakly_connected.py,sha256=q5siwSHQYtbKDcVc4dUDzfKh-DClETmuy3jLQN2_K4o,4413 +networkx/algorithms/connectivity/__init__.py,sha256=EvYKw8LJn7wyZECHAsuEkIaSl-cV-LhymR6tqcn90p8,281 +networkx/algorithms/connectivity/__pycache__/__init__.cpython-312.pyc,, +networkx/algorithms/connectivity/__pycache__/connectivity.cpython-312.pyc,, +networkx/algorithms/connectivity/__pycache__/cuts.cpython-312.pyc,, +networkx/algorithms/connectivity/__pycache__/disjoint_paths.cpython-312.pyc,, +networkx/algorithms/connectivity/__pycache__/edge_augmentation.cpython-312.pyc,, +networkx/algorithms/connectivity/__pycache__/edge_kcomponents.cpython-312.pyc,, +networkx/algorithms/connectivity/__pycache__/kcomponents.cpython-312.pyc,, +networkx/algorithms/connectivity/__pycache__/kcutsets.cpython-312.pyc,, +networkx/algorithms/connectivity/__pycache__/stoerwagner.cpython-312.pyc,, +networkx/algorithms/connectivity/__pycache__/utils.cpython-312.pyc,, +networkx/algorithms/connectivity/connectivity.py,sha256=xck9yth1asWFAM9Hp7UP6vdUh-Kr0IOmq046plOAXPQ,29367 +networkx/algorithms/connectivity/cuts.py,sha256=o-5GRQotOVXsNq7Kx_w7yBBVuGLa57whS2YF5DVb1KQ,23199 +networkx/algorithms/connectivity/disjoint_paths.py,sha256=57ZerbGqn30B8cwomcsS0GScectvP-mgCMb0GH-RYb8,14649 +networkx/algorithms/connectivity/edge_augmentation.py,sha256=cK9S6pRnsKLyb_57guKfrAbLiXL6sMALHCf7pR4tzFM,44063 +networkx/algorithms/connectivity/edge_kcomponents.py,sha256=hqABcfCqZ-rb45I0qYE-X4NtstsKJbxl37FZzzmoXA4,20894 +networkx/algorithms/connectivity/kcomponents.py,sha256=qDDeMqPb5rWbi6EKZwetBft5Qmv6PSUngQTVunJxyy0,8086 +networkx/algorithms/connectivity/kcutsets.py,sha256=zYohzgkR2FODi_Ew2M9uMLb_a9ZP5fNqcXJwMYy6P7o,9371 +networkx/algorithms/connectivity/stoerwagner.py,sha256=WodsJEqKgsmTTcyUBk2u3wV_CXeon-cAzveWgIGgFmA,5431 +networkx/algorithms/connectivity/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +networkx/algorithms/connectivity/tests/__pycache__/__init__.cpython-312.pyc,, +networkx/algorithms/connectivity/tests/__pycache__/test_connectivity.cpython-312.pyc,, +networkx/algorithms/connectivity/tests/__pycache__/test_cuts.cpython-312.pyc,, +networkx/algorithms/connectivity/tests/__pycache__/test_disjoint_paths.cpython-312.pyc,, +networkx/algorithms/connectivity/tests/__pycache__/test_edge_augmentation.cpython-312.pyc,, +networkx/algorithms/connectivity/tests/__pycache__/test_edge_kcomponents.cpython-312.pyc,, +networkx/algorithms/connectivity/tests/__pycache__/test_kcomponents.cpython-312.pyc,, +networkx/algorithms/connectivity/tests/__pycache__/test_kcutsets.cpython-312.pyc,, +networkx/algorithms/connectivity/tests/__pycache__/test_stoer_wagner.cpython-312.pyc,, +networkx/algorithms/connectivity/tests/test_connectivity.py,sha256=eSmsi8uQk6MI591JgtSu2elIusb08bmSZS0h9gxb76I,15027 +networkx/algorithms/connectivity/tests/test_cuts.py,sha256=q8dxYBAnsGtIBBPZCjIvMszZvXOYST2cVVsCJvXbIhw,10356 +networkx/algorithms/connectivity/tests/test_disjoint_paths.py,sha256=NLHReLoXSKoA6KPBNRbjF84ktg5PEaaktIj2AII3SDY,8392 +networkx/algorithms/connectivity/tests/test_edge_augmentation.py,sha256=gIu58oxk_uSexawlr3H1ds2HGqkTHHGhhVYUCNmfrb4,15737 +networkx/algorithms/connectivity/tests/test_edge_kcomponents.py,sha256=Bzo77zG79Lv_-OzdI1ZZKO_c13wrnFmfv60FrkDyXI0,16453 +networkx/algorithms/connectivity/tests/test_kcomponents.py,sha256=vLwQOLRevXeeid3e7SSKVpd_OyAJE8PxiAU8GYX0Tf0,9276 +networkx/algorithms/connectivity/tests/test_kcutsets.py,sha256=w5DyrjndNqyT3qxQH6WTBCuBR0ljYXatZCtcT1WeiYM,8974 +networkx/algorithms/connectivity/tests/test_stoer_wagner.py,sha256=A291C30_t2CI1erPCqN1W0DoAj3zqNA8fThPIj4Rku0,3011 +networkx/algorithms/connectivity/utils.py,sha256=gL8LmZnK4GKAZQcIPEhVNYmVi18Mqsqwg4O4j_et56s,3217 +networkx/algorithms/core.py,sha256=6SO5Wz-LSkx6t2DSq0GPmOvFALGe0gVsXdsvno9Fljs,17479 +networkx/algorithms/covering.py,sha256=I_on4DUgmwbtbo-qlBq4YWhOsNjuSHCfVGZNAL-Sefs,5278 +networkx/algorithms/cuts.py,sha256=eqwi5bhCMchItKlRAUw4wGVRtHUK0JzTkaXJFUyxXEw,10419 +networkx/algorithms/cycles.py,sha256=AGpANFoLZ2td8YkGn0cHypASJuRllAwj_-g2Vve7QW8,43302 +networkx/algorithms/d_separation.py,sha256=DpOl1YaI-72_fgUJ2NI3Piuj7EQRQ8xmyAQCRdLgjTw,26089 +networkx/algorithms/dag.py,sha256=OPgrOtnKJVMwxu2AxBGWPZyO7ti6qXa6pn57SsO8FAQ,44673 +networkx/algorithms/distance_measures.py,sha256=cQIRYjP4FtpbA3SP_dOPM6hvwpM1z77oMfmuZ4w8suI,37618 +networkx/algorithms/distance_regular.py,sha256=M9m6qitAtCXYbv2k9hDjF0CpxdeEXPu2_KOI8CCGc_I,8391 +networkx/algorithms/dominance.py,sha256=00Ng1SMzAvxodLTfAeLnXexpPb13IlB_S_mDEuT8JhY,3897 +networkx/algorithms/dominating.py,sha256=yoRB4WCe0wMvJpkbKtNo2fpd0jTVq6b09JsCYcaeR_Y,8145 +networkx/algorithms/efficiency_measures.py,sha256=VKbLKJgdIbno-YnJaLaCZt7TNXXnQPdz8N99uJCo748,4741 +networkx/algorithms/euler.py,sha256=yCqKaGchFSRPTRDXq7u1fH2IXZF94wWf9S10K9-Cd6U,14205 +networkx/algorithms/flow/__init__.py,sha256=rVtMUy6dViPLewjDRntmn15QF0bQwiDdQbZZx9j7Drc,341 +networkx/algorithms/flow/__pycache__/__init__.cpython-312.pyc,, +networkx/algorithms/flow/__pycache__/boykovkolmogorov.cpython-312.pyc,, +networkx/algorithms/flow/__pycache__/capacityscaling.cpython-312.pyc,, +networkx/algorithms/flow/__pycache__/dinitz_alg.cpython-312.pyc,, +networkx/algorithms/flow/__pycache__/edmondskarp.cpython-312.pyc,, +networkx/algorithms/flow/__pycache__/gomory_hu.cpython-312.pyc,, +networkx/algorithms/flow/__pycache__/maxflow.cpython-312.pyc,, +networkx/algorithms/flow/__pycache__/mincost.cpython-312.pyc,, +networkx/algorithms/flow/__pycache__/networksimplex.cpython-312.pyc,, +networkx/algorithms/flow/__pycache__/preflowpush.cpython-312.pyc,, +networkx/algorithms/flow/__pycache__/shortestaugmentingpath.cpython-312.pyc,, +networkx/algorithms/flow/__pycache__/utils.cpython-312.pyc,, +networkx/algorithms/flow/boykovkolmogorov.py,sha256=qFcppmiXz4VKKFd4RbDsiWOqJODtDTHbNr9_UFTjQaU,13334 +networkx/algorithms/flow/capacityscaling.py,sha256=8rng2qO5kawNSxq2S8BNlUMmdvNSoC6R8ekiBGU8LxU,14469 +networkx/algorithms/flow/dinitz_alg.py,sha256=I5nnZVsj0aU8-9Cje0umey407epFzpd7BDJpkI6ESK4,8341 +networkx/algorithms/flow/edmondskarp.py,sha256=PEIwLftevS2VYHaTzzZMSOLPy7QSBPsWPedjx1lR6Cs,8056 +networkx/algorithms/flow/gomory_hu.py,sha256=EuibaxPl65shGM9Jxvaa9WMwMmoczDvXXc2b0E81cqM,6345 +networkx/algorithms/flow/maxflow.py,sha256=GuVA4MlPwdOeCdPRXW2QVW1OdJqgzSpPhEyASr302u4,22975 +networkx/algorithms/flow/mincost.py,sha256=GzMYInS4QcNe0yImGrVXJ0bRd7t5TSSMa9jSeenIoOk,12853 +networkx/algorithms/flow/networksimplex.py,sha256=6F1JNT1pMEMt-C27H3PsdZYF-53SrJMrHaakQ8pD7Ng,25098 +networkx/algorithms/flow/preflowpush.py,sha256=CUKZ0-7X9l7P7qH_2n2Immbf8mFm8vocH2SY0tIwjGo,15721 +networkx/algorithms/flow/shortestaugmentingpath.py,sha256=gXXdkY3nH4d0hXVn0P2-kzfC3DHcuCdrudFdxetflKI,10372 +networkx/algorithms/flow/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +networkx/algorithms/flow/tests/__pycache__/__init__.cpython-312.pyc,, +networkx/algorithms/flow/tests/__pycache__/test_gomory_hu.cpython-312.pyc,, +networkx/algorithms/flow/tests/__pycache__/test_maxflow.cpython-312.pyc,, +networkx/algorithms/flow/tests/__pycache__/test_maxflow_large_graph.cpython-312.pyc,, +networkx/algorithms/flow/tests/__pycache__/test_mincost.cpython-312.pyc,, +networkx/algorithms/flow/tests/__pycache__/test_networksimplex.cpython-312.pyc,, +networkx/algorithms/flow/tests/gl1.gpickle.bz2,sha256=z4-BzrXqruFiGqYLiS2D5ZamFz9vZRc1m2ef89qhsPg,44623 +networkx/algorithms/flow/tests/gw1.gpickle.bz2,sha256=b3nw6Q-kxR7HkWXxWWPh7YlHdXbga8qmeuYiwmBBGTE,42248 +networkx/algorithms/flow/tests/netgen-2.gpickle.bz2,sha256=OxfmbN7ajtuNHexyYmx38fZd1GdeP3bcL8T9hKoDjjA,18972 +networkx/algorithms/flow/tests/test_gomory_hu.py,sha256=aWtbI3AHofIK6LDJnmj9UH1QOfulXsi5NyB7bNyV2Vw,4471 +networkx/algorithms/flow/tests/test_maxflow.py,sha256=4CtGOqeyloAxFSajaxPfGuyVhE0R3IdJf2SuIg4kHKQ,18940 +networkx/algorithms/flow/tests/test_maxflow_large_graph.py,sha256=P2e7G8TKN17zrMeOEbSIkLwK08MjfOtNhPva3nLM-B0,4612 +networkx/algorithms/flow/tests/test_mincost.py,sha256=vI61ZCLoWAzwYU4hU0AJS8Ori8vAWEPzFDCUoiloVRk,17806 +networkx/algorithms/flow/tests/test_networksimplex.py,sha256=tCw5C1hLEwbUbt_ySWgkvRyLKj-1T2wfXV9HT4Fx77Q,14162 +networkx/algorithms/flow/tests/wlm3.gpickle.bz2,sha256=zKy6Hg-_swvsNh8OSOyIyZnTR0_Npd35O9RErOF8-g4,88132 +networkx/algorithms/flow/utils.py,sha256=jexiKM-_BQfSAy15f4iZ_Km7DvRrcdOKqi6hxu9X0MM,6246 +networkx/algorithms/graph_hashing.py,sha256=kS881g6vUvMZms856A_tblq3ljB6BanUbt5az37fSlo,16918 +networkx/algorithms/graphical.py,sha256=1NdlhXuGEgUkHPo47EoNTWUMfdeTpiv7BBVM9ty2ivw,15831 +networkx/algorithms/hierarchy.py,sha256=_KFhCF1Afr2TrkPhqx-1PXUXEtfYLhbRShC58ZKbDGE,1786 +networkx/algorithms/hybrid.py,sha256=z3sIFMOpja1wlj-lI8YI6OIbSLZWHr66uSqyVESZWXY,6209 +networkx/algorithms/isolate.py,sha256=8F4GCBHZIW21CZresBlHuZhvq85ruPH_pnlxYetbHuQ,2251 +networkx/algorithms/isomorphism/__init__.py,sha256=gPRQ-_X6xN2lJZPQNw86IVj4NemGmbQYTejf5yJ32N4,406 +networkx/algorithms/isomorphism/__pycache__/__init__.cpython-312.pyc,, +networkx/algorithms/isomorphism/__pycache__/ismags.cpython-312.pyc,, +networkx/algorithms/isomorphism/__pycache__/isomorph.cpython-312.pyc,, +networkx/algorithms/isomorphism/__pycache__/isomorphvf2.cpython-312.pyc,, +networkx/algorithms/isomorphism/__pycache__/matchhelpers.cpython-312.pyc,, +networkx/algorithms/isomorphism/__pycache__/temporalisomorphvf2.cpython-312.pyc,, +networkx/algorithms/isomorphism/__pycache__/tree_isomorphism.cpython-312.pyc,, +networkx/algorithms/isomorphism/__pycache__/vf2pp.cpython-312.pyc,, +networkx/algorithms/isomorphism/__pycache__/vf2userfunc.cpython-312.pyc,, +networkx/algorithms/isomorphism/ismags.py,sha256=AwJKq94d5e_RU0PZs5Gm7D73v-eOBBP8QDkpPxUocd0,60177 +networkx/algorithms/isomorphism/isomorph.py,sha256=O2TZtUPe89CsZxoNchv6FyYsGU79kWrwywGFBeonksE,10561 +networkx/algorithms/isomorphism/isomorphvf2.py,sha256=NTc9uCm2RnR9RxuKsAS_70RWD1zJYCJNPhefcJZUi5U,47637 +networkx/algorithms/isomorphism/matchhelpers.py,sha256=PaZ7PjmNNsJO9KoeRrf9JgcDHIcFr1tZckQc_ol4e9I,10884 +networkx/algorithms/isomorphism/temporalisomorphvf2.py,sha256=-1NW81l8kM9orQ2ni9tcNizQzEhOUE9BaBJXjUWqhiI,10948 +networkx/algorithms/isomorphism/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +networkx/algorithms/isomorphism/tests/__pycache__/__init__.cpython-312.pyc,, +networkx/algorithms/isomorphism/tests/__pycache__/test_ismags.cpython-312.pyc,, +networkx/algorithms/isomorphism/tests/__pycache__/test_isomorphism.cpython-312.pyc,, +networkx/algorithms/isomorphism/tests/__pycache__/test_isomorphvf2.cpython-312.pyc,, +networkx/algorithms/isomorphism/tests/__pycache__/test_match_helpers.cpython-312.pyc,, +networkx/algorithms/isomorphism/tests/__pycache__/test_temporalisomorphvf2.cpython-312.pyc,, +networkx/algorithms/isomorphism/tests/__pycache__/test_tree_isomorphism.cpython-312.pyc,, +networkx/algorithms/isomorphism/tests/__pycache__/test_vf2pp.cpython-312.pyc,, +networkx/algorithms/isomorphism/tests/__pycache__/test_vf2pp_helpers.cpython-312.pyc,, +networkx/algorithms/isomorphism/tests/__pycache__/test_vf2userfunc.cpython-312.pyc,, +networkx/algorithms/isomorphism/tests/iso_r01_s80.A99,sha256=hKzMtYLUR8Oqp9pmJR6RwG7qo31aNPZcnXy4KHDGhqU,1442 +networkx/algorithms/isomorphism/tests/iso_r01_s80.B99,sha256=AHx_W2xG4JEcz1xKoN5TwCHVE6-UO2PiMByynkd4TPE,1442 +networkx/algorithms/isomorphism/tests/si2_b06_m200.A99,sha256=NVnPFA52amNl3qM55G1V9eL9ZlP9NwugBlPf-zekTFU,310 +networkx/algorithms/isomorphism/tests/si2_b06_m200.B99,sha256=-clIDp05LFNRHA2BghhGTeyuXDqBBqA9XpEzpB7Ku7M,1602 +networkx/algorithms/isomorphism/tests/test_ismags.py,sha256=YIrF8xSbnY8zsxiqjSZkzMFXF9TKwZ91m9araTfxJwE,24323 +networkx/algorithms/isomorphism/tests/test_isomorphism.py,sha256=e92l9AcYLKISmXB3hOdKanrvgKlUKuQ0TuXz7xuCD1Q,4576 +networkx/algorithms/isomorphism/tests/test_isomorphvf2.py,sha256=fVZZQgTU_L_kgp6KsiVwy5iLBewVFzXGDl72VQ0CgS0,15303 +networkx/algorithms/isomorphism/tests/test_match_helpers.py,sha256=uuTcvjgf2LPqSQzzECPIh0dezw8-a1IN0u42u8TxwAw,2483 +networkx/algorithms/isomorphism/tests/test_temporalisomorphvf2.py,sha256=k8032J4ItZ4aFHeOraOpiF8y4aPm2O1g44UvUfrQJgg,7343 +networkx/algorithms/isomorphism/tests/test_tree_isomorphism.py,sha256=mZPWRv2sSVJ9PbpGi5MEj5GaFbrp_ha2bL1wre1-Jqo,5681 +networkx/algorithms/isomorphism/tests/test_vf2pp.py,sha256=u_baOdKDdp34CQWwPob8henY90k8_hdK82SQDGesaO0,51386 +networkx/algorithms/isomorphism/tests/test_vf2pp_helpers.py,sha256=A8Y-FcGsLPWvmci5oHHDowSeh-Fhl_y88TrvboPZwdw,90251 +networkx/algorithms/isomorphism/tests/test_vf2userfunc.py,sha256=TrXs7E3Ynva18dBmaJDO4QGsySteCwK32cUKw-WkZ0I,6309 +networkx/algorithms/isomorphism/tree_isomorphism.py,sha256=J4RfMpO5LfL5z_wI3xP2wcbK2vMFEsxAQqBddPdw3YA,9033 +networkx/algorithms/isomorphism/vf2pp.py,sha256=jM3mzJNC0EutWHDmDkKIh1JfsB9XbpTkzEC6qXD0Lpo,36681 +networkx/algorithms/isomorphism/vf2userfunc.py,sha256=HiPwyr7nJF1QS9w69MzKf6wGvO8cgjvdS5vW59iwCew,7371 +networkx/algorithms/link_analysis/__init__.py,sha256=UkcgTDdzsIu-jsJ4jBwP8sF2CsRPC1YcZZT-q5Wlj3I,118 +networkx/algorithms/link_analysis/__pycache__/__init__.cpython-312.pyc,, +networkx/algorithms/link_analysis/__pycache__/hits_alg.cpython-312.pyc,, +networkx/algorithms/link_analysis/__pycache__/pagerank_alg.cpython-312.pyc,, +networkx/algorithms/link_analysis/hits_alg.py,sha256=ot5sEhIvQ-JruYgcqCoi_EvNk89UWx_skG4KayIuO9I,10439 +networkx/algorithms/link_analysis/pagerank_alg.py,sha256=e7HuXXXVCG79YG0iuIOr1wqA_zC60hKoXGlMfvh9k1o,17202 +networkx/algorithms/link_analysis/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +networkx/algorithms/link_analysis/tests/__pycache__/__init__.cpython-312.pyc,, +networkx/algorithms/link_analysis/tests/__pycache__/test_hits.cpython-312.pyc,, +networkx/algorithms/link_analysis/tests/__pycache__/test_pagerank.cpython-312.pyc,, +networkx/algorithms/link_analysis/tests/test_hits.py,sha256=BQ7QHXvJDkXtK-_VDFagtdp_-VXxVerwPymjGjzFWA0,2546 +networkx/algorithms/link_analysis/tests/test_pagerank.py,sha256=rUJNa_2nKDSk75Fg1yygMPAqg_Bdk0gGWMDRKY8g1lk,7282 +networkx/algorithms/link_prediction.py,sha256=UYo_LJgoVXcM1iLMXswM2g4jvUJmvxln3e5bVfXxQ10,22253 +networkx/algorithms/lowest_common_ancestors.py,sha256=D1LgoX_ibv2hR-viKEx6l_qp3mWVCkW6YAlGGdoWgXQ,9286 +networkx/algorithms/matching.py,sha256=Xgi-zy9C2pNvGwTixTFV7ut7W-l8s7zUgJha9v8D0s8,44325 +networkx/algorithms/minors/__init__.py,sha256=ceeKdsZ6U1H40ED-KmtVGkbADxeWMTVG07Ja8P7N_Pg,587 +networkx/algorithms/minors/__pycache__/__init__.cpython-312.pyc,, +networkx/algorithms/minors/__pycache__/contraction.cpython-312.pyc,, +networkx/algorithms/minors/contraction.py,sha256=o5i1UfWpI1KhD4-pVx4yuFJvwi00yJx58Te6GYpys7Y,27606 +networkx/algorithms/minors/tests/__pycache__/test_contraction.cpython-312.pyc,, +networkx/algorithms/minors/tests/test_contraction.py,sha256=31FgiENkElrScHzJrQ3DcKG8W2ieow6zI7PZ9wgVJKE,17674 +networkx/algorithms/mis.py,sha256=BEMv_dW8R6CjMMXJQGIhS4HpS8A8AkLJJWnz3GstuS4,2344 +networkx/algorithms/moral.py,sha256=z5lp42k4kqYk7t_FfszVj5KAC7BxXe6Adik3T2qvA6o,1535 +networkx/algorithms/node_classification.py,sha256=s2yjsHk4mj_6llCuawgGvJ59HyswbcW6ufGQC4UBmG0,6465 +networkx/algorithms/non_randomness.py,sha256=rjIoT7-aqKwGSBNoYxgKniDFgqSDwV8Hh21lkWRYGVE,5787 +networkx/algorithms/operators/__init__.py,sha256=dJ3xOXvHxSzzM3-YcfvjGTJ_ndxULF1TybkIRzUS87Y,201 +networkx/algorithms/operators/__pycache__/__init__.cpython-312.pyc,, +networkx/algorithms/operators/__pycache__/all.cpython-312.pyc,, +networkx/algorithms/operators/__pycache__/binary.cpython-312.pyc,, +networkx/algorithms/operators/__pycache__/product.cpython-312.pyc,, +networkx/algorithms/operators/__pycache__/unary.cpython-312.pyc,, +networkx/algorithms/operators/all.py,sha256=v_W9ZT3u4STNvT4YI9zYn1Z2PDMhqfh4vfhtO4glaIA,9718 +networkx/algorithms/operators/binary.py,sha256=hziSCLDIVIoTWPV56fED5gI1pErOdyLOQ3Z_E_Nz9As,13150 +networkx/algorithms/operators/product.py,sha256=FQkSIduOv-z1ktVzid2T40759S-BmAfTlya88VytuZc,19632 +networkx/algorithms/operators/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +networkx/algorithms/operators/tests/__pycache__/__init__.cpython-312.pyc,, +networkx/algorithms/operators/tests/__pycache__/test_all.cpython-312.pyc,, +networkx/algorithms/operators/tests/__pycache__/test_binary.cpython-312.pyc,, +networkx/algorithms/operators/tests/__pycache__/test_product.cpython-312.pyc,, +networkx/algorithms/operators/tests/__pycache__/test_unary.cpython-312.pyc,, +networkx/algorithms/operators/tests/test_all.py,sha256=Jn8IfNZ1P4Aa51C0gU58S2A-DPksQUFersWco0kG0Yw,8280 +networkx/algorithms/operators/tests/test_binary.py,sha256=BdZQS-qsWbEbRXuWANvH2eXEv7myAKyuldtQzHj6n4Y,12190 +networkx/algorithms/operators/tests/test_product.py,sha256=i4pBb5A4NmaCsllR1XizyhUToaQFMuLZ-JrywkQFdbU,15155 +networkx/algorithms/operators/tests/test_unary.py,sha256=UZdzbt5GI9hnflEizUWXihGqBWmSFJDkzjwVv6wziQE,1415 +networkx/algorithms/operators/unary.py,sha256=Eo2yeTg-F5uODGWSWR_im5VaKZQ97LyATIuKZcAFQR8,1795 +networkx/algorithms/perfect_graph.py,sha256=aMdQkfcXEySVutIOwngHkvDEy1qinO_92uZFJx6Ydnk,2597 +networkx/algorithms/planar_drawing.py,sha256=AXuoT3aFgEtCeMnAaUsRqjxCABdNYZ8Oo9sGOKBQto0,16254 +networkx/algorithms/planarity.py,sha256=nN6QCMz9PJYRqnAa06qEDANdd9bxsbO64PQdbVEGwk4,49887 +networkx/algorithms/polynomials.py,sha256=iP30_mcOlj81Vrzt4iB_ZZxYiRokubs-O1i9RW4pgTw,11278 +networkx/algorithms/reciprocity.py,sha256=1WMhLbSMkVPxRPlfUvbgO5FgVvJHn1doXQF4WuqSLQk,2855 +networkx/algorithms/regular.py,sha256=fEHD4VBREbJwSlR48okVZBuwgvD76GXdT28NitJI8qQ,4877 +networkx/algorithms/richclub.py,sha256=kARzso3M6wnUcAJo2g8ga_ZtigL2czDNzeUDzBtRfqo,4892 +networkx/algorithms/shortest_paths/__init__.py,sha256=Rmxtsje-mPdQyeYhE8TP2NId-iZEOu4eAsWhVRm2Xqk,285 +networkx/algorithms/shortest_paths/__pycache__/__init__.cpython-312.pyc,, +networkx/algorithms/shortest_paths/__pycache__/astar.cpython-312.pyc,, +networkx/algorithms/shortest_paths/__pycache__/dense.cpython-312.pyc,, +networkx/algorithms/shortest_paths/__pycache__/generic.cpython-312.pyc,, +networkx/algorithms/shortest_paths/__pycache__/unweighted.cpython-312.pyc,, +networkx/algorithms/shortest_paths/__pycache__/weighted.cpython-312.pyc,, +networkx/algorithms/shortest_paths/astar.py,sha256=0pEnYNzG7Z86zWqyFe0OL8HTlQO9PwjDABy_ypC30_A,8937 +networkx/algorithms/shortest_paths/dense.py,sha256=5Y8ziU-RsWZRTzxEEzg5gB7f4j9yxbNhplTbLaMliG8,8261 +networkx/algorithms/shortest_paths/generic.py,sha256=-7wIqqDWeScy8brYFkdt68nuxkSgXVs1iXcwGWoBZAU,25337 +networkx/algorithms/shortest_paths/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +networkx/algorithms/shortest_paths/tests/__pycache__/__init__.cpython-312.pyc,, +networkx/algorithms/shortest_paths/tests/__pycache__/test_astar.cpython-312.pyc,, +networkx/algorithms/shortest_paths/tests/__pycache__/test_dense.cpython-312.pyc,, +networkx/algorithms/shortest_paths/tests/__pycache__/test_dense_numpy.cpython-312.pyc,, +networkx/algorithms/shortest_paths/tests/__pycache__/test_generic.cpython-312.pyc,, +networkx/algorithms/shortest_paths/tests/__pycache__/test_unweighted.cpython-312.pyc,, +networkx/algorithms/shortest_paths/tests/__pycache__/test_weighted.cpython-312.pyc,, +networkx/algorithms/shortest_paths/tests/test_astar.py,sha256=F0zXZ7R0eXP8Et4F3mrCtdtfE1-tQTGB2ATcFveDUHI,8989 +networkx/algorithms/shortest_paths/tests/test_dense.py,sha256=ievl4gu3Exl_31hp4OKcsAGPb3g3_xFUM4t3NnvrG_A,6747 +networkx/algorithms/shortest_paths/tests/test_dense_numpy.py,sha256=IEwhjPNTlc2H1on-B3WhFoyLkDkJVdAFBjn675XALHA,2299 +networkx/algorithms/shortest_paths/tests/test_generic.py,sha256=CQJxa5by1xE1a2E8iZm8GAtiPy_DILcrB4dIyWVM_qM,20609 +networkx/algorithms/shortest_paths/tests/test_unweighted.py,sha256=r1F5qVEDZnzPn5yJ71Rp-Z7pq33CzJFfZmDhVBekR9Y,5879 +networkx/algorithms/shortest_paths/tests/test_weighted.py,sha256=dzMts7Y1mMUuz4zEz4adEqYUNoXi9BMf-oyzQ0BMG3s,35502 +networkx/algorithms/shortest_paths/unweighted.py,sha256=iuJAO3WzB1p-fwpDyGM4snuFB5k57KIsVGcAJUhCpi0,17825 +networkx/algorithms/shortest_paths/weighted.py,sha256=PphAp7PmN9qpWzkLudePIVgvySh_SGDi5GhdQN5iufk,84155 +networkx/algorithms/similarity.py,sha256=E8P8vWqCR-r2SFVSqW9Y-Bb4I4R7BzUc_jzhiCYx7tU,72679 +networkx/algorithms/simple_paths.py,sha256=8oG_bRDVRrBKVCDEjZLHW3Qyzrv5KVynp-Hl0m5SNmA,31091 +networkx/algorithms/smallworld.py,sha256=3xT-z2_CVdp5-Ap8vF6fsd3DiavDYtspFNZrcwcpXG0,13565 +networkx/algorithms/smetric.py,sha256=_Aj4BIMnafiXbJtLkvAfAnIEMdI9OcVvMy6kk9KKTns,770 +networkx/algorithms/sparsifiers.py,sha256=4T8pMlh-usEHA2-rZFh-CmZbBY9dcXIHjoqR-oJ2hSw,10048 +networkx/algorithms/structuralholes.py,sha256=rYaVkndSJ3kFxbjkhfQd6uMBpYO7NNlcsOPR0Xbiy7Y,12626 +networkx/algorithms/summarization.py,sha256=CygTsSthyCKHs0ZTZsCgWnyaT8annQbLpUtahmfY9Sw,23251 +networkx/algorithms/swap.py,sha256=NVZMmlnkdxgwwNw5GDrc8waNERcdCu52ydHcBdOA_hw,14744 +networkx/algorithms/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +networkx/algorithms/tests/__pycache__/__init__.cpython-312.pyc,, +networkx/algorithms/tests/__pycache__/test_asteroidal.cpython-312.pyc,, +networkx/algorithms/tests/__pycache__/test_boundary.cpython-312.pyc,, +networkx/algorithms/tests/__pycache__/test_bridges.cpython-312.pyc,, +networkx/algorithms/tests/__pycache__/test_broadcasting.cpython-312.pyc,, +networkx/algorithms/tests/__pycache__/test_chains.cpython-312.pyc,, +networkx/algorithms/tests/__pycache__/test_chordal.cpython-312.pyc,, +networkx/algorithms/tests/__pycache__/test_clique.cpython-312.pyc,, +networkx/algorithms/tests/__pycache__/test_cluster.cpython-312.pyc,, +networkx/algorithms/tests/__pycache__/test_communicability.cpython-312.pyc,, +networkx/algorithms/tests/__pycache__/test_core.cpython-312.pyc,, +networkx/algorithms/tests/__pycache__/test_covering.cpython-312.pyc,, +networkx/algorithms/tests/__pycache__/test_cuts.cpython-312.pyc,, +networkx/algorithms/tests/__pycache__/test_cycles.cpython-312.pyc,, +networkx/algorithms/tests/__pycache__/test_d_separation.cpython-312.pyc,, +networkx/algorithms/tests/__pycache__/test_dag.cpython-312.pyc,, +networkx/algorithms/tests/__pycache__/test_distance_measures.cpython-312.pyc,, +networkx/algorithms/tests/__pycache__/test_distance_regular.cpython-312.pyc,, +networkx/algorithms/tests/__pycache__/test_dominance.cpython-312.pyc,, +networkx/algorithms/tests/__pycache__/test_dominating.cpython-312.pyc,, +networkx/algorithms/tests/__pycache__/test_efficiency.cpython-312.pyc,, +networkx/algorithms/tests/__pycache__/test_euler.cpython-312.pyc,, +networkx/algorithms/tests/__pycache__/test_graph_hashing.cpython-312.pyc,, +networkx/algorithms/tests/__pycache__/test_graphical.cpython-312.pyc,, +networkx/algorithms/tests/__pycache__/test_hierarchy.cpython-312.pyc,, +networkx/algorithms/tests/__pycache__/test_hybrid.cpython-312.pyc,, +networkx/algorithms/tests/__pycache__/test_isolate.cpython-312.pyc,, +networkx/algorithms/tests/__pycache__/test_link_prediction.cpython-312.pyc,, +networkx/algorithms/tests/__pycache__/test_lowest_common_ancestors.cpython-312.pyc,, +networkx/algorithms/tests/__pycache__/test_matching.cpython-312.pyc,, +networkx/algorithms/tests/__pycache__/test_max_weight_clique.cpython-312.pyc,, +networkx/algorithms/tests/__pycache__/test_mis.cpython-312.pyc,, +networkx/algorithms/tests/__pycache__/test_moral.cpython-312.pyc,, +networkx/algorithms/tests/__pycache__/test_node_classification.cpython-312.pyc,, +networkx/algorithms/tests/__pycache__/test_non_randomness.cpython-312.pyc,, +networkx/algorithms/tests/__pycache__/test_perfect_graph.cpython-312.pyc,, +networkx/algorithms/tests/__pycache__/test_planar_drawing.cpython-312.pyc,, +networkx/algorithms/tests/__pycache__/test_planarity.cpython-312.pyc,, +networkx/algorithms/tests/__pycache__/test_polynomials.cpython-312.pyc,, +networkx/algorithms/tests/__pycache__/test_reciprocity.cpython-312.pyc,, +networkx/algorithms/tests/__pycache__/test_regular.cpython-312.pyc,, +networkx/algorithms/tests/__pycache__/test_richclub.cpython-312.pyc,, +networkx/algorithms/tests/__pycache__/test_similarity.cpython-312.pyc,, +networkx/algorithms/tests/__pycache__/test_simple_paths.cpython-312.pyc,, +networkx/algorithms/tests/__pycache__/test_smallworld.cpython-312.pyc,, +networkx/algorithms/tests/__pycache__/test_smetric.cpython-312.pyc,, +networkx/algorithms/tests/__pycache__/test_sparsifiers.cpython-312.pyc,, +networkx/algorithms/tests/__pycache__/test_structuralholes.cpython-312.pyc,, +networkx/algorithms/tests/__pycache__/test_summarization.cpython-312.pyc,, +networkx/algorithms/tests/__pycache__/test_swap.cpython-312.pyc,, +networkx/algorithms/tests/__pycache__/test_threshold.cpython-312.pyc,, +networkx/algorithms/tests/__pycache__/test_time_dependent.cpython-312.pyc,, +networkx/algorithms/tests/__pycache__/test_tournament.cpython-312.pyc,, +networkx/algorithms/tests/__pycache__/test_triads.cpython-312.pyc,, +networkx/algorithms/tests/__pycache__/test_vitality.cpython-312.pyc,, +networkx/algorithms/tests/__pycache__/test_voronoi.cpython-312.pyc,, +networkx/algorithms/tests/__pycache__/test_walks.cpython-312.pyc,, +networkx/algorithms/tests/__pycache__/test_wiener.cpython-312.pyc,, +networkx/algorithms/tests/test_asteroidal.py,sha256=DnWI5_jnaaZMxtG44XD0K690HZs8ez7HU_9dSR-p6eA,502 +networkx/algorithms/tests/test_boundary.py,sha256=1OSJh32FYFhAVYB5zqxhZGEXZLS0HPp9kvfHZvWmD3o,6227 +networkx/algorithms/tests/test_bridges.py,sha256=jSCguECho0GNHnu0vpRh1twyfGP6tWFcaYL1rgvc8mU,4026 +networkx/algorithms/tests/test_broadcasting.py,sha256=GqjkpHC5Z7ogZZdf1qsz5B1lmX-Pd5pFMD5e3QRPAkI,3034 +networkx/algorithms/tests/test_chains.py,sha256=4fR5Uh2NMrTZGT1yR6sAsQEP3uy1U3UnwjcXLFk9d1Y,4196 +networkx/algorithms/tests/test_chordal.py,sha256=DPdNPY7KtqCsCwYVb4xQfnIm-z35dUJIWxNHtAiQLAQ,4438 +networkx/algorithms/tests/test_clique.py,sha256=RX1Q38DRDmclLLJ04pfE5RIFAy86EuTZ2QAERrtkjS4,9772 +networkx/algorithms/tests/test_cluster.py,sha256=b0OXtKvUYGzDxBrnIbPh8A3vo3jmRYOFzW02MVSWYgo,19320 +networkx/algorithms/tests/test_communicability.py,sha256=4KK9wU9gAUqHAAAyHwAKpq2dV9g415s_X0qd7Tt83gU,2938 +networkx/algorithms/tests/test_core.py,sha256=Q3bi_c0LpxZ-0VopHU6Lyzkb_JKseVT16H45qDuax9A,9619 +networkx/algorithms/tests/test_covering.py,sha256=EeBjQ5mxcVctgavqXZ255T8ryFocuxjxdVpIxVUNFvw,2718 +networkx/algorithms/tests/test_cuts.py,sha256=gKm9VDtnmwFli6kgwV1ktEFI_rw84p2Sg02Em6SoW5Q,5376 +networkx/algorithms/tests/test_cycles.py,sha256=CBEQ_kNlHg5PHb9vKMS9ot-lqUuvs_cAgaPtnEQn76g,34851 +networkx/algorithms/tests/test_d_separation.py,sha256=cxoAOA_EahXQHFsFaRopz6cp3h5sMnD7D_isq4_EuWI,10714 +networkx/algorithms/tests/test_dag.py,sha256=sd6OlsrK57rY7x6vMfWSf11tEiKH5BakNp7747KvCnA,29407 +networkx/algorithms/tests/test_distance_measures.py,sha256=kF-pR5WbVBs1d_IO-U6Dr-sf8MaRHfxc_D286sBN62E,28793 +networkx/algorithms/tests/test_distance_regular.py,sha256=w27OTUtAI0VQv7cikkOdJg4bo4q7xTNIVE8nbU_x7b8,2915 +networkx/algorithms/tests/test_dominance.py,sha256=rD-m7LVQpqrOTCUJrli43lYpezYxajfkcNd3xvGhTj8,9811 +networkx/algorithms/tests/test_dominating.py,sha256=5WwPlrQ6_pFaVx_-K4D5cHNbCZL23qp-PytgsXDuS4Q,3112 +networkx/algorithms/tests/test_efficiency.py,sha256=QKWMvyjCG1Byt-oNp7Rz_qxnVeT77Zk27lrzI1qH0mA,1894 +networkx/algorithms/tests/test_euler.py,sha256=L4L1ljHVxQxjQQludO2r6k3UZU7WAY_N6WYUjFx1fEk,11209 +networkx/algorithms/tests/test_graph_hashing.py,sha256=ribxC8ZkNxkkBDyjSZ8P2f8J6y5y4g-Ik-k5hBgZcx4,31567 +networkx/algorithms/tests/test_graphical.py,sha256=uhFjvs04odxABToY4IRig_CaUTpAC3SfZRu1p1T7FwY,5366 +networkx/algorithms/tests/test_hierarchy.py,sha256=uW8DqCdXiAeypkNPKcAYX7aW86CawYH84Q0bW4cDTXo,1184 +networkx/algorithms/tests/test_hybrid.py,sha256=kQLzaMoqZcKFaJ3D7PKbY2O-FX59XDZ1pN5un8My-tk,720 +networkx/algorithms/tests/test_isolate.py,sha256=LyR0YYHJDH5vppQzGzGiJK-aaIV17_Jmla8dMf93olg,555 +networkx/algorithms/tests/test_link_prediction.py,sha256=lP6slSHwXdzdJzNSeMTUsP88ypSqe6EeVy-bUP2d-48,20451 +networkx/algorithms/tests/test_lowest_common_ancestors.py,sha256=5ZT_17q-5ipw7NNgNisaVcxIxrY6XeQQaUlBXHx9k9g,14160 +networkx/algorithms/tests/test_matching.py,sha256=1IKeUi49HR8aAOm2V4cmBL2NGXvF9cb8mqW9jnkNGuY,18319 +networkx/algorithms/tests/test_max_weight_clique.py,sha256=M1eoy8OtuQVZkEvNMauV9vqR6hHtOCrtq6INv2qzMyA,6739 +networkx/algorithms/tests/test_mis.py,sha256=Z2tKoqbs-AFPzEBDYO7S8U-F7usLfZJ2l6j2DpZUts4,1865 +networkx/algorithms/tests/test_moral.py,sha256=15PZgkx7O9aXQB1npQ2JNqBBkEqPPP2RfeZzKqY-GNU,452 +networkx/algorithms/tests/test_node_classification.py,sha256=NgJJKUHH1GoD1GE3F4QRYBLM3fUo_En3RNtZvhqCjlg,4663 +networkx/algorithms/tests/test_non_randomness.py,sha256=iDHCFzFvxGXCi_vWnv-DkXHcmO9uU6KVpYGMCpHKQG8,1678 +networkx/algorithms/tests/test_perfect_graph.py,sha256=Y2mpreh5TNCkqn4PDO57MFkgYozt-tlZFScKH3TZJRg,609 +networkx/algorithms/tests/test_planar_drawing.py,sha256=fBTd9JzDkTqNQz7GBQCS9lShU1P3lWRQ7L-E5WhSdlA,8765 +networkx/algorithms/tests/test_planarity.py,sha256=2iy22H4APB2K0DiPcoWmiQV9BTQlJ9JSOexubN14sU0,17312 +networkx/algorithms/tests/test_polynomials.py,sha256=baI0Kua1pRngRC6Scm5gRRwi1bl0iET5_Xxo3AZTP3A,1983 +networkx/algorithms/tests/test_reciprocity.py,sha256=X_PXWFOTzuEcyMWpRdwEJfm8lJOfNE_1rb9AAybf4is,1296 +networkx/algorithms/tests/test_regular.py,sha256=ppXES6zvpsppoZmzXPFim-S4ZMWPG2Zhhmlgc67y_GA,2951 +networkx/algorithms/tests/test_richclub.py,sha256=Wl8aE0CSswaXJiHCQ42IkyRvjWaJQZ2OQcbC7LpIgfo,3962 +networkx/algorithms/tests/test_similarity.py,sha256=l02q6PMnweng8EfUm6Z0yor-Uy-zd6WN0Tm28lh3pBU,41248 +networkx/algorithms/tests/test_simple_paths.py,sha256=7U9wCXz4SHK0XeYrs1k2KjYgrYVQDnts2ggQLzU18p0,25181 +networkx/algorithms/tests/test_smallworld.py,sha256=bmoNDxIuhV0bBKWh2hgFw0XYTPCc2rgg1y77uMg6rE8,2390 +networkx/algorithms/tests/test_smetric.py,sha256=VM14L4X1AABvINDL9qKXzlech_Q2g4Aee-ozWM2Qrr4,144 +networkx/algorithms/tests/test_sparsifiers.py,sha256=1GRbQy1vfmwv6eUhP4Io0aykH2VyTJfFWmncrXmTqi4,4044 +networkx/algorithms/tests/test_structuralholes.py,sha256=F0xZ1nlKJLGmQDebDiExju4fMffu79qThnyF7ZGA53A,7592 +networkx/algorithms/tests/test_summarization.py,sha256=uNyaUstobIEu6M_Hexik-3YiYTRSy_XO6LUqoE4wazw,21312 +networkx/algorithms/tests/test_swap.py,sha256=WJtGMkSbAd1Cv06VaUeDVHosNOtdigsqEspyux0ExCs,6144 +networkx/algorithms/tests/test_threshold.py,sha256=x0hqqbH65nbDNEUV0vYz0J2TSmmBbJ0daEgLIR7CRm0,9733 +networkx/algorithms/tests/test_time_dependent.py,sha256=NmuV2kDo4nh2MeN0hwcJf0QSDtqMD0dfSeeKSsYBtQ8,13342 +networkx/algorithms/tests/test_tournament.py,sha256=tfOPwN7YHnw0oxl9d7-ErPBFKz6KBfSc5S3tbGrGHs8,4107 +networkx/algorithms/tests/test_triads.py,sha256=CbnFABCMDU0X1mzu3tzW4-WL2qn5ptaDTOShNaTjIwk,8219 +networkx/algorithms/tests/test_vitality.py,sha256=p5lPWCtVMtbvxDw6TJUaf8vpb0zKPoz5pND722xiypQ,1380 +networkx/algorithms/tests/test_voronoi.py,sha256=M4B6JtkJUw56ULEWRs1kyVEUsroNrnb5FBq9OioAyHM,3477 +networkx/algorithms/tests/test_walks.py,sha256=X8cb-YvGHiiqbMEXuKMSdTAb9WtVtbHjIESNSqpJTmU,1499 +networkx/algorithms/tests/test_wiener.py,sha256=uT6Es_OFbJqxnVY7lImzlJSRTQS4eJnMaKotKbObMd8,4186 +networkx/algorithms/threshold.py,sha256=maOMixTxDibvBL2BkVcaYz1Co_09jg8d7DpAW2blNnY,30776 +networkx/algorithms/time_dependent.py,sha256=PAeJ7Yt8kUqbDgvBaz_ZfUFZg-w-vf1gPC0HO6go_TI,5762 +networkx/algorithms/tournament.py,sha256=TyWD0P-8JYXsckulbyr-bkKOoc0gLUHK59R1RQm2We4,11613 +networkx/algorithms/traversal/__init__.py,sha256=YtFrfNjciqTOI6jGePQaJ01tRSEQXTHqTGGNhDEDb_8,142 +networkx/algorithms/traversal/__pycache__/__init__.cpython-312.pyc,, +networkx/algorithms/traversal/__pycache__/beamsearch.cpython-312.pyc,, +networkx/algorithms/traversal/__pycache__/breadth_first_search.cpython-312.pyc,, +networkx/algorithms/traversal/__pycache__/depth_first_search.cpython-312.pyc,, +networkx/algorithms/traversal/__pycache__/edgebfs.cpython-312.pyc,, +networkx/algorithms/traversal/__pycache__/edgedfs.cpython-312.pyc,, +networkx/algorithms/traversal/beamsearch.py,sha256=Vn0U4Wck8ICShIAGggv3tVtQWVW0ABEz_hcBsGrql6o,3473 +networkx/algorithms/traversal/breadth_first_search.py,sha256=KGpFnXuCQrDwcQg27Irtrqu7hcIWlFSlVWGpA26XbHA,18259 +networkx/algorithms/traversal/depth_first_search.py,sha256=2V4T3tGujcAtV3W6WcTQUjGAAe3b1rqinONowUhLsa8,16795 +networkx/algorithms/traversal/edgebfs.py,sha256=JJ1mQCmv8WydqQtIVQC1HqkqEFDMeKVzJ_hexwU8sXA,6333 +networkx/algorithms/traversal/edgedfs.py,sha256=T1xHGr8p_eC1pEUyCP-kpaYlIY_la_ms-J0-50c4EDk,6041 +networkx/algorithms/traversal/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +networkx/algorithms/traversal/tests/__pycache__/__init__.cpython-312.pyc,, +networkx/algorithms/traversal/tests/__pycache__/test_beamsearch.cpython-312.pyc,, +networkx/algorithms/traversal/tests/__pycache__/test_bfs.cpython-312.pyc,, +networkx/algorithms/traversal/tests/__pycache__/test_dfs.cpython-312.pyc,, +networkx/algorithms/traversal/tests/__pycache__/test_edgebfs.cpython-312.pyc,, +networkx/algorithms/traversal/tests/__pycache__/test_edgedfs.cpython-312.pyc,, +networkx/algorithms/traversal/tests/test_beamsearch.py,sha256=bzUcswZ1qo0ecDZYSER_4enbsW6SjTpb_3Nb3fqmkAo,900 +networkx/algorithms/traversal/tests/test_bfs.py,sha256=tGQSiYhc6BxaZEuw-G3ZxVfYLN7q0zrP_BeKl6EfxkU,6447 +networkx/algorithms/traversal/tests/test_dfs.py,sha256=rQMq7GToM6CVU3WwIZfmqa-HrqlmvIT1DryRU7NbU_U,10580 +networkx/algorithms/traversal/tests/test_edgebfs.py,sha256=8oplCu0fct3QipT0JB0-292EA2aOm8zWlMkPedfe6iY,4702 +networkx/algorithms/traversal/tests/test_edgedfs.py,sha256=HGmC3GUYSn9XLMHQpdefdE6g-Uh3KqbmgEEXBcckdYc,4775 +networkx/algorithms/tree/__init__.py,sha256=wOh_v70XEdFHL2qUMje7qkJI5RcjFNrDremJYl4zzkg,182 +networkx/algorithms/tree/__pycache__/__init__.cpython-312.pyc,, +networkx/algorithms/tree/__pycache__/branchings.cpython-312.pyc,, +networkx/algorithms/tree/__pycache__/coding.cpython-312.pyc,, +networkx/algorithms/tree/__pycache__/decomposition.cpython-312.pyc,, +networkx/algorithms/tree/__pycache__/distance_measures.cpython-312.pyc,, +networkx/algorithms/tree/__pycache__/mst.cpython-312.pyc,, +networkx/algorithms/tree/__pycache__/operations.cpython-312.pyc,, +networkx/algorithms/tree/__pycache__/recognition.cpython-312.pyc,, +networkx/algorithms/tree/branchings.py,sha256=B0c_uKpcnV2SwJMZJRK0BMEz8LkvIcOhv1y0AI0gTnY,34339 +networkx/algorithms/tree/coding.py,sha256=l9P4jhmhg_9uYBVMNSmLYW8btHJH07-Sl2SIKJtO1LA,13466 +networkx/algorithms/tree/decomposition.py,sha256=lY_rqx9JxnLEkp1wiAv0mX62PGPwGQ6SW4Jp48o8aiw,3071 +networkx/algorithms/tree/distance_measures.py,sha256=Dwtk2BRTXmEGjFFtpiAbk02HrX2Hb5kjXusfwn6nm9M,6981 +networkx/algorithms/tree/mst.py,sha256=UMeSnnpKrPeq7a_w_tR415w9TOLltmzuXyIy_HfqbAU,46145 +networkx/algorithms/tree/operations.py,sha256=TjfLQpxwlaLgdOnhLkdzHfZ68yFCYz6BMzzh_d_2mJc,4048 +networkx/algorithms/tree/recognition.py,sha256=bYnaDN0ZaIWTgq0tbPEHAcdxQBWZpDvWypZarBbA334,7569 +networkx/algorithms/tree/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +networkx/algorithms/tree/tests/__pycache__/__init__.cpython-312.pyc,, +networkx/algorithms/tree/tests/__pycache__/test_branchings.cpython-312.pyc,, +networkx/algorithms/tree/tests/__pycache__/test_coding.cpython-312.pyc,, +networkx/algorithms/tree/tests/__pycache__/test_decomposition.cpython-312.pyc,, +networkx/algorithms/tree/tests/__pycache__/test_distance_measures.cpython-312.pyc,, +networkx/algorithms/tree/tests/__pycache__/test_mst.cpython-312.pyc,, +networkx/algorithms/tree/tests/__pycache__/test_operations.cpython-312.pyc,, +networkx/algorithms/tree/tests/__pycache__/test_recognition.cpython-312.pyc,, +networkx/algorithms/tree/tests/test_branchings.py,sha256=euky-3tFOV5CVV5v_yFawuVGCaqTuTrKtBIkCygwhiA,17731 +networkx/algorithms/tree/tests/test_coding.py,sha256=XC6SbfA2zVGH4FyJJyv6o8eOnBu7FNzNot3SKs7QmEo,3955 +networkx/algorithms/tree/tests/test_decomposition.py,sha256=vnl_xoQzi1LnlZL25vXOZWwvaWmon3-x222OKt4eDqE,1871 +networkx/algorithms/tree/tests/test_distance_measures.py,sha256=WNzqjgDuFLUt_NgjD4R8gGhbYhQ3F5a4HZfBBipunv0,3389 +networkx/algorithms/tree/tests/test_mst.py,sha256=D3o217cjuUtf0TxZ9ks1wqQ4cpwVySoOBYfg0Jmr1AU,32376 +networkx/algorithms/tree/tests/test_operations.py,sha256=ybU96kROTVJRTyjLG7JSJjYlPxaWmYjUVJqbXV5VGGI,1961 +networkx/algorithms/tree/tests/test_recognition.py,sha256=qeMEIvg-j2MqaU-TNIQhCcXxao8vTBy0wjpU7jr2iw8,4521 +networkx/algorithms/triads.py,sha256=FqOtB5fi40k4EoA_-bV1TBwimsRihAqrVdFyNnq0hns,14233 +networkx/algorithms/vitality.py,sha256=8M1cubIydO49El2kwVCURHZ2UwCtfGVFeGS8-JYt1ko,2289 +networkx/algorithms/voronoi.py,sha256=07SnSpxLDz4k6K59Jo-VTNA-Qy5knaHfBC-y_5vAOLQ,3183 +networkx/algorithms/walks.py,sha256=5A611YwX9D8IZYbFcQ4aT3cuKZWHq1QOzCpy4mAM6XE,2312 +networkx/algorithms/wiener.py,sha256=88sTSCXt6gakAmxP4BD_ir1R-_0NOUxXlyZKeElzzvs,9221 +networkx/classes/__init__.py,sha256=Q9oONJrnTFs874SGpwcbV_kyJTDcrLI69GFt99MiE6I,364 +networkx/classes/__pycache__/__init__.cpython-312.pyc,, +networkx/classes/__pycache__/coreviews.cpython-312.pyc,, +networkx/classes/__pycache__/digraph.cpython-312.pyc,, +networkx/classes/__pycache__/filters.cpython-312.pyc,, +networkx/classes/__pycache__/function.cpython-312.pyc,, +networkx/classes/__pycache__/graph.cpython-312.pyc,, +networkx/classes/__pycache__/graphviews.cpython-312.pyc,, +networkx/classes/__pycache__/multidigraph.cpython-312.pyc,, +networkx/classes/__pycache__/multigraph.cpython-312.pyc,, +networkx/classes/__pycache__/reportviews.cpython-312.pyc,, +networkx/classes/coreviews.py,sha256=5Dgi8PjMZJz_Uun2SXL5a6mxtAfdbm3kPIdbgzSTXgM,13251 +networkx/classes/digraph.py,sha256=ys8hmrqUjto00TDwBNCwiaNAahFbizNvKkkx84BxS30,48781 +networkx/classes/filters.py,sha256=PCy7BsoIby8VcamqDjZQiNAe_5egI0WKUq-y5nc9unQ,2817 +networkx/classes/function.py,sha256=pPpy-TgC33yNX3nPMZoSjfVmaiiSTbfwYJJMFGa35hQ,43794 +networkx/classes/graph.py,sha256=bY5x7WQHoQxSZow95OUAmU8UZaYV0ZWQvhrEFjSAt0Y,72337 +networkx/classes/graphviews.py,sha256=ulUTLozEK_hj_4TGHdgvxveR2-rb92Q14jjxH4oH4Go,8520 +networkx/classes/multidigraph.py,sha256=qBhb2eoiMejz5fkv-ihLsqwQKKYnUjUANHnrWZKYQfI,37036 +networkx/classes/multigraph.py,sha256=7SD8_ZT7yE1EfIotDzIgITSj4HkKLxZfj6ZnWjQ7hIA,47931 +networkx/classes/reportviews.py,sha256=u0hNZqaWXCfLMP_lq835XCIVStkZQJ9HaQPeDPPoo88,46132 +networkx/classes/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +networkx/classes/tests/__pycache__/__init__.cpython-312.pyc,, +networkx/classes/tests/__pycache__/dispatch_interface.cpython-312.pyc,, +networkx/classes/tests/__pycache__/historical_tests.cpython-312.pyc,, +networkx/classes/tests/__pycache__/test_coreviews.cpython-312.pyc,, +networkx/classes/tests/__pycache__/test_digraph.cpython-312.pyc,, +networkx/classes/tests/__pycache__/test_digraph_historical.cpython-312.pyc,, +networkx/classes/tests/__pycache__/test_filters.cpython-312.pyc,, +networkx/classes/tests/__pycache__/test_function.cpython-312.pyc,, +networkx/classes/tests/__pycache__/test_graph.cpython-312.pyc,, +networkx/classes/tests/__pycache__/test_graph_historical.cpython-312.pyc,, +networkx/classes/tests/__pycache__/test_graphviews.cpython-312.pyc,, +networkx/classes/tests/__pycache__/test_multidigraph.cpython-312.pyc,, +networkx/classes/tests/__pycache__/test_multigraph.cpython-312.pyc,, +networkx/classes/tests/__pycache__/test_reportviews.cpython-312.pyc,, +networkx/classes/tests/__pycache__/test_special.cpython-312.pyc,, +networkx/classes/tests/__pycache__/test_subgraphviews.cpython-312.pyc,, +networkx/classes/tests/dispatch_interface.py,sha256=gffiIwS4OZWKIgRkfvghixQds5hY0tPBi86p0lpV7eY,6823 +networkx/classes/tests/historical_tests.py,sha256=oUJSS-QhvInhad9AJCXCCk0x7OzAclIF9RjBSZ6gB58,16291 +networkx/classes/tests/test_coreviews.py,sha256=qzdozzWK8vLag-CAUqrXAM2CZZwMFN5vMu6Tdrwdf-E,12128 +networkx/classes/tests/test_digraph.py,sha256=uw0FuEu3y_YI-PSGuQCRytFpXLF7Eye2fqLJaKbXkBc,12283 +networkx/classes/tests/test_digraph_historical.py,sha256=8TbDcvmLgfllfGomTRStSpT5WIQiTHLt-rS3GyJEItU,3668 +networkx/classes/tests/test_filters.py,sha256=fBLig8z548gsBBlQw6VJdGZb4IcqJj7_0mi2Fd2ncEM,5851 +networkx/classes/tests/test_function.py,sha256=PDtKrB31Ix1-abPc8NzuP-h7HOOIRtkL5bvcOE8mshU,35481 +networkx/classes/tests/test_graph.py,sha256=ST12bvoj72uzUDpnyVz-Zx0mlXV2CegcEg3txT5qS6k,31798 +networkx/classes/tests/test_graph_historical.py,sha256=EVAndjelyyZ9laQCTrj6KR6lhdmG8Y1raP2IrxkZxdA,258 +networkx/classes/tests/test_graphviews.py,sha256=JoTS_ZY7u4dBHEttD55WT1pD47FcvELBEzw-uJQiqv4,11496 +networkx/classes/tests/test_multidigraph.py,sha256=ryTKegCoYixXbAqOn3mIt9vSMb5666Dv-pfMkXEjoUE,16342 +networkx/classes/tests/test_multigraph.py,sha256=0vFQO3RCJaBpzXvnQzdWa_qYLHNo_I9DICYhPZJNUMk,18777 +networkx/classes/tests/test_reportviews.py,sha256=MgP9_cKlw4aTU5m1XbsNTM3ZjmO7roOOf-7x-Hn9xKs,41332 +networkx/classes/tests/test_special.py,sha256=wl-Idzdc55eOQ4Tn5SNqOG21AjwLb2TTp9x102Qx3Ms,4053 +networkx/classes/tests/test_subgraphviews.py,sha256=iIMkbHdtJsgDlcdphhE_uUTJ0VGTGFzKIVvMd7oamoU,13669 +networkx/conftest.py,sha256=5-BwqVMgfaZyRZICFpG1plLMJ2G1IchSQVEuRjoBtyc,8041 +networkx/convert.py,sha256=Xmkhacuyw9CDviBTZPlH6nmD-JabSk0aPcVmGgp4LeY,16143 +networkx/convert_matrix.py,sha256=9BXSEXzCEIte1nx8f35cWhXo-EVYjOkuW4H3Jutq-oE,45342 +networkx/drawing/__init__.py,sha256=rnTFNzLc4fis1hTAEpnWTC80neAR88-llVQ-LObN-i4,160 +networkx/drawing/__pycache__/__init__.cpython-312.pyc,, +networkx/drawing/__pycache__/layout.cpython-312.pyc,, +networkx/drawing/__pycache__/nx_agraph.cpython-312.pyc,, +networkx/drawing/__pycache__/nx_latex.cpython-312.pyc,, +networkx/drawing/__pycache__/nx_pydot.cpython-312.pyc,, +networkx/drawing/__pycache__/nx_pylab.cpython-312.pyc,, +networkx/drawing/layout.py,sha256=5MUr2NTp4L3IbOShTb_m7fD4SH1HPZKJPJ4G1mJC3Js,66346 +networkx/drawing/nx_agraph.py,sha256=4Vd8JQH4BQ7BaRO8TmYlqBKmFPm8e2XcDDJiqFkNMxM,14248 +networkx/drawing/nx_latex.py,sha256=PySHixo2YhGV0xbKLroaX-_XcI1L5u8IF8nP6xdV2NU,24846 +networkx/drawing/nx_pydot.py,sha256=9KbNVrVcU5gFmo1GPpB_UMBXcD1tnBYDBzxHvgg6mOs,9992 +networkx/drawing/nx_pylab.py,sha256=MHoDvYE4FO9kJRogqeB1FfqrStyLcjQTq3fr67vAjOE,103488 +networkx/drawing/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +networkx/drawing/tests/__pycache__/__init__.cpython-312.pyc,, +networkx/drawing/tests/__pycache__/test_agraph.cpython-312.pyc,, +networkx/drawing/tests/__pycache__/test_image_comparison_pylab_mpl.cpython-312.pyc,, +networkx/drawing/tests/__pycache__/test_latex.cpython-312.pyc,, +networkx/drawing/tests/__pycache__/test_layout.cpython-312.pyc,, +networkx/drawing/tests/__pycache__/test_pydot.cpython-312.pyc,, +networkx/drawing/tests/__pycache__/test_pylab.cpython-312.pyc,, +networkx/drawing/tests/baseline/test_display_complex.png,sha256=3YPN6JEuyq1KEpRgSnH-2yECI81frU_5fw0VxHlwo9A,142315 +networkx/drawing/tests/baseline/test_display_empty_graph.png,sha256=XsOkBCZlt-55c40ElwEapBENVfmdc4TZKIF1ufs5Dzk,3257 +networkx/drawing/tests/baseline/test_display_house_with_colors.png,sha256=uiEx6_2RddHG-516OsRVM0AG7M5CGiAo4tpDgbgPKIU,26002 +networkx/drawing/tests/baseline/test_display_labels_and_colors.png,sha256=RfA1OtWYKttP5_LDScIcx8MFbC-_5wljXeDsZAqAqDU,62571 +networkx/drawing/tests/baseline/test_display_shortest_path.png,sha256=6W7EMitF8jojJ8epmaYEWpjhYRlzBlL7QV98MStxvvQ,35264 +networkx/drawing/tests/baseline/test_house_with_colors.png,sha256=FQi9pIRFwjq4gvgB8cDdBHL5euQUJFw6sQlABf2kRVo,21918 +networkx/drawing/tests/test_agraph.py,sha256=5cbqLGEgJmAocWOMSikv_9ykRWfBymjGfalvKbR1_KE,8725 +networkx/drawing/tests/test_image_comparison_pylab_mpl.py,sha256=2UtGMJFpcWiG0sQtXIYT0j03Q2LhGqWM2G-pfKY3Sw0,6340 +networkx/drawing/tests/test_latex.py,sha256=YQjLxsZnx7_CX2sJ3nN0cUiJzC7p6UhyFN6ufbox5G0,8621 +networkx/drawing/tests/test_layout.py,sha256=2_wJfBcCcHwBm1JRrQiLMcnNWoaURX6F4LFcn5OPul4,23709 +networkx/drawing/tests/test_pydot.py,sha256=X9b66gWqMgdTEyRJ7Zmy5kL9cr22waI688K9BJUf4Bk,4973 +networkx/drawing/tests/test_pylab.py,sha256=TMGpM6IKm5vTSu9Dbfm8mvyhMqz2aAu1gafGbLiOU1k,54825 +networkx/exception.py,sha256=hC8efPfIzOFo0jiWiQbTPaNKuNTuUwhp9RPw--pdv4U,3787 +networkx/generators/__init__.py,sha256=EoYB5c5ZE4rsNKZvl1TRQy2Vo2D3T2H-YunyD2i6sa0,1366 +networkx/generators/__pycache__/__init__.cpython-312.pyc,, +networkx/generators/__pycache__/atlas.cpython-312.pyc,, +networkx/generators/__pycache__/classic.cpython-312.pyc,, +networkx/generators/__pycache__/cographs.cpython-312.pyc,, +networkx/generators/__pycache__/community.cpython-312.pyc,, +networkx/generators/__pycache__/degree_seq.cpython-312.pyc,, +networkx/generators/__pycache__/directed.cpython-312.pyc,, +networkx/generators/__pycache__/duplication.cpython-312.pyc,, +networkx/generators/__pycache__/ego.cpython-312.pyc,, +networkx/generators/__pycache__/expanders.cpython-312.pyc,, +networkx/generators/__pycache__/geometric.cpython-312.pyc,, +networkx/generators/__pycache__/harary_graph.cpython-312.pyc,, +networkx/generators/__pycache__/internet_as_graphs.cpython-312.pyc,, +networkx/generators/__pycache__/intersection.cpython-312.pyc,, +networkx/generators/__pycache__/interval_graph.cpython-312.pyc,, +networkx/generators/__pycache__/joint_degree_seq.cpython-312.pyc,, +networkx/generators/__pycache__/lattice.cpython-312.pyc,, +networkx/generators/__pycache__/line.cpython-312.pyc,, +networkx/generators/__pycache__/mycielski.cpython-312.pyc,, +networkx/generators/__pycache__/nonisomorphic_trees.cpython-312.pyc,, +networkx/generators/__pycache__/random_clustered.cpython-312.pyc,, +networkx/generators/__pycache__/random_graphs.cpython-312.pyc,, +networkx/generators/__pycache__/small.cpython-312.pyc,, +networkx/generators/__pycache__/social.cpython-312.pyc,, +networkx/generators/__pycache__/spectral_graph_forge.cpython-312.pyc,, +networkx/generators/__pycache__/stochastic.cpython-312.pyc,, +networkx/generators/__pycache__/sudoku.cpython-312.pyc,, +networkx/generators/__pycache__/time_series.cpython-312.pyc,, +networkx/generators/__pycache__/trees.cpython-312.pyc,, +networkx/generators/__pycache__/triads.cpython-312.pyc,, +networkx/generators/atlas.dat.gz,sha256=c_xBbfAWSSNgd1HLdZ9K6B3rX2VQvyW-Wcht47dH5B0,8887 +networkx/generators/atlas.py,sha256=cbki_oYDi_7vniSesqJZ-PCEM3BvnzEdjSc3TA3s1xc,6955 +networkx/generators/classic.py,sha256=Wzey-Pl6Rm-ELYyg2lIb0lFZAxywRr5iQxv7zo3d9Bk,32658 +networkx/generators/cographs.py,sha256=-WR4_yrNk_X5nj7egb7A22eKPVymOdIYM-IftSRH4WA,1891 +networkx/generators/community.py,sha256=_p_4OfItbg8nS0b3EvojCXZ8cESdC-0Gj67V5w2veuM,34911 +networkx/generators/degree_seq.py,sha256=Hxv1rjY2NS8qmnvSWh4rcvublDZzor3D3IiB2OAFXoE,30493 +networkx/generators/directed.py,sha256=rtgf2Imf34SogrWA0BNgDltlA5kUv_l2NkxKPcJgGtQ,18149 +networkx/generators/duplication.py,sha256=hmYAHJBez7WlfdVGGa288JFUBHoIUdVqEGCodApKOr4,5831 +networkx/generators/ego.py,sha256=ylW-UMvg9C8cqygw9vrBNLGAVZJT4U5EWbU4gaT_H1g,1894 +networkx/generators/expanders.py,sha256=gJioNdyhBOnNcjh0qC-kfbFs1SEMU5_o9GHy85pdJXk,15184 +networkx/generators/geometric.py,sha256=6slgS0vraxql67AOP9lWzBrpNbx_OBZCoBwYuSkQQXg,39461 +networkx/generators/harary_graph.py,sha256=N2_b49xrET0bjZ4C7sgcG2xLv64ia9xjvJ2NB75CAz8,4950 +networkx/generators/internet_as_graphs.py,sha256=RuhEW9rQ8f5VzeIyZe6GqSWgxKtMVuwqnzv0llW3FeY,14226 +networkx/generators/intersection.py,sha256=EFm0AOjnqyp8KcT7kGWqANq-_vq9kQ0d_0DzVyQyP-o,4101 +networkx/generators/interval_graph.py,sha256=ZTmdgQbBx3M6sysGWXbGyngYYOC1TAXD3Ozkw4deQFw,2204 +networkx/generators/joint_degree_seq.py,sha256=nyp86NC_4XvzvwpwwzKrrCSz1i_4bESSDtVjWvpkWFg,24773 +networkx/generators/lattice.py,sha256=5iujIY5pF_VhbWEE2osM5i7uuYqs5Hv1wS33WGPMn_o,14740 +networkx/generators/line.py,sha256=DDQZvD2Y2c-4kc_xTuUWpmmViLftTSGKFukgqSiyI_c,17540 +networkx/generators/mycielski.py,sha256=xBX2m77sCzumoH5cAGitksvEEW-ocbCnbdaN7fKUtVk,3314 +networkx/generators/nonisomorphic_trees.py,sha256=62FIt4Qx-giwDflNKP5BrDoCv1HbQmlp6oVGjqGeRlE,6993 +networkx/generators/random_clustered.py,sha256=i_NdvvchHvsvbwgQtoWSY_pLwvhO9Lh02MSZXzgGb7c,4183 +networkx/generators/random_graphs.py,sha256=WIJ4v8PSNpHePclSCQ3IcLWwcipZJ2Cn9pNx6XenBEo,51669 +networkx/generators/small.py,sha256=duBVJgz4f2TByU4SdyRGHw7YyLHrlrr0UFw6spVTCbo,30967 +networkx/generators/social.py,sha256=brzAScpAxWGRExE9J7FuiJ29QZxBm8bXpRgCc0IoHP0,23416 +networkx/generators/spectral_graph_forge.py,sha256=9r6d9f5Y03KPX138sQTKWG3_RjeiYQVUJJWwCHqG4MA,4282 +networkx/generators/stochastic.py,sha256=Qg9vWm9EOug2OQVIHL_dZ5HrXc16lxnWyzX52KWNEPI,1981 +networkx/generators/sudoku.py,sha256=kLM2AP0H4966uYiNO1oAFEmv5qBftU_bOfYucRxexM0,4288 +networkx/generators/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +networkx/generators/tests/__pycache__/__init__.cpython-312.pyc,, +networkx/generators/tests/__pycache__/test_atlas.cpython-312.pyc,, +networkx/generators/tests/__pycache__/test_classic.cpython-312.pyc,, +networkx/generators/tests/__pycache__/test_cographs.cpython-312.pyc,, +networkx/generators/tests/__pycache__/test_community.cpython-312.pyc,, +networkx/generators/tests/__pycache__/test_degree_seq.cpython-312.pyc,, +networkx/generators/tests/__pycache__/test_directed.cpython-312.pyc,, +networkx/generators/tests/__pycache__/test_duplication.cpython-312.pyc,, +networkx/generators/tests/__pycache__/test_ego.cpython-312.pyc,, +networkx/generators/tests/__pycache__/test_expanders.cpython-312.pyc,, +networkx/generators/tests/__pycache__/test_geometric.cpython-312.pyc,, +networkx/generators/tests/__pycache__/test_harary_graph.cpython-312.pyc,, +networkx/generators/tests/__pycache__/test_internet_as_graphs.cpython-312.pyc,, +networkx/generators/tests/__pycache__/test_intersection.cpython-312.pyc,, +networkx/generators/tests/__pycache__/test_interval_graph.cpython-312.pyc,, +networkx/generators/tests/__pycache__/test_joint_degree_seq.cpython-312.pyc,, +networkx/generators/tests/__pycache__/test_lattice.cpython-312.pyc,, +networkx/generators/tests/__pycache__/test_line.cpython-312.pyc,, +networkx/generators/tests/__pycache__/test_mycielski.cpython-312.pyc,, +networkx/generators/tests/__pycache__/test_nonisomorphic_trees.cpython-312.pyc,, +networkx/generators/tests/__pycache__/test_random_clustered.cpython-312.pyc,, +networkx/generators/tests/__pycache__/test_random_graphs.cpython-312.pyc,, +networkx/generators/tests/__pycache__/test_small.cpython-312.pyc,, +networkx/generators/tests/__pycache__/test_spectral_graph_forge.cpython-312.pyc,, +networkx/generators/tests/__pycache__/test_stochastic.cpython-312.pyc,, +networkx/generators/tests/__pycache__/test_sudoku.cpython-312.pyc,, +networkx/generators/tests/__pycache__/test_time_series.cpython-312.pyc,, +networkx/generators/tests/__pycache__/test_trees.cpython-312.pyc,, +networkx/generators/tests/__pycache__/test_triads.cpython-312.pyc,, +networkx/generators/tests/test_atlas.py,sha256=nwXJL4O5jUqhTwqhkPxHY8s3KXHQTDEdsfbg4MsSzVQ,2530 +networkx/generators/tests/test_classic.py,sha256=eRtftt3fcr5kYJI8PffuY3tWOnn4u0yJZwKav1bZACU,24400 +networkx/generators/tests/test_cographs.py,sha256=v2fYoolmfwfCpJZRKRtH9YKyWcuJkaPOQUpDK9DMsn0,553 +networkx/generators/tests/test_community.py,sha256=FGcDo3Ajb-yYc5kUkFbVfOJVMG-YppbAtjgBPcVzjLc,11311 +networkx/generators/tests/test_degree_seq.py,sha256=V_X-Mc0H2sqEy7OTJb4yY9khsi-9HD-DIA_fvngW-VE,7284 +networkx/generators/tests/test_directed.py,sha256=e8C7AF2whjaqdxga7WSvCrtIx7ByBG_JyJ9gYqJ6HBA,6020 +networkx/generators/tests/test_duplication.py,sha256=UdIGDF_fishanWid1xO_aH4NDfie8xpIqd26qndhOqI,3155 +networkx/generators/tests/test_ego.py,sha256=8v1Qjmkli9wIhhUuqzgqCzysr0C1Z2C3oJMCUoNvgY4,1327 +networkx/generators/tests/test_expanders.py,sha256=Cf0cCJOplngPlN23OVqDcrny2QZlrYMrrWHeIi0APaA,6363 +networkx/generators/tests/test_geometric.py,sha256=gnVm4dam_Er88YwaNpNZC6mjJjfgwMYhyLOtU9oPn1o,18087 +networkx/generators/tests/test_harary_graph.py,sha256=GiX5LXXJaNxzjvd-Nyw_QuARzbFGkA6zE1R1eX8mclw,4936 +networkx/generators/tests/test_internet_as_graphs.py,sha256=3et6aawAPZ9bPNWqADJ3bQN_2KhyqPf8VFgrBAkgABQ,8514 +networkx/generators/tests/test_intersection.py,sha256=hcIit5fKfOn3VjMhz9KqovZK9tzxZfmC6ezvA7gZAvM,819 +networkx/generators/tests/test_interval_graph.py,sha256=JYMi-QMkJQdBU9uOdfm0Xr6MEYqIbhU5oSDa6D3tSb0,4277 +networkx/generators/tests/test_joint_degree_seq.py,sha256=8TXTZI3Um2gBXtP-4yhGKf9vCi78-NVmWZw9r9WG3F8,4270 +networkx/generators/tests/test_lattice.py,sha256=iYB2F7j1Qt3QAHkhGnGKcgiRNB1Gq9MxtE3152XdS_A,10102 +networkx/generators/tests/test_line.py,sha256=Dt1R1Hnm47aq_8FX9gRFdby0sV3Oxyap1R5Z4eK0A5o,10516 +networkx/generators/tests/test_mycielski.py,sha256=fwZLO1ybcltRy6TzCel8tPBil1oZWv9QSXs779H6Xt0,946 +networkx/generators/tests/test_nonisomorphic_trees.py,sha256=KFRwMfx-x_a9oGvPlhtMlWGk__rKgm77Gk4LtbxFrZE,2945 +networkx/generators/tests/test_random_clustered.py,sha256=SalHqWvpnXA3QrDRMjLx15dk2c4Us8Ck52clUERoUI8,1297 +networkx/generators/tests/test_random_graphs.py,sha256=R5dHYuzAbcIBduoP7PUdoV-6CFauqk5r1Ak1Slwlsho,19565 +networkx/generators/tests/test_small.py,sha256=y9rZHpwQG4Bjp-sHJp18rM3A0uEGOUkbklqlJec11ao,8074 +networkx/generators/tests/test_spectral_graph_forge.py,sha256=x4jyTiQiydaUPWYaGsNFsIB47PAzSSwQYCNXGa2B4SU,1594 +networkx/generators/tests/test_stochastic.py,sha256=f-5KD3RpoQf369gXHH7KGebE19g5lCkXR_alcwmFm_s,2179 +networkx/generators/tests/test_sudoku.py,sha256=dgOmk-B7MxCVkbHdZzsLZppQ61FAArVy4McSVL8Afzo,1968 +networkx/generators/tests/test_time_series.py,sha256=rgmFcitlKa_kF6TzJ2ze91lSmNJlqjhvgrYet0AUZx8,2230 +networkx/generators/tests/test_trees.py,sha256=Pvh0MvTKaRuZuwWL-wpJIC0zlBAcnTirpSLJi-9c7qc,7006 +networkx/generators/tests/test_triads.py,sha256=K8anVEP8R90Y172IrKIOrYRWRJBGeqxNqU9isX7Ybxs,333 +networkx/generators/time_series.py,sha256=_DMiY9X95O_9sK2BSeeTb2yMWfStBwKFWwn6FUOXN4Q,2439 +networkx/generators/trees.py,sha256=KvKYhZwwSl1KKMyKBe_6G_UTpLwLLeqdWwRkWJ2Xxz4,36517 +networkx/generators/triads.py,sha256=7kScTf3ITDi3qsSa-IvGMpa9diEaFwQnRuIf3Tv4UBI,2452 +networkx/lazy_imports.py,sha256=pZWn3f6NCY-lGtViKQh11NJ5Cn-cXBKhvacMycg3844,5764 +networkx/linalg/__init__.py,sha256=7iyNZ_YYBnlsW8zSfhUgvEkywOrUWfpIuyS86ZOKlG8,568 +networkx/linalg/__pycache__/__init__.cpython-312.pyc,, +networkx/linalg/__pycache__/algebraicconnectivity.cpython-312.pyc,, +networkx/linalg/__pycache__/attrmatrix.cpython-312.pyc,, +networkx/linalg/__pycache__/bethehessianmatrix.cpython-312.pyc,, +networkx/linalg/__pycache__/graphmatrix.cpython-312.pyc,, +networkx/linalg/__pycache__/laplacianmatrix.cpython-312.pyc,, +networkx/linalg/__pycache__/modularitymatrix.cpython-312.pyc,, +networkx/linalg/__pycache__/spectrum.cpython-312.pyc,, +networkx/linalg/algebraicconnectivity.py,sha256=qAjHk8Ew2av9HM-Sevf99alKHWkeo4R76U-1SFn2so8,20790 +networkx/linalg/attrmatrix.py,sha256=gG2cZZm4N7PQhXuq5nxZq1S_OmW61641rdRnrDiX_ws,15900 +networkx/linalg/bethehessianmatrix.py,sha256=Q5dJZOMopwWnvpSdebfo9QSQABEmLy-z1_hGCWsopDo,2513 +networkx/linalg/graphmatrix.py,sha256=NIs2uWGS_8lJJ5IQ8Og9aIWHawghtlCDWifqOIKV2-c,5623 +networkx/linalg/laplacianmatrix.py,sha256=YrzwLTLoGIisJVS2NLaD9g4fA3VDKzlYo1u3yOCVrF4,16722 +networkx/linalg/modularitymatrix.py,sha256=R_VITtgIkGenxlsCLN4u6CYxj3_HiPXfeU29yarntRo,4706 +networkx/linalg/spectrum.py,sha256=aRY7ApYv5HxrO_4O8brxpZRw3SJU3fYzlgMwhEIXcrc,4215 +networkx/linalg/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +networkx/linalg/tests/__pycache__/__init__.cpython-312.pyc,, +networkx/linalg/tests/__pycache__/test_algebraic_connectivity.cpython-312.pyc,, +networkx/linalg/tests/__pycache__/test_attrmatrix.cpython-312.pyc,, +networkx/linalg/tests/__pycache__/test_bethehessian.cpython-312.pyc,, +networkx/linalg/tests/__pycache__/test_graphmatrix.cpython-312.pyc,, +networkx/linalg/tests/__pycache__/test_laplacian.cpython-312.pyc,, +networkx/linalg/tests/__pycache__/test_modularity.cpython-312.pyc,, +networkx/linalg/tests/__pycache__/test_spectrum.cpython-312.pyc,, +networkx/linalg/tests/test_algebraic_connectivity.py,sha256=uXq7h1fCmvcUyJhh3DujyZvJH2SGlgo-JRdLbff3bKI,13735 +networkx/linalg/tests/test_attrmatrix.py,sha256=I8p_ME5Cq__SZJoxla1y5ynGaa_b4UruZRcuzPOh_wE,2833 +networkx/linalg/tests/test_bethehessian.py,sha256=WQM3H_y1GWrtM2B659HYQ2vvYGs-4H9V1qeo3Bx75B4,1268 +networkx/linalg/tests/test_graphmatrix.py,sha256=6y-DSezX5slSms8LtEJcoggI4wP_duBqodNYWKKvM4w,8655 +networkx/linalg/tests/test_laplacian.py,sha256=sIJ64KVVCUcK3b4ohF-443222vR64myIlV6addwwfWs,13953 +networkx/linalg/tests/test_modularity.py,sha256=Xs7iaUr_QqcxG0t0xHx54iYbM2VyQNmFeANovTMJomE,3056 +networkx/linalg/tests/test_spectrum.py,sha256=Hrl3FE0Z0u4urwY5eNMx0VcH4IRTYgudT04EmfhkosA,2769 +networkx/readwrite/__init__.py,sha256=TvSbnGEHQ5F9CY2tkpjWYOyrUj6BeW3sc6P4_IczbKA,561 +networkx/readwrite/__pycache__/__init__.cpython-312.pyc,, +networkx/readwrite/__pycache__/adjlist.cpython-312.pyc,, +networkx/readwrite/__pycache__/edgelist.cpython-312.pyc,, +networkx/readwrite/__pycache__/gexf.cpython-312.pyc,, +networkx/readwrite/__pycache__/gml.cpython-312.pyc,, +networkx/readwrite/__pycache__/graph6.cpython-312.pyc,, +networkx/readwrite/__pycache__/graphml.cpython-312.pyc,, +networkx/readwrite/__pycache__/leda.cpython-312.pyc,, +networkx/readwrite/__pycache__/multiline_adjlist.cpython-312.pyc,, +networkx/readwrite/__pycache__/p2g.cpython-312.pyc,, +networkx/readwrite/__pycache__/pajek.cpython-312.pyc,, +networkx/readwrite/__pycache__/sparse6.cpython-312.pyc,, +networkx/readwrite/__pycache__/text.cpython-312.pyc,, +networkx/readwrite/adjlist.py,sha256=XkmtOO-aP-BGLoIjMoSXEOhCIEQz1TJcSrhMHGPCJTc,9050 +networkx/readwrite/edgelist.py,sha256=XwemCOs-xNet3mVIGpxol3RgFVJVa1iXsgBonAHDmug,14292 +networkx/readwrite/gexf.py,sha256=B5rj7Ra1XHSZR4qO5DRJMAEgWwQfiL0IKlE85fzQZ5Q,40438 +networkx/readwrite/gml.py,sha256=JcP0hsDKwpZOPzZeXdoEiKBBNd94Ox8y7_cjT5AVBT4,31193 +networkx/readwrite/graph6.py,sha256=dED6yWelHARFvyKniX8xSTtqj1-asDs1vUBWuZOG9cA,11684 +networkx/readwrite/graphml.py,sha256=sb-D5I5gmqyNj_TSNUvLn-96NBXtOAPFMEKvT3mkdTw,39326 +networkx/readwrite/json_graph/__init__.py,sha256=37XJPMmilcwwo8KqouLWUly7Yv5tZ7IKraMHbBRx3fI,677 +networkx/readwrite/json_graph/__pycache__/__init__.cpython-312.pyc,, +networkx/readwrite/json_graph/__pycache__/adjacency.cpython-312.pyc,, +networkx/readwrite/json_graph/__pycache__/cytoscape.cpython-312.pyc,, +networkx/readwrite/json_graph/__pycache__/node_link.cpython-312.pyc,, +networkx/readwrite/json_graph/__pycache__/tree.cpython-312.pyc,, +networkx/readwrite/json_graph/adjacency.py,sha256=WM6fdncV87WDLPOfF-IbOlOOBMX0utUjJ09UsxtwRAo,4716 +networkx/readwrite/json_graph/cytoscape.py,sha256=NaHGbwLuQCx_GB5R7Uag2ACKSmrpCFQKsRk3y9hE6Io,5841 +networkx/readwrite/json_graph/node_link.py,sha256=9hr4sk2fIHYDPuwSyyGNLNKDhahf6aFAEsIRhd5V890,7850 +networkx/readwrite/json_graph/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +networkx/readwrite/json_graph/tests/__pycache__/__init__.cpython-312.pyc,, +networkx/readwrite/json_graph/tests/__pycache__/test_adjacency.cpython-312.pyc,, +networkx/readwrite/json_graph/tests/__pycache__/test_cytoscape.cpython-312.pyc,, +networkx/readwrite/json_graph/tests/__pycache__/test_node_link.cpython-312.pyc,, +networkx/readwrite/json_graph/tests/__pycache__/test_tree.cpython-312.pyc,, +networkx/readwrite/json_graph/tests/test_adjacency.py,sha256=jueQE3Z_W5BZuCjr0hEsOWSfoQ2fP51p0o0m7IcXUuE,2456 +networkx/readwrite/json_graph/tests/test_cytoscape.py,sha256=vFoDzcSRI9THlmp4Fu2HHhIF9AUmECWs5mftVWjaWWs,2044 +networkx/readwrite/json_graph/tests/test_node_link.py,sha256=3RbMNRlLBLN86EG4YyCvNmA3ISzmECqr17EqTcov3kI,3364 +networkx/readwrite/json_graph/tests/test_tree.py,sha256=zBXv3_db2XGxFs3XQ35btNf_ku52aLXXiHZmmX4ixAs,1352 +networkx/readwrite/json_graph/tree.py,sha256=K4rF4Kds4g0JhgcPTrrR_I3Pswpze8yCVH4M-WF9nn0,3851 +networkx/readwrite/leda.py,sha256=mtuIRKecfykiInePt-oIw91JB1vIgNXuUEbuBInO7Hs,2852 +networkx/readwrite/multiline_adjlist.py,sha256=TyvVZih3DIYaCQ6MC7XF8GuGBfcYIX-q-ccYu2Qs05M,11384 +networkx/readwrite/p2g.py,sha256=wDPg_3u42rlsv8pFtVLbZIjo-BN6dd95jCxFAoEC_6E,3253 +networkx/readwrite/pajek.py,sha256=0Xli27YbjyW5Ny6-qqVOtBmhZkPj4i3cJPvwjU3mMjM,8746 +networkx/readwrite/sparse6.py,sha256=kGEbDmnK1qcR5I6LZnPKYXLe7Qp6esT6gftkwSt1xcs,10442 +networkx/readwrite/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +networkx/readwrite/tests/__pycache__/__init__.cpython-312.pyc,, +networkx/readwrite/tests/__pycache__/test_adjlist.cpython-312.pyc,, +networkx/readwrite/tests/__pycache__/test_edgelist.cpython-312.pyc,, +networkx/readwrite/tests/__pycache__/test_gexf.cpython-312.pyc,, +networkx/readwrite/tests/__pycache__/test_gml.cpython-312.pyc,, +networkx/readwrite/tests/__pycache__/test_graph6.cpython-312.pyc,, +networkx/readwrite/tests/__pycache__/test_graphml.cpython-312.pyc,, +networkx/readwrite/tests/__pycache__/test_leda.cpython-312.pyc,, +networkx/readwrite/tests/__pycache__/test_p2g.cpython-312.pyc,, +networkx/readwrite/tests/__pycache__/test_pajek.cpython-312.pyc,, +networkx/readwrite/tests/__pycache__/test_sparse6.cpython-312.pyc,, +networkx/readwrite/tests/__pycache__/test_text.cpython-312.pyc,, +networkx/readwrite/tests/test_adjlist.py,sha256=aCDyLpv8cekWwEZgv5rB4gtwUvor7_YDArTYvL2ngPs,12756 +networkx/readwrite/tests/test_edgelist.py,sha256=5_rZ5rI_o8VDJr-R4besR-XcPwKY8-dHn4VmrrlBorc,10190 +networkx/readwrite/tests/test_gexf.py,sha256=TJLCj0ULSewUt5PlqEuoD8yhZoc6jFPqElIyQYhoKz0,21327 +networkx/readwrite/tests/test_gml.py,sha256=SIuCCSoH9psZdjKkrJfdGHbMROhUsfms70B71rnozUU,21399 +networkx/readwrite/tests/test_graph6.py,sha256=WMQmTgrI_VDcli1Gl5cjM17EFKc8_AkwoR14FLS1sXQ,6559 +networkx/readwrite/tests/test_graphml.py,sha256=RHZqs_-0Vw_NboMK1Sq65Nmk_XPAjfvnsgiPCptnpnI,67633 +networkx/readwrite/tests/test_leda.py,sha256=_5F4nLLQ1oAZQMZtTQoFncZL0Oc-IsztFBglEdQeH3k,1392 +networkx/readwrite/tests/test_p2g.py,sha256=0kvtohQuM2zjw88ogoDMhVbFcm85R3Sk2_yFdQ7TcWg,1319 +networkx/readwrite/tests/test_pajek.py,sha256=VxMk_TXQE1R-2t2B2OTSkwvPepKNYwExDbdlTDb0SOM,4696 +networkx/readwrite/tests/test_sparse6.py,sha256=zpd0hUPtOqwAJiuke4FXWFaQVa6Fa47dgIdHpCWXQ6U,5288 +networkx/readwrite/tests/test_text.py,sha256=x1N97hD31HPkj9Wn2PYti5-gcwaFNnStkaN_38HKnIg,55319 +networkx/readwrite/text.py,sha256=u62a-zG_7NpJD4Bh6kBZbUntORVM4XZId9zGX9wJBGQ,29140 +networkx/relabel.py,sha256=0HptAQOBToKhLZzxscd6FQpzVCNMlYmiHjHul69ct8o,10300 +networkx/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +networkx/tests/__pycache__/__init__.cpython-312.pyc,, +networkx/tests/__pycache__/test_all_random_functions.cpython-312.pyc,, +networkx/tests/__pycache__/test_convert.cpython-312.pyc,, +networkx/tests/__pycache__/test_convert_numpy.cpython-312.pyc,, +networkx/tests/__pycache__/test_convert_pandas.cpython-312.pyc,, +networkx/tests/__pycache__/test_convert_scipy.cpython-312.pyc,, +networkx/tests/__pycache__/test_exceptions.cpython-312.pyc,, +networkx/tests/__pycache__/test_import.cpython-312.pyc,, +networkx/tests/__pycache__/test_lazy_imports.cpython-312.pyc,, +networkx/tests/__pycache__/test_relabel.cpython-312.pyc,, +networkx/tests/__pycache__/test_removed_functions_exception_messages.cpython-312.pyc,, +networkx/tests/test_all_random_functions.py,sha256=T6bYvfqCmBfqOHAAJAqCv95VceTqdT32cN740HZTWH0,8549 +networkx/tests/test_convert.py,sha256=SoIVrqJFF9Gu9Jff_apfbpqg8QhkfC6QW4qzoSM-ukM,12731 +networkx/tests/test_convert_numpy.py,sha256=wivMBF5pmDd2FdrHX72J4Xt26IxTIfb12G-t-TWch40,19032 +networkx/tests/test_convert_pandas.py,sha256=VXumRYNIV2jdr4dfDsRidqpUDXHlgbhAFhaiqWlcx6s,13361 +networkx/tests/test_convert_scipy.py,sha256=IO9Xfke5Vk0SC4c7UR4YbQMjVZBuADQBt_wlMaOxTSQ,10381 +networkx/tests/test_exceptions.py,sha256=XYkpPzqMepSw3MPRUJN5LcFsUsy3YT_fiRDhm0OeAeQ,927 +networkx/tests/test_import.py,sha256=Gm4ujfH9JkQtDrSjOlwXXXUuubI057wskKLCkF6Z92k,220 +networkx/tests/test_lazy_imports.py,sha256=PylxEvRtD8ekwH-1M1sjnV_4uyhCYQl4WvlyF6ah0wo,2675 +networkx/tests/test_relabel.py,sha256=2qua3Z45m9mXkBQYmD82h17-pK6fYHJLpntn6ILj9cg,14554 +networkx/tests/test_removed_functions_exception_messages.py,sha256=V6AyU-RHQmilakEMJ1kpK4HSgzKbmimIxu0pBQ1DVaY,177 +networkx/utils/__init__.py,sha256=7pxleRNpBWuL3FEQz3CzKLn17b6_eSwkM7dqnL1okDk,302 +networkx/utils/__pycache__/__init__.cpython-312.pyc,, +networkx/utils/__pycache__/backends.cpython-312.pyc,, +networkx/utils/__pycache__/configs.cpython-312.pyc,, +networkx/utils/__pycache__/decorators.cpython-312.pyc,, +networkx/utils/__pycache__/heaps.cpython-312.pyc,, +networkx/utils/__pycache__/mapped_queue.cpython-312.pyc,, +networkx/utils/__pycache__/misc.cpython-312.pyc,, +networkx/utils/__pycache__/random_sequence.cpython-312.pyc,, +networkx/utils/__pycache__/rcm.cpython-312.pyc,, +networkx/utils/__pycache__/union_find.cpython-312.pyc,, +networkx/utils/backends.py,sha256=IDXNJjBWOfcqPm3Any5-kfwU9hZTH33R16gr3vZ6wnE,96862 +networkx/utils/configs.py,sha256=1LFNHgkR6CdXxTpNT8j4QdQ5vn5r7BTzPlxFYrEm7_E,15440 +networkx/utils/decorators.py,sha256=rRgAZw6glxOBYN6n5kkbQ7RS43EH3eVbJ5F91-uk0IM,44713 +networkx/utils/heaps.py,sha256=98tdylWKqrLQKdNlpjT2NvIpuOF2mT2Lhh5OzVg3Hu8,10355 +networkx/utils/mapped_queue.py,sha256=WdIRk27D_ArmPs9tdpvQLQCV4Tmus212BQhxsFIMYgk,10184 +networkx/utils/misc.py,sha256=zifBxwKM3msQsmMVg5vAMDo84ZPByU_yEig0VPS0iek,22844 +networkx/utils/random_sequence.py,sha256=kk3F8_5cazolIeSL_eDdZzQ15aB3b5lHazef9kM1NNw,5303 +networkx/utils/rcm.py,sha256=t2LJq6BG39JnPl_353nM8eDfGWy3LVL8BdrBZCcAy2g,4618 +networkx/utils/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +networkx/utils/tests/__pycache__/__init__.cpython-312.pyc,, +networkx/utils/tests/__pycache__/test__init.cpython-312.pyc,, +networkx/utils/tests/__pycache__/test_backends.cpython-312.pyc,, +networkx/utils/tests/__pycache__/test_config.cpython-312.pyc,, +networkx/utils/tests/__pycache__/test_decorators.cpython-312.pyc,, +networkx/utils/tests/__pycache__/test_heaps.cpython-312.pyc,, +networkx/utils/tests/__pycache__/test_mapped_queue.cpython-312.pyc,, +networkx/utils/tests/__pycache__/test_misc.cpython-312.pyc,, +networkx/utils/tests/__pycache__/test_random_sequence.cpython-312.pyc,, +networkx/utils/tests/__pycache__/test_rcm.cpython-312.pyc,, +networkx/utils/tests/__pycache__/test_unionfind.cpython-312.pyc,, +networkx/utils/tests/test__init.py,sha256=QE0i-lNE4pG2eYjB2mZ0uw7jPD-7TdL7Y9p73JoWQmo,363 +networkx/utils/tests/test_backends.py,sha256=VzzgmdT_ymvQ7EKB4BbdntkBYaO5w6OgXzvr7pbgQsY,7784 +networkx/utils/tests/test_config.py,sha256=eBAiwADWJgCAIyJjgF3-zn89VPIGftiWMWyccpFnyMU,8717 +networkx/utils/tests/test_decorators.py,sha256=dm3b5yiQPlnlT_4pSm0FwK-xBGV9dcnhv14Vh9Jiz1o,14050 +networkx/utils/tests/test_heaps.py,sha256=qCuWMzpcMH1Gwu014CAams78o151QD5YL0mB1fz16Yw,3711 +networkx/utils/tests/test_mapped_queue.py,sha256=l1Nguzz68Fv91FnAT7y7B0GXSoje9uoWiObHo7TliGM,7354 +networkx/utils/tests/test_misc.py,sha256=3rKc5wy3qAwk1JdVZYD8FaTF4_4Y1_Tq1nbza6eR_jc,13965 +networkx/utils/tests/test_random_sequence.py,sha256=yvfWhJQJJqM1W-_etHo7BsLXA18YiHcLaF-IdhSwcYA,1502 +networkx/utils/tests/test_rcm.py,sha256=UvUAkgmQMGk_Nn94TJyQsle4A5SLQFqMQWld1tiQ2lk,1421 +networkx/utils/tests/test_unionfind.py,sha256=j-DF5XyeJzq1hoeAgN5Nye2Au7EPD040t8oS4Aw2IwU,1579 +networkx/utils/union_find.py,sha256=NxKlBlyS71A1Wlnt28L-wyZoI9ExZvJth_0e2XSVris,3338 diff --git a/lib/python3.12/site-packages/networkx-3.6.1.dist-info/WHEEL b/lib/python3.12/site-packages/networkx-3.6.1.dist-info/WHEEL new file mode 100644 index 0000000000000000000000000000000000000000..e7fa31b6f3f78deb1022c1f7927f07d4d16da822 --- /dev/null +++ b/lib/python3.12/site-packages/networkx-3.6.1.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: setuptools (80.9.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/lib/python3.12/site-packages/networkx-3.6.1.dist-info/entry_points.txt b/lib/python3.12/site-packages/networkx-3.6.1.dist-info/entry_points.txt new file mode 100644 index 0000000000000000000000000000000000000000..377282a2e4c3dc8b924035d0dfd32d7ae50920c8 --- /dev/null +++ b/lib/python3.12/site-packages/networkx-3.6.1.dist-info/entry_points.txt @@ -0,0 +1,2 @@ +[networkx.backends] +nx_loopback = networkx.classes.tests.dispatch_interface:backend_interface diff --git a/lib/python3.12/site-packages/networkx-3.6.1.dist-info/licenses/LICENSE.txt b/lib/python3.12/site-packages/networkx-3.6.1.dist-info/licenses/LICENSE.txt new file mode 100644 index 0000000000000000000000000000000000000000..0bf9a8f3f4922a0b70aac95ac7aab9574975342b --- /dev/null +++ b/lib/python3.12/site-packages/networkx-3.6.1.dist-info/licenses/LICENSE.txt @@ -0,0 +1,37 @@ +NetworkX is distributed with the 3-clause BSD license. + +:: + + Copyright (c) 2004-2025, NetworkX Developers + Aric Hagberg + Dan Schult + Pieter Swart + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + + * Neither the name of the NetworkX Developers nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/lib/python3.12/site-packages/networkx-3.6.1.dist-info/top_level.txt b/lib/python3.12/site-packages/networkx-3.6.1.dist-info/top_level.txt new file mode 100644 index 0000000000000000000000000000000000000000..4d07dfe2f85d6849d7f416dcce756b2501ba847e --- /dev/null +++ b/lib/python3.12/site-packages/networkx-3.6.1.dist-info/top_level.txt @@ -0,0 +1 @@ +networkx diff --git a/lib/python3.12/site-packages/pandas-2.2.3.dist-info/INSTALLER b/lib/python3.12/site-packages/pandas-2.2.3.dist-info/INSTALLER new file mode 100644 index 0000000000000000000000000000000000000000..a1b589e38a32041e49332e5e81c2d363dc418d68 --- /dev/null +++ b/lib/python3.12/site-packages/pandas-2.2.3.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/lib/python3.12/site-packages/pandas-2.2.3.dist-info/LICENSE b/lib/python3.12/site-packages/pandas-2.2.3.dist-info/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..733963a744c3d6710058de9f0ea316893c5492ca --- /dev/null +++ b/lib/python3.12/site-packages/pandas-2.2.3.dist-info/LICENSE @@ -0,0 +1,1250 @@ +BSD 3-Clause License + +Copyright (c) 2008-2011, AQR Capital Management, LLC, Lambda Foundry, Inc. and PyData Development Team +All rights reserved. + +Copyright (c) 2011-2023, Open source contributors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +Copyright (c) 2010-2019 Keith Goodman +Copyright (c) 2019 Bottleneck Developers +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE.Copyright 2017- Paul Ganssle +Copyright 2017- dateutil contributors (see AUTHORS file) + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +The above license applies to all contributions after 2017-12-01, as well as +all contributions that have been re-licensed (see AUTHORS file for the list of +contributors who have re-licensed their code). +-------------------------------------------------------------------------------- +dateutil - Extensions to the standard Python datetime module. + +Copyright (c) 2003-2011 - Gustavo Niemeyer +Copyright (c) 2012-2014 - Tomi Pieviläinen +Copyright (c) 2014-2016 - Yaron de Leeuw +Copyright (c) 2015- - Paul Ganssle +Copyright (c) 2015- - dateutil contributors (see AUTHORS file) + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +The above BSD License Applies to all code, even that also covered by Apache 2.0.# MIT License + +Copyright (c) 2019 Hadley Wickham; RStudio; and Evan Miller + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +Based on http://opensource.org/licenses/MIT + +This is a template. Complete and ship as file LICENSE the following 2 +lines (only) + +YEAR: +COPYRIGHT HOLDER: + +and specify as + +License: MIT + file LICENSE + +Copyright (c) , + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +The MIT License + +Copyright (c) 2008- Attractive Chaos + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE.musl as a whole is licensed under the following standard MIT license: + +---------------------------------------------------------------------- +Copyright © 2005-2020 Rich Felker, et al. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +---------------------------------------------------------------------- + +Authors/contributors include: + +A. Wilcox +Ada Worcester +Alex Dowad +Alex Suykov +Alexander Monakov +Andre McCurdy +Andrew Kelley +Anthony G. Basile +Aric Belsito +Arvid Picciani +Bartosz Brachaczek +Benjamin Peterson +Bobby Bingham +Boris Brezillon +Brent Cook +Chris Spiegel +Clément Vasseur +Daniel Micay +Daniel Sabogal +Daurnimator +David Carlier +David Edelsohn +Denys Vlasenko +Dmitry Ivanov +Dmitry V. Levin +Drew DeVault +Emil Renner Berthing +Fangrui Song +Felix Fietkau +Felix Janda +Gianluca Anzolin +Hauke Mehrtens +He X +Hiltjo Posthuma +Isaac Dunham +Jaydeep Patil +Jens Gustedt +Jeremy Huntwork +Jo-Philipp Wich +Joakim Sindholt +John Spencer +Julien Ramseier +Justin Cormack +Kaarle Ritvanen +Khem Raj +Kylie McClain +Leah Neukirchen +Luca Barbato +Luka Perkov +M Farkas-Dyck (Strake) +Mahesh Bodapati +Markus Wichmann +Masanori Ogino +Michael Clark +Michael Forney +Mikhail Kremnyov +Natanael Copa +Nicholas J. Kain +orc +Pascal Cuoq +Patrick Oppenlander +Petr Hosek +Petr Skocik +Pierre Carrier +Reini Urban +Rich Felker +Richard Pennington +Ryan Fairfax +Samuel Holland +Segev Finer +Shiz +sin +Solar Designer +Stefan Kristiansson +Stefan O'Rear +Szabolcs Nagy +Timo Teräs +Trutz Behn +Valentin Ochs +Will Dietz +William Haddon +William Pitcock + +Portions of this software are derived from third-party works licensed +under terms compatible with the above MIT license: + +The TRE regular expression implementation (src/regex/reg* and +src/regex/tre*) is Copyright © 2001-2008 Ville Laurikari and licensed +under a 2-clause BSD license (license text in the source files). The +included version has been heavily modified by Rich Felker in 2012, in +the interests of size, simplicity, and namespace cleanliness. + +Much of the math library code (src/math/* and src/complex/*) is +Copyright © 1993,2004 Sun Microsystems or +Copyright © 2003-2011 David Schultz or +Copyright © 2003-2009 Steven G. Kargl or +Copyright © 2003-2009 Bruce D. Evans or +Copyright © 2008 Stephen L. Moshier or +Copyright © 2017-2018 Arm Limited +and labelled as such in comments in the individual source files. All +have been licensed under extremely permissive terms. + +The ARM memcpy code (src/string/arm/memcpy.S) is Copyright © 2008 +The Android Open Source Project and is licensed under a two-clause BSD +license. It was taken from Bionic libc, used on Android. + +The AArch64 memcpy and memset code (src/string/aarch64/*) are +Copyright © 1999-2019, Arm Limited. + +The implementation of DES for crypt (src/crypt/crypt_des.c) is +Copyright © 1994 David Burren. It is licensed under a BSD license. + +The implementation of blowfish crypt (src/crypt/crypt_blowfish.c) was +originally written by Solar Designer and placed into the public +domain. The code also comes with a fallback permissive license for use +in jurisdictions that may not recognize the public domain. + +The smoothsort implementation (src/stdlib/qsort.c) is Copyright © 2011 +Valentin Ochs and is licensed under an MIT-style license. + +The x86_64 port was written by Nicholas J. Kain and is licensed under +the standard MIT terms. + +The mips and microblaze ports were originally written by Richard +Pennington for use in the ellcc project. The original code was adapted +by Rich Felker for build system and code conventions during upstream +integration. It is licensed under the standard MIT terms. + +The mips64 port was contributed by Imagination Technologies and is +licensed under the standard MIT terms. + +The powerpc port was also originally written by Richard Pennington, +and later supplemented and integrated by John Spencer. It is licensed +under the standard MIT terms. + +All other files which have no copyright comments are original works +produced specifically for use as part of this library, written either +by Rich Felker, the main author of the library, or by one or more +contibutors listed above. Details on authorship of individual files +can be found in the git version control history of the project. The +omission of copyright and license comments in each file is in the +interest of source tree size. + +In addition, permission is hereby granted for all public header files +(include/* and arch/*/bits/*) and crt files intended to be linked into +applications (crt/*, ldso/dlstart.c, and arch/*/crt_arch.h) to omit +the copyright notice and permission notice otherwise required by the +license, and to use these files without any requirement of +attribution. These files include substantial contributions from: + +Bobby Bingham +John Spencer +Nicholas J. Kain +Rich Felker +Richard Pennington +Stefan Kristiansson +Szabolcs Nagy + +all of whom have explicitly granted such permission. + +This file previously contained text expressing a belief that most of +the files covered by the above exception were sufficiently trivial not +to be subject to copyright, resulting in confusion over whether it +negated the permissions granted in the license. In the spirit of +permissive licensing, and of not having licensing issues being an +obstacle to adoption, that text has been removed.Copyright (c) 2005-2023, NumPy Developers. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + + * Neither the name of the NumPy Developers nor the names of any + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + +Copyright (c) Donald Stufft and individual contributors. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.A. HISTORY OF THE SOFTWARE +========================== + +Python was created in the early 1990s by Guido van Rossum at Stichting +Mathematisch Centrum (CWI, see https://www.cwi.nl) in the Netherlands +as a successor of a language called ABC. Guido remains Python's +principal author, although it includes many contributions from others. + +In 1995, Guido continued his work on Python at the Corporation for +National Research Initiatives (CNRI, see https://www.cnri.reston.va.us) +in Reston, Virginia where he released several versions of the +software. + +In May 2000, Guido and the Python core development team moved to +BeOpen.com to form the BeOpen PythonLabs team. In October of the same +year, the PythonLabs team moved to Digital Creations, which became +Zope Corporation. In 2001, the Python Software Foundation (PSF, see +https://www.python.org/psf/) was formed, a non-profit organization +created specifically to own Python-related Intellectual Property. +Zope Corporation was a sponsoring member of the PSF. + +All Python releases are Open Source (see https://opensource.org for +the Open Source Definition). Historically, most, but not all, Python +releases have also been GPL-compatible; the table below summarizes +the various releases. + + Release Derived Year Owner GPL- + from compatible? (1) + + 0.9.0 thru 1.2 1991-1995 CWI yes + 1.3 thru 1.5.2 1.2 1995-1999 CNRI yes + 1.6 1.5.2 2000 CNRI no + 2.0 1.6 2000 BeOpen.com no + 1.6.1 1.6 2001 CNRI yes (2) + 2.1 2.0+1.6.1 2001 PSF no + 2.0.1 2.0+1.6.1 2001 PSF yes + 2.1.1 2.1+2.0.1 2001 PSF yes + 2.1.2 2.1.1 2002 PSF yes + 2.1.3 2.1.2 2002 PSF yes + 2.2 and above 2.1.1 2001-now PSF yes + +Footnotes: + +(1) GPL-compatible doesn't mean that we're distributing Python under + the GPL. All Python licenses, unlike the GPL, let you distribute + a modified version without making your changes open source. The + GPL-compatible licenses make it possible to combine Python with + other software that is released under the GPL; the others don't. + +(2) According to Richard Stallman, 1.6.1 is not GPL-compatible, + because its license has a choice of law clause. According to + CNRI, however, Stallman's lawyer has told CNRI's lawyer that 1.6.1 + is "not incompatible" with the GPL. + +Thanks to the many outside volunteers who have worked under Guido's +direction to make these releases possible. + + +B. TERMS AND CONDITIONS FOR ACCESSING OR OTHERWISE USING PYTHON +=============================================================== + +Python software and documentation are licensed under the +Python Software Foundation License Version 2. + +Starting with Python 3.8.6, examples, recipes, and other code in +the documentation are dual licensed under the PSF License Version 2 +and the Zero-Clause BSD license. + +Some software incorporated into Python is under different licenses. +The licenses are listed with code falling under that license. + + +PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 +-------------------------------------------- + +1. This LICENSE AGREEMENT is between the Python Software Foundation +("PSF"), and the Individual or Organization ("Licensee") accessing and +otherwise using this software ("Python") in source or binary form and +its associated documentation. + +2. Subject to the terms and conditions of this License Agreement, PSF hereby +grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, +analyze, test, perform and/or display publicly, prepare derivative works, +distribute, and otherwise use Python alone or in any derivative version, +provided, however, that PSF's License Agreement and PSF's notice of copyright, +i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, +2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023 Python Software Foundation; +All Rights Reserved" are retained in Python alone or in any derivative version +prepared by Licensee. + +3. In the event Licensee prepares a derivative work that is based on +or incorporates Python or any part thereof, and wants to make +the derivative work available to others as provided herein, then +Licensee hereby agrees to include in any such work a brief summary of +the changes made to Python. + +4. PSF is making Python available to Licensee on an "AS IS" +basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR +IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND +DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS +FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT +INFRINGE ANY THIRD PARTY RIGHTS. + +5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON +FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS +A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON, +OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +6. This License Agreement will automatically terminate upon a material +breach of its terms and conditions. + +7. Nothing in this License Agreement shall be deemed to create any +relationship of agency, partnership, or joint venture between PSF and +Licensee. This License Agreement does not grant permission to use PSF +trademarks or trade name in a trademark sense to endorse or promote +products or services of Licensee, or any third party. + +8. By copying, installing or otherwise using Python, Licensee +agrees to be bound by the terms and conditions of this License +Agreement. + + +BEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0 +------------------------------------------- + +BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1 + +1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an +office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the +Individual or Organization ("Licensee") accessing and otherwise using +this software in source or binary form and its associated +documentation ("the Software"). + +2. Subject to the terms and conditions of this BeOpen Python License +Agreement, BeOpen hereby grants Licensee a non-exclusive, +royalty-free, world-wide license to reproduce, analyze, test, perform +and/or display publicly, prepare derivative works, distribute, and +otherwise use the Software alone or in any derivative version, +provided, however, that the BeOpen Python License is retained in the +Software, alone or in any derivative version prepared by Licensee. + +3. BeOpen is making the Software available to Licensee on an "AS IS" +basis. BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR +IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND +DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS +FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT +INFRINGE ANY THIRD PARTY RIGHTS. + +4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE +SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS +AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY +DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +5. This License Agreement will automatically terminate upon a material +breach of its terms and conditions. + +6. This License Agreement shall be governed by and interpreted in all +respects by the law of the State of California, excluding conflict of +law provisions. Nothing in this License Agreement shall be deemed to +create any relationship of agency, partnership, or joint venture +between BeOpen and Licensee. This License Agreement does not grant +permission to use BeOpen trademarks or trade names in a trademark +sense to endorse or promote products or services of Licensee, or any +third party. As an exception, the "BeOpen Python" logos available at +http://www.pythonlabs.com/logos.html may be used according to the +permissions granted on that web page. + +7. By copying, installing or otherwise using the software, Licensee +agrees to be bound by the terms and conditions of this License +Agreement. + + +CNRI LICENSE AGREEMENT FOR PYTHON 1.6.1 +--------------------------------------- + +1. This LICENSE AGREEMENT is between the Corporation for National +Research Initiatives, having an office at 1895 Preston White Drive, +Reston, VA 20191 ("CNRI"), and the Individual or Organization +("Licensee") accessing and otherwise using Python 1.6.1 software in +source or binary form and its associated documentation. + +2. Subject to the terms and conditions of this License Agreement, CNRI +hereby grants Licensee a nonexclusive, royalty-free, world-wide +license to reproduce, analyze, test, perform and/or display publicly, +prepare derivative works, distribute, and otherwise use Python 1.6.1 +alone or in any derivative version, provided, however, that CNRI's +License Agreement and CNRI's notice of copyright, i.e., "Copyright (c) +1995-2001 Corporation for National Research Initiatives; All Rights +Reserved" are retained in Python 1.6.1 alone or in any derivative +version prepared by Licensee. Alternately, in lieu of CNRI's License +Agreement, Licensee may substitute the following text (omitting the +quotes): "Python 1.6.1 is made available subject to the terms and +conditions in CNRI's License Agreement. This Agreement together with +Python 1.6.1 may be located on the internet using the following +unique, persistent identifier (known as a handle): 1895.22/1013. This +Agreement may also be obtained from a proxy server on the internet +using the following URL: http://hdl.handle.net/1895.22/1013". + +3. In the event Licensee prepares a derivative work that is based on +or incorporates Python 1.6.1 or any part thereof, and wants to make +the derivative work available to others as provided herein, then +Licensee hereby agrees to include in any such work a brief summary of +the changes made to Python 1.6.1. + +4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS" +basis. CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR +IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND +DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS +FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT +INFRINGE ANY THIRD PARTY RIGHTS. + +5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON +1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS +A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1, +OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +6. This License Agreement will automatically terminate upon a material +breach of its terms and conditions. + +7. This License Agreement shall be governed by the federal +intellectual property law of the United States, including without +limitation the federal copyright law, and, to the extent such +U.S. federal law does not apply, by the law of the Commonwealth of +Virginia, excluding Virginia's conflict of law provisions. +Notwithstanding the foregoing, with regard to derivative works based +on Python 1.6.1 that incorporate non-separable material that was +previously distributed under the GNU General Public License (GPL), the +law of the Commonwealth of Virginia shall govern this License +Agreement only as to issues arising under or with respect to +Paragraphs 4, 5, and 7 of this License Agreement. Nothing in this +License Agreement shall be deemed to create any relationship of +agency, partnership, or joint venture between CNRI and Licensee. This +License Agreement does not grant permission to use CNRI trademarks or +trade name in a trademark sense to endorse or promote products or +services of Licensee, or any third party. + +8. By clicking on the "ACCEPT" button where indicated, or by copying, +installing or otherwise using Python 1.6.1, Licensee agrees to be +bound by the terms and conditions of this License Agreement. + + ACCEPT + + +CWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2 +-------------------------------------------------- + +Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam, +The Netherlands. All rights reserved. + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Stichting Mathematisch +Centrum or CWI not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO +THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE +FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +ZERO-CLAUSE BSD LICENSE FOR CODE IN THE PYTHON DOCUMENTATION +---------------------------------------------------------------------- + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. +Copyright (c) 2014, Al Sweigart +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the {organization} nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.Copyright (c) 2017 Anthony Sottile + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE.Copyright (c) 2015-2019 Jared Hobbs + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE.Developed by ESN, an Electronic Arts Inc. studio. +Copyright (c) 2014, Electronic Arts Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: +* Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. +* Neither the name of ESN, Electronic Arts Inc. nor the +names of its contributors may be used to endorse or promote products +derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL ELECTRONIC ARTS INC. BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---- + +Portions of code from MODP_ASCII - Ascii transformations (upper/lower, etc) +https://github.com/client9/stringencoders + + Copyright 2005, 2006, 2007 + Nick Galbreath -- nickg [at] modp [dot] com + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + Neither the name of the modp.com nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + This is the standard "new" BSD license: + http://www.opensource.org/licenses/bsd-license.php + +https://github.com/client9/stringencoders/blob/cfd5c1507325ae497ea9bacdacba12c0ffd79d30/COPYING + +---- + +Numeric decoder derived from from TCL library +https://opensource.apple.com/source/tcl/tcl-14/tcl/license.terms + * Copyright (c) 1988-1993 The Regents of the University of California. + * Copyright (c) 1994 Sun Microsystems, Inc. + + This software is copyrighted by the Regents of the University of + California, Sun Microsystems, Inc., Scriptics Corporation, ActiveState + Corporation and other parties. The following terms apply to all files + associated with the software unless explicitly disclaimed in + individual files. + + The authors hereby grant permission to use, copy, modify, distribute, + and license this software and its documentation for any purpose, provided + that existing copyright notices are retained in all copies and that this + notice is included verbatim in any distributions. No written agreement, + license, or royalty fee is required for any of the authorized uses. + Modifications to this software may be copyrighted by their authors + and need not follow the licensing terms described here, provided that + the new terms are clearly indicated on the first page of each file where + they apply. + + IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY + FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY + DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE + IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE + NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR + MODIFICATIONS. + + GOVERNMENT USE: If you are acquiring this software on behalf of the + U.S. government, the Government shall have only "Restricted Rights" + in the software and related documentation as defined in the Federal + Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2). If you + are acquiring the software on behalf of the Department of Defense, the + software shall be classified as "Commercial Computer Software" and the + Government shall have only "Restricted Rights" as defined in Clause + 252.227-7013 (c) (1) of DFARs. Notwithstanding the foregoing, the + authors grant the U.S. Government and others acting in its behalf + permission to use and distribute the software in accordance with the + terms specified in this license.Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and +distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, "control" means (i) the power, direct or +indirect, to cause the direction or management of such entity, whether by +contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising +permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + +"Object" form shall mean any form resulting from mechanical transformation or +translation of a Source form, including but not limited to compiled object code, +generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made +available under the License, as indicated by a copyright notice that is included +in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that +is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative Works +shall not include works that remain separable from, or merely link (or bind by +name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative Works +thereof, that is intentionally submitted to Licensor for inclusion in the Work +by the copyright owner or by an individual or Legal Entity authorized to submit +on behalf of the copyright owner. For the purposes of this definition, +"submitted" means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor for +the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently +incorporated within the Work. + +2. Grant of Copyright License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the Work and such +Derivative Works in Source or Object form. + +3. Grant of Patent License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable (except as stated in this section) patent license to make, have +made, use, offer to sell, sell, import, and otherwise transfer the Work, where +such license applies only to those patent claims licensable by such Contributor +that are necessarily infringed by their Contribution(s) alone or by combination +of their Contribution(s) with the Work to which such Contribution(s) was +submitted. If You institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work or a +Contribution incorporated within the Work constitutes direct or contributory +patent infringement, then any patent licenses granted to You under this License +for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. + +You may reproduce and distribute copies of the Work or Derivative Works thereof +in any medium, with or without modifications, and in Source or Object form, +provided that You meet the following conditions: + +You must give any other recipients of the Work or Derivative Works a copy of +this License; and +You must cause any modified files to carry prominent notices stating that You +changed the files; and +You must retain, in the Source form of any Derivative Works that You distribute, +all copyright, patent, trademark, and attribution notices from the Source form +of the Work, excluding those notices that do not pertain to any part of the +Derivative Works; and +If the Work includes a "NOTICE" text file as part of its distribution, then any +Derivative Works that You distribute must include a readable copy of the +attribution notices contained within such NOTICE file, excluding those notices +that do not pertain to any part of the Derivative Works, in at least one of the +following places: within a NOTICE text file distributed as part of the +Derivative Works; within the Source form or documentation, if provided along +with the Derivative Works; or, within a display generated by the Derivative +Works, if and wherever such third-party notices normally appear. The contents of +the NOTICE file are for informational purposes only and do not modify the +License. You may add Your own attribution notices within Derivative Works that +You distribute, alongside or as an addendum to the NOTICE text from the Work, +provided that such additional attribution notices cannot be construed as +modifying the License. +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works as a whole, +provided Your use, reproduction, and distribution of the Work otherwise complies +with the conditions stated in this License. + +5. Submission of Contributions. + +Unless You explicitly state otherwise, any Contribution intentionally submitted +for inclusion in the Work by You to the Licensor shall be under the terms and +conditions of this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify the terms of +any separate license agreement you may have executed with Licensor regarding +such Contributions. + +6. Trademarks. + +This License does not grant permission to use the trade names, trademarks, +service marks, or product names of the Licensor, except as required for +reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. + +Unless required by applicable law or agreed to in writing, Licensor provides the +Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, +including, without limitation, any warranties or conditions of TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are +solely responsible for determining the appropriateness of using or +redistributing the Work and assume any risks associated with Your exercise of +permissions under this License. + +8. Limitation of Liability. + +In no event and under no legal theory, whether in tort (including negligence), +contract, or otherwise, unless required by applicable law (such as deliberate +and grossly negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, incidental, +or consequential damages of any character arising as a result of this License or +out of the use or inability to use the Work (including but not limited to +damages for loss of goodwill, work stoppage, computer failure or malfunction, or +any and all other commercial damages or losses), even if such Contributor has +been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. + +While redistributing the Work or Derivative Works thereof, You may choose to +offer, and charge a fee for, acceptance of support, warranty, indemnity, or +other liability obligations and/or rights consistent with this License. However, +in accepting such obligations, You may act only on Your own behalf and on Your +sole responsibility, not on behalf of any other Contributor, and only if You +agree to indemnify, defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason of your +accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets "[]" replaced with your own +identifying information. (Don't include the brackets!) The text should be +enclosed in the appropriate comment syntax for the file format. We also +recommend that a file or class name and description of purpose be included on +the same "printed page" as the copyright notice for easier identification within +third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/lib/python3.12/site-packages/pandas-2.2.3.dist-info/METADATA b/lib/python3.12/site-packages/pandas-2.2.3.dist-info/METADATA new file mode 100644 index 0000000000000000000000000000000000000000..344345f4c22cf91766ee26bc3bf91d625e7330f7 --- /dev/null +++ b/lib/python3.12/site-packages/pandas-2.2.3.dist-info/METADATA @@ -0,0 +1,1573 @@ +Metadata-Version: 2.1 +Name: pandas +Version: 2.2.3 +Summary: Powerful data structures for data analysis, time series, and statistics +Home-page: https://pandas.pydata.org +Author-Email: The Pandas Development Team +License: BSD 3-Clause License + + Copyright (c) 2008-2011, AQR Capital Management, LLC, Lambda Foundry, Inc. and PyData Development Team + All rights reserved. + + Copyright (c) 2011-2023, Open source contributors. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + Copyright (c) 2010-2019 Keith Goodman + Copyright (c) 2019 Bottleneck Developers + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE.Copyright 2017- Paul Ganssle + Copyright 2017- dateutil contributors (see AUTHORS file) + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + The above license applies to all contributions after 2017-12-01, as well as + all contributions that have been re-licensed (see AUTHORS file for the list of + contributors who have re-licensed their code). + -------------------------------------------------------------------------------- + dateutil - Extensions to the standard Python datetime module. + + Copyright (c) 2003-2011 - Gustavo Niemeyer + Copyright (c) 2012-2014 - Tomi Pieviläinen + Copyright (c) 2014-2016 - Yaron de Leeuw + Copyright (c) 2015- - Paul Ganssle + Copyright (c) 2015- - dateutil contributors (see AUTHORS file) + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + The above BSD License Applies to all code, even that also covered by Apache 2.0.# MIT License + + Copyright (c) 2019 Hadley Wickham; RStudio; and Evan Miller + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + Based on http://opensource.org/licenses/MIT + + This is a template. Complete and ship as file LICENSE the following 2 + lines (only) + + YEAR: + COPYRIGHT HOLDER: + + and specify as + + License: MIT + file LICENSE + + Copyright (c) , + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + The MIT License + + Copyright (c) 2008- Attractive Chaos + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE.musl as a whole is licensed under the following standard MIT license: + + ---------------------------------------------------------------------- + Copyright © 2005-2020 Rich Felker, et al. + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + ---------------------------------------------------------------------- + + Authors/contributors include: + + A. Wilcox + Ada Worcester + Alex Dowad + Alex Suykov + Alexander Monakov + Andre McCurdy + Andrew Kelley + Anthony G. Basile + Aric Belsito + Arvid Picciani + Bartosz Brachaczek + Benjamin Peterson + Bobby Bingham + Boris Brezillon + Brent Cook + Chris Spiegel + Clément Vasseur + Daniel Micay + Daniel Sabogal + Daurnimator + David Carlier + David Edelsohn + Denys Vlasenko + Dmitry Ivanov + Dmitry V. Levin + Drew DeVault + Emil Renner Berthing + Fangrui Song + Felix Fietkau + Felix Janda + Gianluca Anzolin + Hauke Mehrtens + He X + Hiltjo Posthuma + Isaac Dunham + Jaydeep Patil + Jens Gustedt + Jeremy Huntwork + Jo-Philipp Wich + Joakim Sindholt + John Spencer + Julien Ramseier + Justin Cormack + Kaarle Ritvanen + Khem Raj + Kylie McClain + Leah Neukirchen + Luca Barbato + Luka Perkov + M Farkas-Dyck (Strake) + Mahesh Bodapati + Markus Wichmann + Masanori Ogino + Michael Clark + Michael Forney + Mikhail Kremnyov + Natanael Copa + Nicholas J. Kain + orc + Pascal Cuoq + Patrick Oppenlander + Petr Hosek + Petr Skocik + Pierre Carrier + Reini Urban + Rich Felker + Richard Pennington + Ryan Fairfax + Samuel Holland + Segev Finer + Shiz + sin + Solar Designer + Stefan Kristiansson + Stefan O'Rear + Szabolcs Nagy + Timo Teräs + Trutz Behn + Valentin Ochs + Will Dietz + William Haddon + William Pitcock + + Portions of this software are derived from third-party works licensed + under terms compatible with the above MIT license: + + The TRE regular expression implementation (src/regex/reg* and + src/regex/tre*) is Copyright © 2001-2008 Ville Laurikari and licensed + under a 2-clause BSD license (license text in the source files). The + included version has been heavily modified by Rich Felker in 2012, in + the interests of size, simplicity, and namespace cleanliness. + + Much of the math library code (src/math/* and src/complex/*) is + Copyright © 1993,2004 Sun Microsystems or + Copyright © 2003-2011 David Schultz or + Copyright © 2003-2009 Steven G. Kargl or + Copyright © 2003-2009 Bruce D. Evans or + Copyright © 2008 Stephen L. Moshier or + Copyright © 2017-2018 Arm Limited + and labelled as such in comments in the individual source files. All + have been licensed under extremely permissive terms. + + The ARM memcpy code (src/string/arm/memcpy.S) is Copyright © 2008 + The Android Open Source Project and is licensed under a two-clause BSD + license. It was taken from Bionic libc, used on Android. + + The AArch64 memcpy and memset code (src/string/aarch64/*) are + Copyright © 1999-2019, Arm Limited. + + The implementation of DES for crypt (src/crypt/crypt_des.c) is + Copyright © 1994 David Burren. It is licensed under a BSD license. + + The implementation of blowfish crypt (src/crypt/crypt_blowfish.c) was + originally written by Solar Designer and placed into the public + domain. The code also comes with a fallback permissive license for use + in jurisdictions that may not recognize the public domain. + + The smoothsort implementation (src/stdlib/qsort.c) is Copyright © 2011 + Valentin Ochs and is licensed under an MIT-style license. + + The x86_64 port was written by Nicholas J. Kain and is licensed under + the standard MIT terms. + + The mips and microblaze ports were originally written by Richard + Pennington for use in the ellcc project. The original code was adapted + by Rich Felker for build system and code conventions during upstream + integration. It is licensed under the standard MIT terms. + + The mips64 port was contributed by Imagination Technologies and is + licensed under the standard MIT terms. + + The powerpc port was also originally written by Richard Pennington, + and later supplemented and integrated by John Spencer. It is licensed + under the standard MIT terms. + + All other files which have no copyright comments are original works + produced specifically for use as part of this library, written either + by Rich Felker, the main author of the library, or by one or more + contibutors listed above. Details on authorship of individual files + can be found in the git version control history of the project. The + omission of copyright and license comments in each file is in the + interest of source tree size. + + In addition, permission is hereby granted for all public header files + (include/* and arch/*/bits/*) and crt files intended to be linked into + applications (crt/*, ldso/dlstart.c, and arch/*/crt_arch.h) to omit + the copyright notice and permission notice otherwise required by the + license, and to use these files without any requirement of + attribution. These files include substantial contributions from: + + Bobby Bingham + John Spencer + Nicholas J. Kain + Rich Felker + Richard Pennington + Stefan Kristiansson + Szabolcs Nagy + + all of whom have explicitly granted such permission. + + This file previously contained text expressing a belief that most of + the files covered by the above exception were sufficiently trivial not + to be subject to copyright, resulting in confusion over whether it + negated the permissions granted in the license. In the spirit of + permissive licensing, and of not having licensing issues being an + obstacle to adoption, that text has been removed.Copyright (c) 2005-2023, NumPy Developers. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + + * Neither the name of the NumPy Developers nor the names of any + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + + Copyright (c) Donald Stufft and individual contributors. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.A. HISTORY OF THE SOFTWARE + ========================== + + Python was created in the early 1990s by Guido van Rossum at Stichting + Mathematisch Centrum (CWI, see https://www.cwi.nl) in the Netherlands + as a successor of a language called ABC. Guido remains Python's + principal author, although it includes many contributions from others. + + In 1995, Guido continued his work on Python at the Corporation for + National Research Initiatives (CNRI, see https://www.cnri.reston.va.us) + in Reston, Virginia where he released several versions of the + software. + + In May 2000, Guido and the Python core development team moved to + BeOpen.com to form the BeOpen PythonLabs team. In October of the same + year, the PythonLabs team moved to Digital Creations, which became + Zope Corporation. In 2001, the Python Software Foundation (PSF, see + https://www.python.org/psf/) was formed, a non-profit organization + created specifically to own Python-related Intellectual Property. + Zope Corporation was a sponsoring member of the PSF. + + All Python releases are Open Source (see https://opensource.org for + the Open Source Definition). Historically, most, but not all, Python + releases have also been GPL-compatible; the table below summarizes + the various releases. + + Release Derived Year Owner GPL- + from compatible? (1) + + 0.9.0 thru 1.2 1991-1995 CWI yes + 1.3 thru 1.5.2 1.2 1995-1999 CNRI yes + 1.6 1.5.2 2000 CNRI no + 2.0 1.6 2000 BeOpen.com no + 1.6.1 1.6 2001 CNRI yes (2) + 2.1 2.0+1.6.1 2001 PSF no + 2.0.1 2.0+1.6.1 2001 PSF yes + 2.1.1 2.1+2.0.1 2001 PSF yes + 2.1.2 2.1.1 2002 PSF yes + 2.1.3 2.1.2 2002 PSF yes + 2.2 and above 2.1.1 2001-now PSF yes + + Footnotes: + + (1) GPL-compatible doesn't mean that we're distributing Python under + the GPL. All Python licenses, unlike the GPL, let you distribute + a modified version without making your changes open source. The + GPL-compatible licenses make it possible to combine Python with + other software that is released under the GPL; the others don't. + + (2) According to Richard Stallman, 1.6.1 is not GPL-compatible, + because its license has a choice of law clause. According to + CNRI, however, Stallman's lawyer has told CNRI's lawyer that 1.6.1 + is "not incompatible" with the GPL. + + Thanks to the many outside volunteers who have worked under Guido's + direction to make these releases possible. + + + B. TERMS AND CONDITIONS FOR ACCESSING OR OTHERWISE USING PYTHON + =============================================================== + + Python software and documentation are licensed under the + Python Software Foundation License Version 2. + + Starting with Python 3.8.6, examples, recipes, and other code in + the documentation are dual licensed under the PSF License Version 2 + and the Zero-Clause BSD license. + + Some software incorporated into Python is under different licenses. + The licenses are listed with code falling under that license. + + + PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 + -------------------------------------------- + + 1. This LICENSE AGREEMENT is between the Python Software Foundation + ("PSF"), and the Individual or Organization ("Licensee") accessing and + otherwise using this software ("Python") in source or binary form and + its associated documentation. + + 2. Subject to the terms and conditions of this License Agreement, PSF hereby + grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, + analyze, test, perform and/or display publicly, prepare derivative works, + distribute, and otherwise use Python alone or in any derivative version, + provided, however, that PSF's License Agreement and PSF's notice of copyright, + i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, + 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023 Python Software Foundation; + All Rights Reserved" are retained in Python alone or in any derivative version + prepared by Licensee. + + 3. In the event Licensee prepares a derivative work that is based on + or incorporates Python or any part thereof, and wants to make + the derivative work available to others as provided herein, then + Licensee hereby agrees to include in any such work a brief summary of + the changes made to Python. + + 4. PSF is making Python available to Licensee on an "AS IS" + basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR + IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND + DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS + FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT + INFRINGE ANY THIRD PARTY RIGHTS. + + 5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON + FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS + A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON, + OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + + 6. This License Agreement will automatically terminate upon a material + breach of its terms and conditions. + + 7. Nothing in this License Agreement shall be deemed to create any + relationship of agency, partnership, or joint venture between PSF and + Licensee. This License Agreement does not grant permission to use PSF + trademarks or trade name in a trademark sense to endorse or promote + products or services of Licensee, or any third party. + + 8. By copying, installing or otherwise using Python, Licensee + agrees to be bound by the terms and conditions of this License + Agreement. + + + BEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0 + ------------------------------------------- + + BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1 + + 1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an + office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the + Individual or Organization ("Licensee") accessing and otherwise using + this software in source or binary form and its associated + documentation ("the Software"). + + 2. Subject to the terms and conditions of this BeOpen Python License + Agreement, BeOpen hereby grants Licensee a non-exclusive, + royalty-free, world-wide license to reproduce, analyze, test, perform + and/or display publicly, prepare derivative works, distribute, and + otherwise use the Software alone or in any derivative version, + provided, however, that the BeOpen Python License is retained in the + Software, alone or in any derivative version prepared by Licensee. + + 3. BeOpen is making the Software available to Licensee on an "AS IS" + basis. BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR + IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND + DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS + FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT + INFRINGE ANY THIRD PARTY RIGHTS. + + 4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE + SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS + AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY + DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + + 5. This License Agreement will automatically terminate upon a material + breach of its terms and conditions. + + 6. This License Agreement shall be governed by and interpreted in all + respects by the law of the State of California, excluding conflict of + law provisions. Nothing in this License Agreement shall be deemed to + create any relationship of agency, partnership, or joint venture + between BeOpen and Licensee. This License Agreement does not grant + permission to use BeOpen trademarks or trade names in a trademark + sense to endorse or promote products or services of Licensee, or any + third party. As an exception, the "BeOpen Python" logos available at + http://www.pythonlabs.com/logos.html may be used according to the + permissions granted on that web page. + + 7. By copying, installing or otherwise using the software, Licensee + agrees to be bound by the terms and conditions of this License + Agreement. + + + CNRI LICENSE AGREEMENT FOR PYTHON 1.6.1 + --------------------------------------- + + 1. This LICENSE AGREEMENT is between the Corporation for National + Research Initiatives, having an office at 1895 Preston White Drive, + Reston, VA 20191 ("CNRI"), and the Individual or Organization + ("Licensee") accessing and otherwise using Python 1.6.1 software in + source or binary form and its associated documentation. + + 2. Subject to the terms and conditions of this License Agreement, CNRI + hereby grants Licensee a nonexclusive, royalty-free, world-wide + license to reproduce, analyze, test, perform and/or display publicly, + prepare derivative works, distribute, and otherwise use Python 1.6.1 + alone or in any derivative version, provided, however, that CNRI's + License Agreement and CNRI's notice of copyright, i.e., "Copyright (c) + 1995-2001 Corporation for National Research Initiatives; All Rights + Reserved" are retained in Python 1.6.1 alone or in any derivative + version prepared by Licensee. Alternately, in lieu of CNRI's License + Agreement, Licensee may substitute the following text (omitting the + quotes): "Python 1.6.1 is made available subject to the terms and + conditions in CNRI's License Agreement. This Agreement together with + Python 1.6.1 may be located on the internet using the following + unique, persistent identifier (known as a handle): 1895.22/1013. This + Agreement may also be obtained from a proxy server on the internet + using the following URL: http://hdl.handle.net/1895.22/1013". + + 3. In the event Licensee prepares a derivative work that is based on + or incorporates Python 1.6.1 or any part thereof, and wants to make + the derivative work available to others as provided herein, then + Licensee hereby agrees to include in any such work a brief summary of + the changes made to Python 1.6.1. + + 4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS" + basis. CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR + IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND + DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS + FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT + INFRINGE ANY THIRD PARTY RIGHTS. + + 5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON + 1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS + A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1, + OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + + 6. This License Agreement will automatically terminate upon a material + breach of its terms and conditions. + + 7. This License Agreement shall be governed by the federal + intellectual property law of the United States, including without + limitation the federal copyright law, and, to the extent such + U.S. federal law does not apply, by the law of the Commonwealth of + Virginia, excluding Virginia's conflict of law provisions. + Notwithstanding the foregoing, with regard to derivative works based + on Python 1.6.1 that incorporate non-separable material that was + previously distributed under the GNU General Public License (GPL), the + law of the Commonwealth of Virginia shall govern this License + Agreement only as to issues arising under or with respect to + Paragraphs 4, 5, and 7 of this License Agreement. Nothing in this + License Agreement shall be deemed to create any relationship of + agency, partnership, or joint venture between CNRI and Licensee. This + License Agreement does not grant permission to use CNRI trademarks or + trade name in a trademark sense to endorse or promote products or + services of Licensee, or any third party. + + 8. By clicking on the "ACCEPT" button where indicated, or by copying, + installing or otherwise using Python 1.6.1, Licensee agrees to be + bound by the terms and conditions of this License Agreement. + + ACCEPT + + + CWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2 + -------------------------------------------------- + + Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam, + The Netherlands. All rights reserved. + + Permission to use, copy, modify, and distribute this software and its + documentation for any purpose and without fee is hereby granted, + provided that the above copyright notice appear in all copies and that + both that copyright notice and this permission notice appear in + supporting documentation, and that the name of Stichting Mathematisch + Centrum or CWI not be used in advertising or publicity pertaining to + distribution of the software without specific, written prior + permission. + + STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO + THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE + FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT + OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + ZERO-CLAUSE BSD LICENSE FOR CODE IN THE PYTHON DOCUMENTATION + ---------------------------------------------------------------------- + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + PERFORMANCE OF THIS SOFTWARE. + Copyright (c) 2014, Al Sweigart + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + * Neither the name of the {organization} nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.Copyright (c) 2017 Anthony Sottile + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE.Copyright (c) 2015-2019 Jared Hobbs + + Permission is hereby granted, free of charge, to any person obtaining a copy of + this software and associated documentation files (the "Software"), to deal in + the Software without restriction, including without limitation the rights to + use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is furnished to do + so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE.Developed by ESN, an Electronic Arts Inc. studio. + Copyright (c) 2014, Electronic Arts Inc. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of ESN, Electronic Arts Inc. nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL ELECTRONIC ARTS INC. BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + ---- + + Portions of code from MODP_ASCII - Ascii transformations (upper/lower, etc) + https://github.com/client9/stringencoders + + Copyright 2005, 2006, 2007 + Nick Galbreath -- nickg [at] modp [dot] com + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + Neither the name of the modp.com nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + This is the standard "new" BSD license: + http://www.opensource.org/licenses/bsd-license.php + + https://github.com/client9/stringencoders/blob/cfd5c1507325ae497ea9bacdacba12c0ffd79d30/COPYING + + ---- + + Numeric decoder derived from from TCL library + https://opensource.apple.com/source/tcl/tcl-14/tcl/license.terms + * Copyright (c) 1988-1993 The Regents of the University of California. + * Copyright (c) 1994 Sun Microsystems, Inc. + + This software is copyrighted by the Regents of the University of + California, Sun Microsystems, Inc., Scriptics Corporation, ActiveState + Corporation and other parties. The following terms apply to all files + associated with the software unless explicitly disclaimed in + individual files. + + The authors hereby grant permission to use, copy, modify, distribute, + and license this software and its documentation for any purpose, provided + that existing copyright notices are retained in all copies and that this + notice is included verbatim in any distributions. No written agreement, + license, or royalty fee is required for any of the authorized uses. + Modifications to this software may be copyrighted by their authors + and need not follow the licensing terms described here, provided that + the new terms are clearly indicated on the first page of each file where + they apply. + + IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY + FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY + DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE + IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE + NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR + MODIFICATIONS. + + GOVERNMENT USE: If you are acquiring this software on behalf of the + U.S. government, the Government shall have only "Restricted Rights" + in the software and related documentation as defined in the Federal + Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2). If you + are acquiring the software on behalf of the Department of Defense, the + software shall be classified as "Commercial Computer Software" and the + Government shall have only "Restricted Rights" as defined in Clause + 252.227-7013 (c) (1) of DFARs. Notwithstanding the foregoing, the + authors grant the U.S. Government and others acting in its behalf + permission to use and distribute the software in accordance with the + terms specified in this license.Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, and + distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by the copyright + owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all other entities + that control, are controlled by, or are under common control with that entity. + For the purposes of this definition, "control" means (i) the power, direct or + indirect, to cause the direction or management of such entity, whether by + contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity exercising + permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, including + but not limited to software source code, documentation source, and configuration + files. + + "Object" form shall mean any form resulting from mechanical transformation or + translation of a Source form, including but not limited to compiled object code, + generated documentation, and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or Object form, made + available under the License, as indicated by a copyright notice that is included + in or attached to the work (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object form, that + is based on (or derived from) the Work and for which the editorial revisions, + annotations, elaborations, or other modifications represent, as a whole, an + original work of authorship. For the purposes of this License, Derivative Works + shall not include works that remain separable from, or merely link (or bind by + name) to the interfaces of, the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including the original version + of the Work and any modifications or additions to that Work or Derivative Works + thereof, that is intentionally submitted to Licensor for inclusion in the Work + by the copyright owner or by an individual or Legal Entity authorized to submit + on behalf of the copyright owner. For the purposes of this definition, + "submitted" means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, and + issue tracking systems that are managed by, or on behalf of, the Licensor for + the purpose of discussing and improving the Work, but excluding communication + that is conspicuously marked or otherwise designated in writing by the copyright + owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity on behalf + of whom a Contribution has been received by Licensor and subsequently + incorporated within the Work. + + 2. Grant of Copyright License. + + Subject to the terms and conditions of this License, each Contributor hereby + grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, + irrevocable copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the Work and such + Derivative Works in Source or Object form. + + 3. Grant of Patent License. + + Subject to the terms and conditions of this License, each Contributor hereby + grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, + irrevocable (except as stated in this section) patent license to make, have + made, use, offer to sell, sell, import, and otherwise transfer the Work, where + such license applies only to those patent claims licensable by such Contributor + that are necessarily infringed by their Contribution(s) alone or by combination + of their Contribution(s) with the Work to which such Contribution(s) was + submitted. If You institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work or a + Contribution incorporated within the Work constitutes direct or contributory + patent infringement, then any patent licenses granted to You under this License + for that Work shall terminate as of the date such litigation is filed. + + 4. Redistribution. + + You may reproduce and distribute copies of the Work or Derivative Works thereof + in any medium, with or without modifications, and in Source or Object form, + provided that You meet the following conditions: + + You must give any other recipients of the Work or Derivative Works a copy of + this License; and + You must cause any modified files to carry prominent notices stating that You + changed the files; and + You must retain, in the Source form of any Derivative Works that You distribute, + all copyright, patent, trademark, and attribution notices from the Source form + of the Work, excluding those notices that do not pertain to any part of the + Derivative Works; and + If the Work includes a "NOTICE" text file as part of its distribution, then any + Derivative Works that You distribute must include a readable copy of the + attribution notices contained within such NOTICE file, excluding those notices + that do not pertain to any part of the Derivative Works, in at least one of the + following places: within a NOTICE text file distributed as part of the + Derivative Works; within the Source form or documentation, if provided along + with the Derivative Works; or, within a display generated by the Derivative + Works, if and wherever such third-party notices normally appear. The contents of + the NOTICE file are for informational purposes only and do not modify the + License. You may add Your own attribution notices within Derivative Works that + You distribute, alongside or as an addendum to the NOTICE text from the Work, + provided that such additional attribution notices cannot be construed as + modifying the License. + You may add Your own copyright statement to Your modifications and may provide + additional or different license terms and conditions for use, reproduction, or + distribution of Your modifications, or for any such Derivative Works as a whole, + provided Your use, reproduction, and distribution of the Work otherwise complies + with the conditions stated in this License. + + 5. Submission of Contributions. + + Unless You explicitly state otherwise, any Contribution intentionally submitted + for inclusion in the Work by You to the Licensor shall be under the terms and + conditions of this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify the terms of + any separate license agreement you may have executed with Licensor regarding + such Contributions. + + 6. Trademarks. + + This License does not grant permission to use the trade names, trademarks, + service marks, or product names of the Licensor, except as required for + reasonable and customary use in describing the origin of the Work and + reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. + + Unless required by applicable law or agreed to in writing, Licensor provides the + Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + including, without limitation, any warranties or conditions of TITLE, + NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are + solely responsible for determining the appropriateness of using or + redistributing the Work and assume any risks associated with Your exercise of + permissions under this License. + + 8. Limitation of Liability. + + In no event and under no legal theory, whether in tort (including negligence), + contract, or otherwise, unless required by applicable law (such as deliberate + and grossly negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, incidental, + or consequential damages of any character arising as a result of this License or + out of the use or inability to use the Work (including but not limited to + damages for loss of goodwill, work stoppage, computer failure or malfunction, or + any and all other commercial damages or losses), even if such Contributor has + been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. + + While redistributing the Work or Derivative Works thereof, You may choose to + offer, and charge a fee for, acceptance of support, warranty, indemnity, or + other liability obligations and/or rights consistent with this License. However, + in accepting such obligations, You may act only on Your own behalf and on Your + sole responsibility, not on behalf of any other Contributor, and only if You + agree to indemnify, defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason of your + accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work + + To apply the Apache License to your work, attach the following boilerplate + notice, with the fields enclosed by brackets "[]" replaced with your own + identifying information. (Don't include the brackets!) The text should be + enclosed in the appropriate comment syntax for the file format. We also + recommend that a file or class name and description of purpose be included on + the same "printed page" as the copyright notice for easier identification within + third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Console +Classifier: Intended Audience :: Science/Research +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Cython +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +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: Topic :: Scientific/Engineering +Project-URL: Homepage, https://pandas.pydata.org +Project-URL: Documentation, https://pandas.pydata.org/docs/ +Project-URL: Repository, https://github.com/pandas-dev/pandas +Requires-Python: >=3.9 +Requires-Dist: numpy>=1.22.4; python_version < "3.11" +Requires-Dist: numpy>=1.23.2; python_version == "3.11" +Requires-Dist: numpy>=1.26.0; python_version >= "3.12" +Requires-Dist: python-dateutil>=2.8.2 +Requires-Dist: pytz>=2020.1 +Requires-Dist: tzdata>=2022.7 +Requires-Dist: hypothesis>=6.46.1; extra == "test" +Requires-Dist: pytest>=7.3.2; extra == "test" +Requires-Dist: pytest-xdist>=2.2.0; extra == "test" +Requires-Dist: pyarrow>=10.0.1; extra == "pyarrow" +Requires-Dist: bottleneck>=1.3.6; extra == "performance" +Requires-Dist: numba>=0.56.4; extra == "performance" +Requires-Dist: numexpr>=2.8.4; extra == "performance" +Requires-Dist: scipy>=1.10.0; extra == "computation" +Requires-Dist: xarray>=2022.12.0; extra == "computation" +Requires-Dist: fsspec>=2022.11.0; extra == "fss" +Requires-Dist: s3fs>=2022.11.0; extra == "aws" +Requires-Dist: gcsfs>=2022.11.0; extra == "gcp" +Requires-Dist: pandas-gbq>=0.19.0; extra == "gcp" +Requires-Dist: odfpy>=1.4.1; extra == "excel" +Requires-Dist: openpyxl>=3.1.0; extra == "excel" +Requires-Dist: python-calamine>=0.1.7; extra == "excel" +Requires-Dist: pyxlsb>=1.0.10; extra == "excel" +Requires-Dist: xlrd>=2.0.1; extra == "excel" +Requires-Dist: xlsxwriter>=3.0.5; extra == "excel" +Requires-Dist: pyarrow>=10.0.1; extra == "parquet" +Requires-Dist: pyarrow>=10.0.1; extra == "feather" +Requires-Dist: tables>=3.8.0; extra == "hdf5" +Requires-Dist: pyreadstat>=1.2.0; extra == "spss" +Requires-Dist: SQLAlchemy>=2.0.0; extra == "postgresql" +Requires-Dist: psycopg2>=2.9.6; extra == "postgresql" +Requires-Dist: adbc-driver-postgresql>=0.8.0; extra == "postgresql" +Requires-Dist: SQLAlchemy>=2.0.0; extra == "mysql" +Requires-Dist: pymysql>=1.0.2; extra == "mysql" +Requires-Dist: SQLAlchemy>=2.0.0; extra == "sql-other" +Requires-Dist: adbc-driver-postgresql>=0.8.0; extra == "sql-other" +Requires-Dist: adbc-driver-sqlite>=0.8.0; extra == "sql-other" +Requires-Dist: beautifulsoup4>=4.11.2; extra == "html" +Requires-Dist: html5lib>=1.1; extra == "html" +Requires-Dist: lxml>=4.9.2; extra == "html" +Requires-Dist: lxml>=4.9.2; extra == "xml" +Requires-Dist: matplotlib>=3.6.3; extra == "plot" +Requires-Dist: jinja2>=3.1.2; extra == "output-formatting" +Requires-Dist: tabulate>=0.9.0; extra == "output-formatting" +Requires-Dist: PyQt5>=5.15.9; extra == "clipboard" +Requires-Dist: qtpy>=2.3.0; extra == "clipboard" +Requires-Dist: zstandard>=0.19.0; extra == "compression" +Requires-Dist: dataframe-api-compat>=0.1.7; extra == "consortium-standard" +Requires-Dist: adbc-driver-postgresql>=0.8.0; extra == "all" +Requires-Dist: adbc-driver-sqlite>=0.8.0; extra == "all" +Requires-Dist: beautifulsoup4>=4.11.2; extra == "all" +Requires-Dist: bottleneck>=1.3.6; extra == "all" +Requires-Dist: dataframe-api-compat>=0.1.7; extra == "all" +Requires-Dist: fastparquet>=2022.12.0; extra == "all" +Requires-Dist: fsspec>=2022.11.0; extra == "all" +Requires-Dist: gcsfs>=2022.11.0; extra == "all" +Requires-Dist: html5lib>=1.1; extra == "all" +Requires-Dist: hypothesis>=6.46.1; extra == "all" +Requires-Dist: jinja2>=3.1.2; extra == "all" +Requires-Dist: lxml>=4.9.2; extra == "all" +Requires-Dist: matplotlib>=3.6.3; extra == "all" +Requires-Dist: numba>=0.56.4; extra == "all" +Requires-Dist: numexpr>=2.8.4; extra == "all" +Requires-Dist: odfpy>=1.4.1; extra == "all" +Requires-Dist: openpyxl>=3.1.0; extra == "all" +Requires-Dist: pandas-gbq>=0.19.0; extra == "all" +Requires-Dist: psycopg2>=2.9.6; extra == "all" +Requires-Dist: pyarrow>=10.0.1; extra == "all" +Requires-Dist: pymysql>=1.0.2; extra == "all" +Requires-Dist: PyQt5>=5.15.9; extra == "all" +Requires-Dist: pyreadstat>=1.2.0; extra == "all" +Requires-Dist: pytest>=7.3.2; extra == "all" +Requires-Dist: pytest-xdist>=2.2.0; extra == "all" +Requires-Dist: python-calamine>=0.1.7; extra == "all" +Requires-Dist: pyxlsb>=1.0.10; extra == "all" +Requires-Dist: qtpy>=2.3.0; extra == "all" +Requires-Dist: scipy>=1.10.0; extra == "all" +Requires-Dist: s3fs>=2022.11.0; extra == "all" +Requires-Dist: SQLAlchemy>=2.0.0; extra == "all" +Requires-Dist: tables>=3.8.0; extra == "all" +Requires-Dist: tabulate>=0.9.0; extra == "all" +Requires-Dist: xarray>=2022.12.0; extra == "all" +Requires-Dist: xlrd>=2.0.1; extra == "all" +Requires-Dist: xlsxwriter>=3.0.5; extra == "all" +Requires-Dist: zstandard>=0.19.0; extra == "all" +Provides-Extra: test +Provides-Extra: pyarrow +Provides-Extra: performance +Provides-Extra: computation +Provides-Extra: fss +Provides-Extra: aws +Provides-Extra: gcp +Provides-Extra: excel +Provides-Extra: parquet +Provides-Extra: feather +Provides-Extra: hdf5 +Provides-Extra: spss +Provides-Extra: postgresql +Provides-Extra: mysql +Provides-Extra: sql-other +Provides-Extra: html +Provides-Extra: xml +Provides-Extra: plot +Provides-Extra: output-formatting +Provides-Extra: clipboard +Provides-Extra: compression +Provides-Extra: consortium-standard +Provides-Extra: all +Description-Content-Type: text/markdown + +
+
+
+ +----------------- + +# pandas: powerful Python data analysis toolkit + +| | | +| --- | --- | +| Testing | [![CI - Test](https://github.com/pandas-dev/pandas/actions/workflows/unit-tests.yml/badge.svg)](https://github.com/pandas-dev/pandas/actions/workflows/unit-tests.yml) [![Coverage](https://codecov.io/github/pandas-dev/pandas/coverage.svg?branch=main)](https://codecov.io/gh/pandas-dev/pandas) | +| Package | [![PyPI Latest Release](https://img.shields.io/pypi/v/pandas.svg)](https://pypi.org/project/pandas/) [![PyPI Downloads](https://img.shields.io/pypi/dm/pandas.svg?label=PyPI%20downloads)](https://pypi.org/project/pandas/) [![Conda Latest Release](https://anaconda.org/conda-forge/pandas/badges/version.svg)](https://anaconda.org/conda-forge/pandas) [![Conda Downloads](https://img.shields.io/conda/dn/conda-forge/pandas.svg?label=Conda%20downloads)](https://anaconda.org/conda-forge/pandas) | +| Meta | [![Powered by NumFOCUS](https://img.shields.io/badge/powered%20by-NumFOCUS-orange.svg?style=flat&colorA=E1523D&colorB=007D8A)](https://numfocus.org) [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.3509134.svg)](https://doi.org/10.5281/zenodo.3509134) [![License - BSD 3-Clause](https://img.shields.io/pypi/l/pandas.svg)](https://github.com/pandas-dev/pandas/blob/main/LICENSE) [![Slack](https://img.shields.io/badge/join_Slack-information-brightgreen.svg?logo=slack)](https://pandas.pydata.org/docs/dev/development/community.html?highlight=slack#community-slack) | + + +## What is it? + +**pandas** is a Python package that provides fast, flexible, and expressive data +structures designed to make working with "relational" or "labeled" data both +easy and intuitive. It aims to be the fundamental high-level building block for +doing practical, **real world** data analysis in Python. Additionally, it has +the broader goal of becoming **the most powerful and flexible open source data +analysis / manipulation tool available in any language**. It is already well on +its way towards this goal. + +## Table of Contents + +- [Main Features](#main-features) +- [Where to get it](#where-to-get-it) +- [Dependencies](#dependencies) +- [Installation from sources](#installation-from-sources) +- [License](#license) +- [Documentation](#documentation) +- [Background](#background) +- [Getting Help](#getting-help) +- [Discussion and Development](#discussion-and-development) +- [Contributing to pandas](#contributing-to-pandas) + +## Main Features +Here are just a few of the things that pandas does well: + + - Easy handling of [**missing data**][missing-data] (represented as + `NaN`, `NA`, or `NaT`) in floating point as well as non-floating point data + - Size mutability: columns can be [**inserted and + deleted**][insertion-deletion] from DataFrame and higher dimensional + objects + - Automatic and explicit [**data alignment**][alignment]: objects can + be explicitly aligned to a set of labels, or the user can simply + ignore the labels and let `Series`, `DataFrame`, etc. automatically + align the data for you in computations + - Powerful, flexible [**group by**][groupby] functionality to perform + split-apply-combine operations on data sets, for both aggregating + and transforming data + - Make it [**easy to convert**][conversion] ragged, + differently-indexed data in other Python and NumPy data structures + into DataFrame objects + - Intelligent label-based [**slicing**][slicing], [**fancy + indexing**][fancy-indexing], and [**subsetting**][subsetting] of + large data sets + - Intuitive [**merging**][merging] and [**joining**][joining] data + sets + - Flexible [**reshaping**][reshape] and [**pivoting**][pivot-table] of + data sets + - [**Hierarchical**][mi] labeling of axes (possible to have multiple + labels per tick) + - Robust IO tools for loading data from [**flat files**][flat-files] + (CSV and delimited), [**Excel files**][excel], [**databases**][db], + and saving/loading data from the ultrafast [**HDF5 format**][hdfstore] + - [**Time series**][timeseries]-specific functionality: date range + generation and frequency conversion, moving window statistics, + date shifting and lagging + + + [missing-data]: https://pandas.pydata.org/pandas-docs/stable/user_guide/missing_data.html + [insertion-deletion]: https://pandas.pydata.org/pandas-docs/stable/user_guide/dsintro.html#column-selection-addition-deletion + [alignment]: https://pandas.pydata.org/pandas-docs/stable/user_guide/dsintro.html?highlight=alignment#intro-to-data-structures + [groupby]: https://pandas.pydata.org/pandas-docs/stable/user_guide/groupby.html#group-by-split-apply-combine + [conversion]: https://pandas.pydata.org/pandas-docs/stable/user_guide/dsintro.html#dataframe + [slicing]: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#slicing-ranges + [fancy-indexing]: https://pandas.pydata.org/pandas-docs/stable/user_guide/advanced.html#advanced + [subsetting]: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#boolean-indexing + [merging]: https://pandas.pydata.org/pandas-docs/stable/user_guide/merging.html#database-style-dataframe-or-named-series-joining-merging + [joining]: https://pandas.pydata.org/pandas-docs/stable/user_guide/merging.html#joining-on-index + [reshape]: https://pandas.pydata.org/pandas-docs/stable/user_guide/reshaping.html + [pivot-table]: https://pandas.pydata.org/pandas-docs/stable/user_guide/reshaping.html + [mi]: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#hierarchical-indexing-multiindex + [flat-files]: https://pandas.pydata.org/pandas-docs/stable/user_guide/io.html#csv-text-files + [excel]: https://pandas.pydata.org/pandas-docs/stable/user_guide/io.html#excel-files + [db]: https://pandas.pydata.org/pandas-docs/stable/user_guide/io.html#sql-queries + [hdfstore]: https://pandas.pydata.org/pandas-docs/stable/user_guide/io.html#hdf5-pytables + [timeseries]: https://pandas.pydata.org/pandas-docs/stable/user_guide/timeseries.html#time-series-date-functionality + +## Where to get it +The source code is currently hosted on GitHub at: +https://github.com/pandas-dev/pandas + +Binary installers for the latest released version are available at the [Python +Package Index (PyPI)](https://pypi.org/project/pandas) and on [Conda](https://docs.conda.io/en/latest/). + +```sh +# conda +conda install -c conda-forge pandas +``` + +```sh +# or PyPI +pip install pandas +``` + +The list of changes to pandas between each release can be found +[here](https://pandas.pydata.org/pandas-docs/stable/whatsnew/index.html). For full +details, see the commit logs at https://github.com/pandas-dev/pandas. + +## Dependencies +- [NumPy - Adds support for large, multi-dimensional arrays, matrices and high-level mathematical functions to operate on these arrays](https://www.numpy.org) +- [python-dateutil - Provides powerful extensions to the standard datetime module](https://dateutil.readthedocs.io/en/stable/index.html) +- [pytz - Brings the Olson tz database into Python which allows accurate and cross platform timezone calculations](https://github.com/stub42/pytz) + +See the [full installation instructions](https://pandas.pydata.org/pandas-docs/stable/install.html#dependencies) for minimum supported versions of required, recommended and optional dependencies. + +## Installation from sources +To install pandas from source you need [Cython](https://cython.org/) in addition to the normal +dependencies above. Cython can be installed from PyPI: + +```sh +pip install cython +``` + +In the `pandas` directory (same one where you found this file after +cloning the git repo), execute: + +```sh +pip install . +``` + +or for installing in [development mode](https://pip.pypa.io/en/latest/cli/pip_install/#install-editable): + + +```sh +python -m pip install -ve . --no-build-isolation --config-settings=editable-verbose=true +``` + +See the full instructions for [installing from source](https://pandas.pydata.org/docs/dev/development/contributing_environment.html). + +## License +[BSD 3](LICENSE) + +## Documentation +The official documentation is hosted on [PyData.org](https://pandas.pydata.org/pandas-docs/stable/). + +## Background +Work on ``pandas`` started at [AQR](https://www.aqr.com/) (a quantitative hedge fund) in 2008 and +has been under active development since then. + +## Getting Help + +For usage questions, the best place to go to is [StackOverflow](https://stackoverflow.com/questions/tagged/pandas). +Further, general questions and discussions can also take place on the [pydata mailing list](https://groups.google.com/forum/?fromgroups#!forum/pydata). + +## Discussion and Development +Most development discussions take place on GitHub in this repo, via the [GitHub issue tracker](https://github.com/pandas-dev/pandas/issues). + +Further, the [pandas-dev mailing list](https://mail.python.org/mailman/listinfo/pandas-dev) can also be used for specialized discussions or design issues, and a [Slack channel](https://pandas.pydata.org/docs/dev/development/community.html?highlight=slack#community-slack) is available for quick development related questions. + +There are also frequent [community meetings](https://pandas.pydata.org/docs/dev/development/community.html#community-meeting) for project maintainers open to the community as well as monthly [new contributor meetings](https://pandas.pydata.org/docs/dev/development/community.html#new-contributor-meeting) to help support new contributors. + +Additional information on the communication channels can be found on the [contributor community](https://pandas.pydata.org/docs/development/community.html) page. + +## Contributing to pandas + +[![Open Source Helpers](https://www.codetriage.com/pandas-dev/pandas/badges/users.svg)](https://www.codetriage.com/pandas-dev/pandas) + +All contributions, bug reports, bug fixes, documentation improvements, enhancements, and ideas are welcome. + +A detailed overview on how to contribute can be found in the **[contributing guide](https://pandas.pydata.org/docs/dev/development/contributing.html)**. + +If you are simply looking to start working with the pandas codebase, navigate to the [GitHub "issues" tab](https://github.com/pandas-dev/pandas/issues) and start looking through interesting issues. There are a number of issues listed under [Docs](https://github.com/pandas-dev/pandas/issues?labels=Docs&sort=updated&state=open) and [good first issue](https://github.com/pandas-dev/pandas/issues?labels=good+first+issue&sort=updated&state=open) where you could start out. + +You can also triage issues which may include reproducing bug reports, or asking for vital information such as version numbers or reproduction instructions. If you would like to start triaging issues, one easy way to get started is to [subscribe to pandas on CodeTriage](https://www.codetriage.com/pandas-dev/pandas). + +Or maybe through using pandas you have an idea of your own or are looking for something in the documentation and thinking ‘this can be improved’...you can do something about it! + +Feel free to ask questions on the [mailing list](https://groups.google.com/forum/?fromgroups#!forum/pydata) or on [Slack](https://pandas.pydata.org/docs/dev/development/community.html?highlight=slack#community-slack). + +As contributors and maintainers to this project, you are expected to abide by pandas' code of conduct. More information can be found at: [Contributor Code of Conduct](https://github.com/pandas-dev/.github/blob/master/CODE_OF_CONDUCT.md) + +
+ +[Go to Top](#table-of-contents) diff --git a/lib/python3.12/site-packages/pandas-2.2.3.dist-info/RECORD b/lib/python3.12/site-packages/pandas-2.2.3.dist-info/RECORD new file mode 100644 index 0000000000000000000000000000000000000000..a65bf4b290db15b16949b8ae032b1bc1359ff01d --- /dev/null +++ b/lib/python3.12/site-packages/pandas-2.2.3.dist-info/RECORD @@ -0,0 +1,2922 @@ +pandas-2.2.3.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +pandas-2.2.3.dist-info/LICENSE,sha256=HeougO0cvQIz-EzuRIaylonxM7q6zPTSleUcQwUfMhY,62399 +pandas-2.2.3.dist-info/METADATA,sha256=8FQjE5gG0NddUnq6QmiSYAhNx6La0fTzKxZ3D5fR9w8,89901 +pandas-2.2.3.dist-info/RECORD,, +pandas-2.2.3.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas-2.2.3.dist-info/WHEEL,sha256=3qIDcXCk577AXiK3pDifO-gE9U_MYWYGgtD78gLa2_U,137 +pandas-2.2.3.dist-info/entry_points.txt,sha256=OVLKNEPs-Q7IWypWBL6fxv56_zt4sRnEI7zawo6y_0w,69 +pandas/__init__.py,sha256=EIvoyjrhoqXHZe5vh-iGfYfC-1qJEH5sLTpqzJZhK3s,8658 +pandas/__pycache__/__init__.cpython-312.pyc,, +pandas/__pycache__/_typing.cpython-312.pyc,, +pandas/__pycache__/_version.cpython-312.pyc,, +pandas/__pycache__/_version_meson.cpython-312.pyc,, +pandas/__pycache__/conftest.cpython-312.pyc,, +pandas/__pycache__/testing.cpython-312.pyc,, +pandas/_config/__init__.py,sha256=hdg_O-v73cCSrIj6uLoz1NRxgYtOILOs8mOPKdDoEUk,1437 +pandas/_config/__pycache__/__init__.cpython-312.pyc,, +pandas/_config/__pycache__/config.cpython-312.pyc,, +pandas/_config/__pycache__/dates.cpython-312.pyc,, +pandas/_config/__pycache__/display.cpython-312.pyc,, +pandas/_config/__pycache__/localization.cpython-312.pyc,, +pandas/_config/config.py,sha256=YwadTnEN93OFAxyzW289d_v4dhWLzxpMHGZrl3xt_XY,25454 +pandas/_config/dates.py,sha256=HgZFPT02hugJO7uhSTjwebcKOd34JkcYY2gSPtOydmg,668 +pandas/_config/display.py,sha256=xv_TetWUhFlVpog23QzyhMYsScops_OOsWIAGnmKdJ8,1804 +pandas/_config/localization.py,sha256=79Q2KU1aHxX6Q8Wn8EGOEUAyv3XIjQ4YaTaEzeFbtwM,5190 +pandas/_libs/__init__.py,sha256=6i-pdZncVhiCRL_ChKyrTLNhn14aDbsYw243-PfAnJQ,673 +pandas/_libs/__pycache__/__init__.cpython-312.pyc,, +pandas/_libs/algos.cpython-312-x86_64-linux-gnu.so,sha256=xTDe0_dupqafEITA6-mnNYlkI4uUPlGFXqTQDuGwxZU,2103944 +pandas/_libs/algos.pyi,sha256=KEF48zZLn3TSUCmd8thdo4DzYvJ5zaCK60hYX6nzyZI,15182 +pandas/_libs/arrays.cpython-312-x86_64-linux-gnu.so,sha256=y4U0L-G0vDA5w12BAW1vZVucuDIF9zw_J7yWhaKaCAc,133152 +pandas/_libs/arrays.pyi,sha256=PfpeOMplxyN2vbfFCdkkSKGCg21SFRydvqBdeJhBVqQ,1105 +pandas/_libs/byteswap.cpython-312-x86_64-linux-gnu.so,sha256=v26KD8OjLlAe8z3JQH2Q6mik1Hxn2GWx-RRyt4bamt8,61696 +pandas/_libs/byteswap.pyi,sha256=SxL2I1rKqe73WZgkO511PWJx20P160V4hrws1TG0JTk,423 +pandas/_libs/groupby.cpython-312-x86_64-linux-gnu.so,sha256=72Y6FP5obQmJC6qwTtLpRE2r6mdUpqefbcBSmY8cyCU,2654792 +pandas/_libs/groupby.pyi,sha256=Q-jrhgZskMvXOhpHP6EhPhutdW4zAoNI2TQ7iE_68qc,7251 +pandas/_libs/hashing.cpython-312-x86_64-linux-gnu.so,sha256=rKDVkmOl-_nUEUktGoG1_me6tSh9Ndm0Zu_rIKS8k6M,213000 +pandas/_libs/hashing.pyi,sha256=cdNwppEilaMnVN77ABt3TBadjUawMtMFgSQb1PCqwQk,181 +pandas/_libs/hashtable.cpython-312-x86_64-linux-gnu.so,sha256=YWtGFY29Cu6Tu8cHhhQ6Ga_M0EfNjzG-KcUibyNGqT0,2165256 +pandas/_libs/hashtable.pyi,sha256=jBv8QuQii-ikWklP76_DPCYfms88fpj6pPOaCOK6s0M,7424 +pandas/_libs/index.cpython-312-x86_64-linux-gnu.so,sha256=t0iDHnQqvQGqBJj7xXSJHd6p6Ch5UXvF-GHm6IdTBLs,934728 +pandas/_libs/index.pyi,sha256=w3sVStmL_0qAt8C_OyGXSyzugtcnELzL7ZJuCiTHXzY,3695 +pandas/_libs/indexing.cpython-312-x86_64-linux-gnu.so,sha256=Z06bjXHYzRkaZZAwmqkCwhGZMGj7Q9pIeXwd-KmfBtM,70656 +pandas/_libs/indexing.pyi,sha256=hlJwakbofPRdt1Lm31bfQ3CvHW-nMxm0nrInSWAey58,427 +pandas/_libs/internals.cpython-312-x86_64-linux-gnu.so,sha256=z-L_Y98xCFmos0XCdB-97exLQCLj20ll4FJ3Uspcfn0,403272 +pandas/_libs/internals.pyi,sha256=1zfOoULlHvpbbRHvPlcrV_kbY7WI3qEXYExbENEDdtE,2761 +pandas/_libs/interval.cpython-312-x86_64-linux-gnu.so,sha256=M0C3wZvrNOqRd2pFDHuiGMtEaodAY_x5nGZ-mavhvTI,1442344 +pandas/_libs/interval.pyi,sha256=cotxOfoqp7DX7XgIeKrGd31mfAeNerW1WD-yBrLfTlE,5378 +pandas/_libs/join.cpython-312-x86_64-linux-gnu.so,sha256=X85LKMPK-w6oWxZ-3fHe_G6vY3YKRbC32czyuAjr4BM,1368968 +pandas/_libs/join.pyi,sha256=O61ZOIYi-I3gZJjDpLYIWWEe3iG0pogEQIB0ZxJ_E3Y,2780 +pandas/_libs/json.cpython-312-x86_64-linux-gnu.so,sha256=-n-TUHdXhkMJo3ul5kD8YWVsB0lD7EXlTHKu8732rH0,64272 +pandas/_libs/json.pyi,sha256=kbqlmh7HTk4cc2hIDWdXZSFqOfh0cqGpBwcys3m32XM,496 +pandas/_libs/lib.cpython-312-x86_64-linux-gnu.so,sha256=Zo8SLBy8fN7XV3BydNM18wGs489M6IzD2XR21RhWOCA,896648 +pandas/_libs/lib.pyi,sha256=CjVLZ1Jm6BKylmgNXq4YZxMNS1_ITw_paZznoH27S4g,7103 +pandas/_libs/missing.cpython-312-x86_64-linux-gnu.so,sha256=JkW4lkkTlmRLaUFI_8N2CjKaUpyfdrvl3czFB8bgQh0,203208 +pandas/_libs/missing.pyi,sha256=iIftmSeHBcfgz7d9JWW_FQcyyAkuBzRiSnZw690OhDw,524 +pandas/_libs/ops.cpython-312-x86_64-linux-gnu.so,sha256=xqTBrtwiwaQZHB4c1TzyfiF96CDC1AXmLSWZu1FI3Dg,258184 +pandas/_libs/ops.pyi,sha256=99NSmMUkneVNWOojl8Dsb8GmbUa5y_uhKUtfIgwgwek,1318 +pandas/_libs/ops_dispatch.cpython-312-x86_64-linux-gnu.so,sha256=1zJMUSOcjlXnhY6YNkIIJwg7-6VCf-QUq26cTiWbsYk,65760 +pandas/_libs/ops_dispatch.pyi,sha256=Yxq3SUJ-qoMZ8ErL7wfHfCsTTcETOuu0FuoCOyhmGl0,124 +pandas/_libs/pandas_datetime.cpython-312-x86_64-linux-gnu.so,sha256=jWjmNIAnEL724kP-qwBEMIsFoXqEn4t1uCTZoz7G0zI,39264 +pandas/_libs/pandas_parser.cpython-312-x86_64-linux-gnu.so,sha256=wUiIvOmmwIZahqWgTCc97a0L9Sec33Y4FSEc4OmDkv0,43424 +pandas/_libs/parsers.cpython-312-x86_64-linux-gnu.so,sha256=XlbVWtuH07eT2rPBFWFjoydgJ2vJ-7ibEQBbVqR3c-8,574248 +pandas/_libs/parsers.pyi,sha256=raoGhPLoRKLQAthm9JQT5rTjLR1PGFDS179aqtQdgnY,2378 +pandas/_libs/properties.cpython-312-x86_64-linux-gnu.so,sha256=iJvKzkSimQmCeRZ5a6lL6wCFPyxV5ks6mOoz1Y2fv_g,96000 +pandas/_libs/properties.pyi,sha256=HF93vy5OSNtQKz5NL_zwTnOj6tzBtW9Cog-5Zk2bnAA,717 +pandas/_libs/reshape.cpython-312-x86_64-linux-gnu.so,sha256=OwnEa9ehZBC8xpJXKmxxG_lAOfUH6yf9MvsWL4Ax7SA,338312 +pandas/_libs/reshape.pyi,sha256=xaU-NNnRhXVT9AVrksVXrbKfAC7Ny9p-Vwp6srRoGns,419 +pandas/_libs/sas.cpython-312-x86_64-linux-gnu.so,sha256=U7WKqfq6_Z1ESzrTqNdSK6WLr0otYEjimsIKmznUSeQ,263016 +pandas/_libs/sas.pyi,sha256=qkrJiuBd7GQbw3DQyhH9M6cMfNSkovArOXRdhJ8PFDA,224 +pandas/_libs/sparse.cpython-312-x86_64-linux-gnu.so,sha256=trkvbztemlYDWdWymmF_O4Jge7OHxQYUf9MfYnVQOmE,919176 +pandas/_libs/sparse.pyi,sha256=Yyi7QHpAt7K6l2iEhxgufRqbvSRfYpBHeC_hJaSK8Ho,1485 +pandas/_libs/testing.cpython-312-x86_64-linux-gnu.so,sha256=QZzCsTsr7SsEKjLIaA90sj5YYksqSLzoxujTrl3NyYQ,135616 +pandas/_libs/testing.pyi,sha256=_fpEWiBmlWGR_3QUj1RU42WCTtW2Ug-EXHpM-kP6vB0,243 +pandas/_libs/tslib.cpython-312-x86_64-linux-gnu.so,sha256=mkYI7aKc9GzgvJr9b3JQglh1xCL35dgMdoRLPd-tPF4,327944 +pandas/_libs/tslib.pyi,sha256=aWJDfzlbmlF6sAst1BTMKMcWt3me50-sqCS5YwWt0HI,969 +pandas/_libs/tslibs/__init__.py,sha256=dowITNV3Gxq8wB3XdqiyRCtEDn83_GkLcGJiQnzM1mA,2125 +pandas/_libs/tslibs/__pycache__/__init__.cpython-312.pyc,, +pandas/_libs/tslibs/base.cpython-312-x86_64-linux-gnu.so,sha256=2h3qE1GrVq5sqcwyX6fq6603lB2UXHuikgRHKE_-2Mg,66368 +pandas/_libs/tslibs/ccalendar.cpython-312-x86_64-linux-gnu.so,sha256=ZmkWZOrst3uvG_wCgD8JRD5_Ga1mKhWtRvFPWN8vDS8,106848 +pandas/_libs/tslibs/ccalendar.pyi,sha256=dizWWmYtxWa5Lc4Hv69iRaJoazRhegJaDGWYgWtJu-U,502 +pandas/_libs/tslibs/conversion.cpython-312-x86_64-linux-gnu.so,sha256=Wy1t5gWD3eth0tI-h4Lqup4pUeEpUyKgh3PE5Nw8Ktc,304104 +pandas/_libs/tslibs/conversion.pyi,sha256=sHO9CBRrDh0wovkr736kI5G6gaW1WY9tSOOAkBi63MA,300 +pandas/_libs/tslibs/dtypes.cpython-312-x86_64-linux-gnu.so,sha256=zTc-7lR2fvBhUNqkOGiHK7G_K5UlRYd-xZeX2xsILXY,202592 +pandas/_libs/tslibs/dtypes.pyi,sha256=ZNUPcAyhkkh7kIGLDIDTfUmwefbtdxxvn668YN-AAeE,1988 +pandas/_libs/tslibs/fields.cpython-312-x86_64-linux-gnu.so,sha256=IMXqVOLxVb9ag4k58qoK7vwQ_zzZ_2CS77veOxsWdmI,328712 +pandas/_libs/tslibs/fields.pyi,sha256=LOke0XZ9XJnzX2MC9nL3u-JpbmddBfpy0UQ_d-_NvN8,1860 +pandas/_libs/tslibs/nattype.cpython-312-x86_64-linux-gnu.so,sha256=nYx4WbNjUwHFpFb256WMJKJYJdg5HObWbVyFUI8YsLI,224896 +pandas/_libs/tslibs/nattype.pyi,sha256=R3qw7RgZFLG7IgKTssmJdjm-lP3V18GEz810nzVHsTs,4116 +pandas/_libs/tslibs/np_datetime.cpython-312-x86_64-linux-gnu.so,sha256=oMsgqV2Lei7GJRFWEN1Eld0IbwzzR2vW-xqFE6LjMEk,148064 +pandas/_libs/tslibs/np_datetime.pyi,sha256=Y6l1KVdyKTMiYfzOjXNwV946GjoFAHaCEEhLDsHRCxI,831 +pandas/_libs/tslibs/offsets.cpython-312-x86_64-linux-gnu.so,sha256=L6dxl_edwSoWo0Rnugo21RyznDDqemSo39mwo-lIoqU,1138272 +pandas/_libs/tslibs/offsets.pyi,sha256=QkYq2CgQ4aZ-92e_8wSpuxaACBIKjk2eI4-M-6wSeZU,8345 +pandas/_libs/tslibs/parsing.cpython-312-x86_64-linux-gnu.so,sha256=VkLOr4l1MrIOLyAVtCISyZ9yoE_At5V2Xxj8jUpPSZU,448808 +pandas/_libs/tslibs/parsing.pyi,sha256=cbS8tHb95ygwDU-9gNaFs83FpbVj8aoQfw7gwJGEE6o,914 +pandas/_libs/tslibs/period.cpython-312-x86_64-linux-gnu.so,sha256=rmJCyX0meJgP8X4iR4z3gwgwGpgAjkeITkCUurTj83c,519912 +pandas/_libs/tslibs/period.pyi,sha256=Bf0lYd6dh9O61Gq_TReVI4NcRf-5aINkdYJNDaq5az8,3908 +pandas/_libs/tslibs/strptime.cpython-312-x86_64-linux-gnu.so,sha256=T29cWee70A-jzMKHYDR6ac_lPQtJSdBc890-xSzpIfc,394056 +pandas/_libs/tslibs/strptime.pyi,sha256=dizASoJenvjCydaWDo72_FQmiNOjLmnCZbUZgCm8EnI,349 +pandas/_libs/tslibs/timedeltas.cpython-312-x86_64-linux-gnu.so,sha256=miRrU3Co3QcXbcedBx72GsdnIxlnuz4BN_zla5GQg8E,619176 +pandas/_libs/tslibs/timedeltas.pyi,sha256=6MW61MbVDqOH4JUQoR32z8qYUWRfPECV3fcQSrOkI_M,5009 +pandas/_libs/tslibs/timestamps.cpython-312-x86_64-linux-gnu.so,sha256=94APoupBDcrbhbIMW544eCVPeVFV9aFNGTtRtEYHlP8,640328 +pandas/_libs/tslibs/timestamps.pyi,sha256=zCu9cAbFf_IVDb1sf5j_Ww5LYSFzGVwMhpZZUP370kw,7831 +pandas/_libs/tslibs/timezones.cpython-312-x86_64-linux-gnu.so,sha256=SakZyICn4kWxhojo56-Eh8XMOUlznzs26acl0PsWj6w,282664 +pandas/_libs/tslibs/timezones.pyi,sha256=MZ9kC5E1J3XlVqyBwFuVd7NsqL8STztzT8W8NK-_2r0,600 +pandas/_libs/tslibs/tzconversion.cpython-312-x86_64-linux-gnu.so,sha256=K2CtRznaEQF9gxF-bW8lucIT02htvn01pO3lPGoh7_c,332680 +pandas/_libs/tslibs/tzconversion.pyi,sha256=MW4HtIKZpf7ZcOUQ4U6FL24BiJpASXI-mN0DOADtl10,560 +pandas/_libs/tslibs/vectorized.cpython-312-x86_64-linux-gnu.so,sha256=AlrzWUx5QT6FXF9kp2AUdDQMLj6JLxKMi0r-Et81de0,241576 +pandas/_libs/tslibs/vectorized.pyi,sha256=Dv5ryF4HiPZcHWMyxyfP4D_tONdqLm2Mn4MpVi5RKCc,1239 +pandas/_libs/window/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/_libs/window/__pycache__/__init__.cpython-312.pyc,, +pandas/_libs/window/aggregations.cpython-312-x86_64-linux-gnu.so,sha256=jKkzAR5rwjnyNe50vZi0gO-wi-n5LYKBfC4aO6ChZWY,394264 +pandas/_libs/window/aggregations.pyi,sha256=vVjfgqY4cBPmjFadcrDc6heCiFbJ5Lz65bCadbHJbwU,4063 +pandas/_libs/window/indexers.cpython-312-x86_64-linux-gnu.so,sha256=ISau5eVdOoiJEfsx7ygO1fUidv_NUt06tUUV6ChHmiY,212936 +pandas/_libs/window/indexers.pyi,sha256=53aBxew7jBcAc9sbSoOlvpQHhiLDSWPXFcVbCeJDbQA,319 +pandas/_libs/writers.cpython-312-x86_64-linux-gnu.so,sha256=pLRPZww1rfePCwFh_r00GvWrNP57a6SDpFPxQH7YdGg,254856 +pandas/_libs/writers.pyi,sha256=RvwFCzrsU4RkKm7Mc3wo12RqdGdo-PuANkMo3Z9hLiU,516 +pandas/_testing/__init__.py,sha256=9-kM4agqo_qFFPSVACpl1zoM5D06biVAQUoDUK92Y0A,17554 +pandas/_testing/__pycache__/__init__.cpython-312.pyc,, +pandas/_testing/__pycache__/_hypothesis.cpython-312.pyc,, +pandas/_testing/__pycache__/_io.cpython-312.pyc,, +pandas/_testing/__pycache__/_warnings.cpython-312.pyc,, +pandas/_testing/__pycache__/asserters.cpython-312.pyc,, +pandas/_testing/__pycache__/compat.cpython-312.pyc,, +pandas/_testing/__pycache__/contexts.cpython-312.pyc,, +pandas/_testing/_hypothesis.py,sha256=WS4ysEJwmMor02cwMw15kBtAR0SLvUUpTfYEpc0c6iI,2426 +pandas/_testing/_io.py,sha256=OwfQ9L0XZgD_Yfi5mF8_BLgPx8pgGZbTzq46uTa7jDo,4448 +pandas/_testing/_warnings.py,sha256=x7YMaPkmSaimJquGT3vAt9pKn0r_Hj5lE1uV0eCoDiU,8357 +pandas/_testing/asserters.py,sha256=nCygrO7daDLBmBtPpJyp87exFgT65accJYgQcjJhNB8,47201 +pandas/_testing/compat.py,sha256=0o_biVI-wLh7kcw9FHvbwYyzNvM0PI06QRD2ZhiD2Fs,658 +pandas/_testing/contexts.py,sha256=TmKKWG1VF-lZTz_6DUuUAbcW7ZqcQ59nVNUxNoLzz3g,6551 +pandas/_typing.py,sha256=gVSimiU46Dduc2Ez8ZaOczv8c-UHTH4FZeg6LL6mnGk,14037 +pandas/_version.py,sha256=yFQDRxMdgDLf9OmPX4d0cyT6tsYJEWLV4QgS5yuNgkE,23612 +pandas/_version_meson.py,sha256=LCx3vAY16LA5zd6bZeSfh2kVnOxvilsYwcRrvSgWEQk,79 +pandas/api/__init__.py,sha256=QnoYVW828TM17uq-3ELeethZm8XN2Y0DkEaTc3sLr3Q,219 +pandas/api/__pycache__/__init__.cpython-312.pyc,, +pandas/api/extensions/__init__.py,sha256=O7tmzpvIT0uv9H5K-yMTKcwZpml9cEaB5CLVMiUkRCk,685 +pandas/api/extensions/__pycache__/__init__.cpython-312.pyc,, +pandas/api/indexers/__init__.py,sha256=kNbZv9nja9iLVmGZU2D6w2dqB2ndsbqTfcsZsGz_Yo0,357 +pandas/api/indexers/__pycache__/__init__.cpython-312.pyc,, +pandas/api/interchange/__init__.py,sha256=J2hQIYAvL7gyh8hG9r3XYPX69lK7nJS3IIHZl4FESjw,230 +pandas/api/interchange/__pycache__/__init__.cpython-312.pyc,, +pandas/api/types/__init__.py,sha256=bOU3TUuskT12Dpp-SsCYtCWdHvBDp3MWf3Etq4ZMdT8,447 +pandas/api/types/__pycache__/__init__.cpython-312.pyc,, +pandas/api/typing/__init__.py,sha256=IC4_ZmjsX4804Nnu-lQDccQr0zt5mzIZEaB3Bzdva8Y,1244 +pandas/api/typing/__pycache__/__init__.cpython-312.pyc,, +pandas/arrays/__init__.py,sha256=gMhtojH1KdRwxMmM_Ulblxk4L09o7WLUsXLp6qdUS-I,1227 +pandas/arrays/__pycache__/__init__.cpython-312.pyc,, +pandas/compat/__init__.py,sha256=kDGb_S-Lep8ZdS_-pGPINcrvoSPbj3x0hVVR1Z8pgy8,4280 +pandas/compat/__pycache__/__init__.cpython-312.pyc,, +pandas/compat/__pycache__/_constants.cpython-312.pyc,, +pandas/compat/__pycache__/_optional.cpython-312.pyc,, +pandas/compat/__pycache__/compressors.cpython-312.pyc,, +pandas/compat/__pycache__/pickle_compat.cpython-312.pyc,, +pandas/compat/__pycache__/pyarrow.cpython-312.pyc,, +pandas/compat/_constants.py,sha256=3_ryOkmiJTO-iTQAla_ApEJfp3V_lClbnepSM3Gi9S4,536 +pandas/compat/_optional.py,sha256=96Zlc2gqUYneSzSlraVRGfh2BsTWp4cOUcG2gHjw2E0,5089 +pandas/compat/compressors.py,sha256=GdDWdKzWqkImjdwzuVBwW2JvI7aMzpPV8QyhxWgJo0g,1975 +pandas/compat/numpy/__init__.py,sha256=UO-06Rj2g2Mk9rptXZG_fLtA3BhSPMVF4JhTLdSt5AM,1366 +pandas/compat/numpy/__pycache__/__init__.cpython-312.pyc,, +pandas/compat/numpy/__pycache__/function.cpython-312.pyc,, +pandas/compat/numpy/function.py,sha256=Qvflr9h4rYCw9o8I3RggkhdRqxvav1yioq_JeEUh2T4,13291 +pandas/compat/pickle_compat.py,sha256=MTp_LYeueJWVJBWKzWUyiwcwu9MvjEtBzEC0SozvWs8,7723 +pandas/compat/pyarrow.py,sha256=WzcKBzCsZapuUPMIUL7IVMiXm1L7yzA4QHv3CF5AuxI,1006 +pandas/conftest.py,sha256=dYirfwk99jRP_W1R20BvUz92mrqoWevlW3Zdfu9fQ5A,48625 +pandas/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/core/__pycache__/__init__.cpython-312.pyc,, +pandas/core/__pycache__/accessor.cpython-312.pyc,, +pandas/core/__pycache__/algorithms.cpython-312.pyc,, +pandas/core/__pycache__/api.cpython-312.pyc,, +pandas/core/__pycache__/apply.cpython-312.pyc,, +pandas/core/__pycache__/arraylike.cpython-312.pyc,, +pandas/core/__pycache__/base.cpython-312.pyc,, +pandas/core/__pycache__/common.cpython-312.pyc,, +pandas/core/__pycache__/config_init.cpython-312.pyc,, +pandas/core/__pycache__/construction.cpython-312.pyc,, +pandas/core/__pycache__/flags.cpython-312.pyc,, +pandas/core/__pycache__/frame.cpython-312.pyc,, +pandas/core/__pycache__/generic.cpython-312.pyc,, +pandas/core/__pycache__/indexing.cpython-312.pyc,, +pandas/core/__pycache__/missing.cpython-312.pyc,, +pandas/core/__pycache__/nanops.cpython-312.pyc,, +pandas/core/__pycache__/resample.cpython-312.pyc,, +pandas/core/__pycache__/roperator.cpython-312.pyc,, +pandas/core/__pycache__/sample.cpython-312.pyc,, +pandas/core/__pycache__/series.cpython-312.pyc,, +pandas/core/__pycache__/shared_docs.cpython-312.pyc,, +pandas/core/__pycache__/sorting.cpython-312.pyc,, +pandas/core/_numba/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/core/_numba/__pycache__/__init__.cpython-312.pyc,, +pandas/core/_numba/__pycache__/executor.cpython-312.pyc,, +pandas/core/_numba/__pycache__/extensions.cpython-312.pyc,, +pandas/core/_numba/executor.py,sha256=vsH8jIzWRHho1Au4euWT2opfg5YLG4SBD7xlpvvXGUs,7530 +pandas/core/_numba/extensions.py,sha256=xRSojNahM3OPU28Bns1S4MXALqHKCmfK2SGleZhkm68,18374 +pandas/core/_numba/kernels/__init__.py,sha256=Z1t4IUC2MO0a5KbA0LurWfRZL4wNksHVBDLprGtPLlo,520 +pandas/core/_numba/kernels/__pycache__/__init__.cpython-312.pyc,, +pandas/core/_numba/kernels/__pycache__/mean_.cpython-312.pyc,, +pandas/core/_numba/kernels/__pycache__/min_max_.cpython-312.pyc,, +pandas/core/_numba/kernels/__pycache__/shared.cpython-312.pyc,, +pandas/core/_numba/kernels/__pycache__/sum_.cpython-312.pyc,, +pandas/core/_numba/kernels/__pycache__/var_.cpython-312.pyc,, +pandas/core/_numba/kernels/mean_.py,sha256=BesqY1gwFXPIeuXAQtDvvDBZuegsszFVTnl4lxguXEA,5646 +pandas/core/_numba/kernels/min_max_.py,sha256=tJ7OSKhne7jXpy4XSBpQS0tkP_0LggkH6iqWlxQ-FeE,3284 +pandas/core/_numba/kernels/shared.py,sha256=JUBa96LX4NmXhgXNyo859IwMXEl29EyhmRdMoQo1n78,611 +pandas/core/_numba/kernels/sum_.py,sha256=FeKOQl22qO6kN4hAmwmA3wXihrph5S03ucSt65GBquU,6488 +pandas/core/_numba/kernels/var_.py,sha256=5BaLdr7HKzdUvKvyifvL9qM36W16SAqk3Ny11OtpW9o,6973 +pandas/core/accessor.py,sha256=u57BIkm61_SNRzSdQjL210Jtil7BWFUB0HPNl9wCKdo,10044 +pandas/core/algorithms.py,sha256=8ENQpDWiM0ESh8EZEnXzw_XtbUUiFojvFi7MOZE16dY,55181 +pandas/core/api.py,sha256=9tm275sTpOKtdUvsFCXYQHmBdeJczGNBV1QGv3TQOOc,2911 +pandas/core/apply.py,sha256=TexZlMDp-LmxlCReQuHB4g_jCM-CHKD-NR9Fy_d5NUc,67345 +pandas/core/array_algos/__init__.py,sha256=8YLlO6TysEPxltfbNKdG9MlVXeDLfTIGNo2nUR-Zwl0,408 +pandas/core/array_algos/__pycache__/__init__.cpython-312.pyc,, +pandas/core/array_algos/__pycache__/datetimelike_accumulations.cpython-312.pyc,, +pandas/core/array_algos/__pycache__/masked_accumulations.cpython-312.pyc,, +pandas/core/array_algos/__pycache__/masked_reductions.cpython-312.pyc,, +pandas/core/array_algos/__pycache__/putmask.cpython-312.pyc,, +pandas/core/array_algos/__pycache__/quantile.cpython-312.pyc,, +pandas/core/array_algos/__pycache__/replace.cpython-312.pyc,, +pandas/core/array_algos/__pycache__/take.cpython-312.pyc,, +pandas/core/array_algos/__pycache__/transforms.cpython-312.pyc,, +pandas/core/array_algos/datetimelike_accumulations.py,sha256=BCy87HXqI2WO0_cCGK-redvi2STJzCxswYYs06YdxB4,1686 +pandas/core/array_algos/masked_accumulations.py,sha256=PL-ZAMai7H1PIXLKE2f9LSL2Ow6WZqkusSQkFfIE8d4,2618 +pandas/core/array_algos/masked_reductions.py,sha256=iUFmp_Fu3-BXM0EBiFfiPERteITlIFFI7IEpHXVkvoY,4855 +pandas/core/array_algos/putmask.py,sha256=g02wtMt5MTIuT4IS6ukE1Eh8KWb3Hi932hc47dszqJ4,4593 +pandas/core/array_algos/quantile.py,sha256=zdzcwgoVRP3eBSM4NJHwocBJC3PINYN1jB02mJubFow,6548 +pandas/core/array_algos/replace.py,sha256=p8CdDslj7WwVNYjpLsT_36e8dmrxfeWzh5ECHe4uxCQ,3918 +pandas/core/array_algos/take.py,sha256=n_pjn9mU7QQJ77SFXogEc5ofoMqRgNbkimwXFunz79M,20815 +pandas/core/array_algos/transforms.py,sha256=TPpSPX5CiePVGTFUwnimpcC5YeBOtjAPK20wQvG92QI,1104 +pandas/core/arraylike.py,sha256=BD2ZQP4zGPd4rJas9lS5C-_qp3XXDL2udU8tzD9bQIQ,17655 +pandas/core/arrays/__init__.py,sha256=dE6WRTblcq40JKhXJQDsOwvhFPJstj_8cegiLthH0ks,1314 +pandas/core/arrays/__pycache__/__init__.cpython-312.pyc,, +pandas/core/arrays/__pycache__/_arrow_string_mixins.cpython-312.pyc,, +pandas/core/arrays/__pycache__/_mixins.cpython-312.pyc,, +pandas/core/arrays/__pycache__/_ranges.cpython-312.pyc,, +pandas/core/arrays/__pycache__/_utils.cpython-312.pyc,, +pandas/core/arrays/__pycache__/base.cpython-312.pyc,, +pandas/core/arrays/__pycache__/boolean.cpython-312.pyc,, +pandas/core/arrays/__pycache__/categorical.cpython-312.pyc,, +pandas/core/arrays/__pycache__/datetimelike.cpython-312.pyc,, +pandas/core/arrays/__pycache__/datetimes.cpython-312.pyc,, +pandas/core/arrays/__pycache__/floating.cpython-312.pyc,, +pandas/core/arrays/__pycache__/integer.cpython-312.pyc,, +pandas/core/arrays/__pycache__/interval.cpython-312.pyc,, +pandas/core/arrays/__pycache__/masked.cpython-312.pyc,, +pandas/core/arrays/__pycache__/numeric.cpython-312.pyc,, +pandas/core/arrays/__pycache__/numpy_.cpython-312.pyc,, +pandas/core/arrays/__pycache__/period.cpython-312.pyc,, +pandas/core/arrays/__pycache__/string_.cpython-312.pyc,, +pandas/core/arrays/__pycache__/string_arrow.cpython-312.pyc,, +pandas/core/arrays/__pycache__/timedeltas.cpython-312.pyc,, +pandas/core/arrays/_arrow_string_mixins.py,sha256=EaRHU4W7E3cOVkXhdp7wT2UGFD_FI9HYIzf26BBtleE,2608 +pandas/core/arrays/_mixins.py,sha256=NxUqWabMVxhv85tKqBu8JAAptApEq_avk6wbnN46xtI,17396 +pandas/core/arrays/_ranges.py,sha256=Ig3E_ROJ5mbOtK639SJ0UqcI229BrtsAfa_avbqrO8g,6996 +pandas/core/arrays/_utils.py,sha256=RmwOy6xNhgZ61qmk_PFnQ5sW-RVrkOhsl4AvQyqOuAY,1901 +pandas/core/arrays/arrow/__init__.py,sha256=-EKwaHww-yrbm7Z5d3AN_KETWmXYgZ2dW6KHaE2iiLI,221 +pandas/core/arrays/arrow/__pycache__/__init__.cpython-312.pyc,, +pandas/core/arrays/arrow/__pycache__/_arrow_utils.cpython-312.pyc,, +pandas/core/arrays/arrow/__pycache__/accessors.cpython-312.pyc,, +pandas/core/arrays/arrow/__pycache__/array.cpython-312.pyc,, +pandas/core/arrays/arrow/__pycache__/extension_types.cpython-312.pyc,, +pandas/core/arrays/arrow/_arrow_utils.py,sha256=KjsV7ts963RSyNEGLGQliypzHJ_hs3mTslWPMXZpGpE,2151 +pandas/core/arrays/arrow/accessors.py,sha256=XxV7NzS1PHca7-Feesus0W8K3HwUHa-aSIIawdCTE8g,13863 +pandas/core/arrays/arrow/array.py,sha256=foZ2kgb9oYx_tx2PhreM_c3THxTy_f6ZdpKPCejU93E,102796 +pandas/core/arrays/arrow/extension_types.py,sha256=NJLTuf_8U8u-Fjt_qfWm7zhUtPQdvjH1JV8fY3oRv-Y,5459 +pandas/core/arrays/base.py,sha256=vEQaNCkTKkgjmuFxlzScuLpMJFLCbscDGrRA0f7WMFk,85047 +pandas/core/arrays/boolean.py,sha256=ln7GjlHHTtByAhQKX9XuymhifZTCNSpk1j7I-fQKObo,12440 +pandas/core/arrays/categorical.py,sha256=cmLBaSSjzKaANAbMi64Euk-Lxvu6n-PZ0zLH-vxrS2o,98997 +pandas/core/arrays/datetimelike.py,sha256=0t3rDfaBmvj8qSyVfxc1DDPdnhAU7m4HRaJsmvTTYUw,89288 +pandas/core/arrays/datetimes.py,sha256=DqOHEGkUk18oIQdDj8CiBbE_XGWWTQeE7uqMxX7S2VU,92345 +pandas/core/arrays/floating.py,sha256=pvZ72VDstzgslAM5-36KEyJ0z5PBVwTNogcJAxhhMP8,4286 +pandas/core/arrays/integer.py,sha256=FWsrgzs_DB3eG8VX1kfzUTMcKOHfa-ACFQh_xVpZPJU,6470 +pandas/core/arrays/interval.py,sha256=ro_WPcUzlxWKjocGLgEseEcljbBInjzrl8yPOCt87G0,63176 +pandas/core/arrays/masked.py,sha256=gVJpxoiEaY5kca8umAkRt6Az2L0GK3ffwjB63ELG62M,55634 +pandas/core/arrays/numeric.py,sha256=lVpSpsG_66z2QMHghCRoYef6dVJJ_QZAf9vkpLMJokI,9165 +pandas/core/arrays/numpy_.py,sha256=6A5ErMvdNooTI91M1qkV6RWL1B9bN1JJgjueq2QRvdU,17493 +pandas/core/arrays/period.py,sha256=M0cLFbFBn_yD99y_kQ8YY4NWMaH1nVdywBf56CrpiJk,40756 +pandas/core/arrays/sparse/__init__.py,sha256=iwvVqa2GG9TjYrd1rxCBjdLeGQBoRqUO2fZnILElbZg,356 +pandas/core/arrays/sparse/__pycache__/__init__.cpython-312.pyc,, +pandas/core/arrays/sparse/__pycache__/accessor.cpython-312.pyc,, +pandas/core/arrays/sparse/__pycache__/array.cpython-312.pyc,, +pandas/core/arrays/sparse/__pycache__/scipy_sparse.cpython-312.pyc,, +pandas/core/arrays/sparse/accessor.py,sha256=lZa3hwvXJKLMkXhqiWU8eev8qthvYQ1HgtW875qQe7g,12503 +pandas/core/arrays/sparse/array.py,sha256=vZng8r0PWtKVb74jc0NlEipWyuevqueRWtOhAt8x6xA,63884 +pandas/core/arrays/sparse/scipy_sparse.py,sha256=rVaj3PtVRrMPlzkoVFSkIopWV0xg0GJnpt1YljWT_zg,6462 +pandas/core/arrays/string_.py,sha256=LhTi6O0RG-XesI-VCunUjOmu-WBvFb6MaMv49JX2gW8,21814 +pandas/core/arrays/string_arrow.py,sha256=7wLrp0wOUJuPmwB9_9BLWOm4BAYiQHahSy0umtg7YIY,24913 +pandas/core/arrays/timedeltas.py,sha256=eTi8b16Jumac8WIx8LLf_9ZeFzA4u1nipHMUoc5-lyM,37830 +pandas/core/base.py,sha256=tvXmsVrlfhc-Br3bae5HKiuQlmKPWL9tRVbi7MhhZpw,40940 +pandas/core/common.py,sha256=WwkpCOI8b9j5rxkhL_Dh5l-7EdkHFfSjIIx-QBsefa0,17449 +pandas/core/computation/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/core/computation/__pycache__/__init__.cpython-312.pyc,, +pandas/core/computation/__pycache__/align.cpython-312.pyc,, +pandas/core/computation/__pycache__/api.cpython-312.pyc,, +pandas/core/computation/__pycache__/check.cpython-312.pyc,, +pandas/core/computation/__pycache__/common.cpython-312.pyc,, +pandas/core/computation/__pycache__/engines.cpython-312.pyc,, +pandas/core/computation/__pycache__/eval.cpython-312.pyc,, +pandas/core/computation/__pycache__/expr.cpython-312.pyc,, +pandas/core/computation/__pycache__/expressions.cpython-312.pyc,, +pandas/core/computation/__pycache__/ops.cpython-312.pyc,, +pandas/core/computation/__pycache__/parsing.cpython-312.pyc,, +pandas/core/computation/__pycache__/pytables.cpython-312.pyc,, +pandas/core/computation/__pycache__/scope.cpython-312.pyc,, +pandas/core/computation/align.py,sha256=IBp-G1qbFMICrgm8DYOF-Kt18iCcY_P3peeIGsDkNv4,6161 +pandas/core/computation/api.py,sha256=CQ2AF0hwydcgTHycMCFiyZIAU57RcZT-TVid17SIsV4,65 +pandas/core/computation/check.py,sha256=Vb1YqLq381-nUp8Vjkg6ycJOxP3dV2aO9XjyM1uhe2Q,226 +pandas/core/computation/common.py,sha256=-2EHScxo2jfEQ1oqnnlQ_2eOvtAIn8O2krBaveSwmjs,1442 +pandas/core/computation/engines.py,sha256=g9eiyVCUtNmJGbexh7KvTreAKKhs5mQaWx4Z5UeOZ5s,3314 +pandas/core/computation/eval.py,sha256=21MaqANbDE4xoBk1Ts_iAj_l7Nn3iERNFX0dHvbeN4Y,14047 +pandas/core/computation/expr.py,sha256=utZSSpAHz692aNvfUpMsqgUyOEfC53B6Gv8ybtppDnE,25064 +pandas/core/computation/expressions.py,sha256=K0vu_v8JBVjJn6eQqNocC4ciNKsIYnEZrq8xwvhik2M,7503 +pandas/core/computation/ops.py,sha256=x5Qe3PfjHF5v-FHBerUr39iNXk_T0hLvw0Wchm0RiAQ,14829 +pandas/core/computation/parsing.py,sha256=VhYh3en2onhyJkzTelz32-U4Vc3XadyjTwOVctsqlEI,6399 +pandas/core/computation/pytables.py,sha256=7-L2GZ43aWNKG6hz-j8RhL8BIEGAEvpYi6rX6Zsvm_4,20745 +pandas/core/computation/scope.py,sha256=eyMdfx-gcgJaVIRY2NBgQDt2nW5KSdUZ3M9VRPYUJtU,10203 +pandas/core/config_init.py,sha256=1WBaE0u_9DDurUtAmLL9vy9rdS4hK5SudW9vhmD8taA,26460 +pandas/core/construction.py,sha256=QbsRsoxwBh0EQFoF8Wr5PgU96s7Lt4LW0f7BFtEwyQk,26339 +pandas/core/dtypes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/core/dtypes/__pycache__/__init__.cpython-312.pyc,, +pandas/core/dtypes/__pycache__/api.cpython-312.pyc,, +pandas/core/dtypes/__pycache__/astype.cpython-312.pyc,, +pandas/core/dtypes/__pycache__/base.cpython-312.pyc,, +pandas/core/dtypes/__pycache__/cast.cpython-312.pyc,, +pandas/core/dtypes/__pycache__/common.cpython-312.pyc,, +pandas/core/dtypes/__pycache__/concat.cpython-312.pyc,, +pandas/core/dtypes/__pycache__/dtypes.cpython-312.pyc,, +pandas/core/dtypes/__pycache__/generic.cpython-312.pyc,, +pandas/core/dtypes/__pycache__/inference.cpython-312.pyc,, +pandas/core/dtypes/__pycache__/missing.cpython-312.pyc,, +pandas/core/dtypes/api.py,sha256=5mtML1OspdDbsWShw1fsDq93pg2pmuUGSBrvQWQcCgg,1819 +pandas/core/dtypes/astype.py,sha256=awzOpnfZ0dCYhzw_J4fekT7u0F0VwgsIapuiAbBkxxg,9207 +pandas/core/dtypes/base.py,sha256=EeL8zNbMtrvObdEJtqjkG_vIsoQE8zDZiIR2dHzHKPI,17042 +pandas/core/dtypes/cast.py,sha256=Uny9eaZD1jGRfWmh6p6_65HSevmNm16vP0BvTzvH5hQ,62164 +pandas/core/dtypes/common.py,sha256=0KQTfjHC6fhcpXg69-aMF54SuRIQFeiHzU_1VBVyGzg,47319 +pandas/core/dtypes/concat.py,sha256=Q_QujfB0C-CIWbcTlktmB02RgxCf7xQsOgEkOV67VPo,12579 +pandas/core/dtypes/dtypes.py,sha256=w3P6AphBpTFUBqdZLpJ36ziH23YdMGMH0r75KsTObYs,76035 +pandas/core/dtypes/generic.py,sha256=avKoJBzIQ0pJiFg9mmQ1D5ltkZsYxu8uPa46Hat70Ro,4122 +pandas/core/dtypes/inference.py,sha256=OqA9itS2osQBP-mp8jJK9RJZJps4VPsTIvQFCX4EbGM,9012 +pandas/core/dtypes/missing.py,sha256=BPzbmr7O7ihmjLKE9A31ck54ANjAtrp8-dVT20MR5fQ,23632 +pandas/core/flags.py,sha256=NxbTcYlNEaO8MKCpbEc22PEpInFn7f7za7EAO6-mxEE,3763 +pandas/core/frame.py,sha256=m4ExWWBrYq8Dv-dqcInaKC0tYJHzCM8vTZDdBmGgEQU,447104 +pandas/core/generic.py,sha256=UEIXZg--GqdP-YsxvqCTvZBDzWHDWronIHNGTzjkbyI,474370 +pandas/core/groupby/__init__.py,sha256=KamY9WI5B4cMap_3wZ5ycMdXM_rOxGSL7RtoKKPfjAo,301 +pandas/core/groupby/__pycache__/__init__.cpython-312.pyc,, +pandas/core/groupby/__pycache__/base.cpython-312.pyc,, +pandas/core/groupby/__pycache__/categorical.cpython-312.pyc,, +pandas/core/groupby/__pycache__/generic.cpython-312.pyc,, +pandas/core/groupby/__pycache__/groupby.cpython-312.pyc,, +pandas/core/groupby/__pycache__/grouper.cpython-312.pyc,, +pandas/core/groupby/__pycache__/indexing.cpython-312.pyc,, +pandas/core/groupby/__pycache__/numba_.cpython-312.pyc,, +pandas/core/groupby/__pycache__/ops.cpython-312.pyc,, +pandas/core/groupby/base.py,sha256=OrqG2_h_Bp8Z_MeLrAGWGROG-MtSloGqeaJ79qYbJm0,2740 +pandas/core/groupby/categorical.py,sha256=iCsl3d_unK4zAh_lR3eDIBVOhwsv9Bj9X1wbnaR90pw,3047 +pandas/core/groupby/generic.py,sha256=LCsrCIjuhcEz-yw3gyk5nYKNiMF1h8en6nQO1hhTywE,96885 +pandas/core/groupby/groupby.py,sha256=yjNsgJmutrWxZj-6oveCtkDFgJg6S4rcXHsJ8rsjVTU,195730 +pandas/core/groupby/grouper.py,sha256=utxyUS7M-sTYaiWek9uRaIaAHHm0jaTbIqDX-GjEHYE,38672 +pandas/core/groupby/indexing.py,sha256=QY4GZ4wDd-1K-we0EfdiFvmdAZ_VxVgPrYB0kBZf6wU,9510 +pandas/core/groupby/numba_.py,sha256=XjfPfYGbYJgkIKYFiq7Gjnr5wwZ8mKrkeHKTW42HZMg,4894 +pandas/core/groupby/ops.py,sha256=qZPzps8n5_67_FcGpByM9G4PFqr7f4PWcwf52Os16uI,38234 +pandas/core/indexers/__init__.py,sha256=M4CyNLiQoQ5ohoAMH5HES9Rh2lpryAM1toL-b1TJXj0,736 +pandas/core/indexers/__pycache__/__init__.cpython-312.pyc,, +pandas/core/indexers/__pycache__/objects.cpython-312.pyc,, +pandas/core/indexers/__pycache__/utils.cpython-312.pyc,, +pandas/core/indexers/objects.py,sha256=PR063DVlu8_-ti7GsLRb0e7o4oAz2xpMil0nMee18z0,14737 +pandas/core/indexers/utils.py,sha256=TgVCAX9r4MZw3QPH6aE-d55gRZcKN9H9X-MTZ4u-LiY,16069 +pandas/core/indexes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/core/indexes/__pycache__/__init__.cpython-312.pyc,, +pandas/core/indexes/__pycache__/accessors.cpython-312.pyc,, +pandas/core/indexes/__pycache__/api.cpython-312.pyc,, +pandas/core/indexes/__pycache__/base.cpython-312.pyc,, +pandas/core/indexes/__pycache__/category.cpython-312.pyc,, +pandas/core/indexes/__pycache__/datetimelike.cpython-312.pyc,, +pandas/core/indexes/__pycache__/datetimes.cpython-312.pyc,, +pandas/core/indexes/__pycache__/extension.cpython-312.pyc,, +pandas/core/indexes/__pycache__/frozen.cpython-312.pyc,, +pandas/core/indexes/__pycache__/interval.cpython-312.pyc,, +pandas/core/indexes/__pycache__/multi.cpython-312.pyc,, +pandas/core/indexes/__pycache__/period.cpython-312.pyc,, +pandas/core/indexes/__pycache__/range.cpython-312.pyc,, +pandas/core/indexes/__pycache__/timedeltas.cpython-312.pyc,, +pandas/core/indexes/accessors.py,sha256=MdP8zNlSQeeU7psOXwGUdQ1-8XKzYCl5mKMIcpMCiN8,19152 +pandas/core/indexes/api.py,sha256=tDBBn84I19nvPFQKj0GAZhb0zioLJqTUJjSVqyc4Fn4,10426 +pandas/core/indexes/base.py,sha256=LVD4AAYIKU0HTYvxSpzcSJ9L3TK6W4HyM7zQDETD_yQ,264290 +pandas/core/indexes/category.py,sha256=_6LpQtBGFsgB4KSZhxEQT4QX57W3172MbvLIAzxboPA,16128 +pandas/core/indexes/datetimelike.py,sha256=JH8_o2NJNQj1A0N0YFcC3m5nQGStacI5bv1G-dzYKVA,28377 +pandas/core/indexes/datetimes.py,sha256=b0B5j5HGthjGLy4FLsMQtjPPjleZEH8agIWfLVne9v0,38327 +pandas/core/indexes/extension.py,sha256=Wy4XfMrJdc4HxuApZw4D-Xr3RyBlGCOKbI27L16tHEE,5188 +pandas/core/indexes/frozen.py,sha256=QuFW2zV8wqY7PD5PHbUMJQc3a-c5Eyfkjblp4umOylM,3482 +pandas/core/indexes/interval.py,sha256=79ddOFRsEoj7glRYNcq-L7rPB5y3jFxuOFnCD5lQW-o,38190 +pandas/core/indexes/multi.py,sha256=JskZSKvKotqbeS-2UE5hTB5eyJ5tuuotoZ3u47v33UY,143195 +pandas/core/indexes/period.py,sha256=ohh7J43CgV1ijxn9ozNhO5Vwu0k1-3yURIWTWeNPRgg,18978 +pandas/core/indexes/range.py,sha256=qt5IS2batjnOHe90UK5jES7pZhglppW_-1wieLlZysA,39511 +pandas/core/indexes/timedeltas.py,sha256=9a5m2wLQUA2v2O6JibpDSssNvNzV8Af6dAJETEpD4qM,10960 +pandas/core/indexing.py,sha256=TRrbtBeUrELiuFiCpAVuP0yIsfrxVLvBbT9bPvlCAmY,97236 +pandas/core/interchange/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/core/interchange/__pycache__/__init__.cpython-312.pyc,, +pandas/core/interchange/__pycache__/buffer.cpython-312.pyc,, +pandas/core/interchange/__pycache__/column.cpython-312.pyc,, +pandas/core/interchange/__pycache__/dataframe.cpython-312.pyc,, +pandas/core/interchange/__pycache__/dataframe_protocol.cpython-312.pyc,, +pandas/core/interchange/__pycache__/from_dataframe.cpython-312.pyc,, +pandas/core/interchange/__pycache__/utils.cpython-312.pyc,, +pandas/core/interchange/buffer.py,sha256=KujVQ1qeXMjgRdvwea37FqO9f2ULmLa6Rtr_mTQ11XU,3453 +pandas/core/interchange/column.py,sha256=tlHYyU6RP9ESD693d4WpDUNP0hq7MaTZnm6tLJXSq98,17547 +pandas/core/interchange/dataframe.py,sha256=M1mWjS70pYLFJau534NtgslcpY_8NiY4dRmRgT73TVo,3879 +pandas/core/interchange/dataframe_protocol.py,sha256=L9Wy8vB5oTsuYJQ9NBY4RIEAWXBclnTOH3I_txkIbZk,16177 +pandas/core/interchange/from_dataframe.py,sha256=VOTMZlybK4-omYdw-roufRYmQd9qJ-ryldcim0fyw_w,17043 +pandas/core/interchange/utils.py,sha256=mYIOfiwjnZd-I2j-SNRWRLFcSIlBtA9MLFCdzgynSFM,4837 +pandas/core/internals/__init__.py,sha256=LE8M58WRu_cvQZns2dxUMeBVjqNfwRWw6vtWKiBrr2I,2615 +pandas/core/internals/__pycache__/__init__.cpython-312.pyc,, +pandas/core/internals/__pycache__/api.cpython-312.pyc,, +pandas/core/internals/__pycache__/array_manager.cpython-312.pyc,, +pandas/core/internals/__pycache__/base.cpython-312.pyc,, +pandas/core/internals/__pycache__/blocks.cpython-312.pyc,, +pandas/core/internals/__pycache__/concat.cpython-312.pyc,, +pandas/core/internals/__pycache__/construction.cpython-312.pyc,, +pandas/core/internals/__pycache__/managers.cpython-312.pyc,, +pandas/core/internals/__pycache__/ops.cpython-312.pyc,, +pandas/core/internals/api.py,sha256=s78Hb4dHuBAufRH9vTd1KO6o0bs-9CoBOsRF6GP03lE,4695 +pandas/core/internals/array_manager.py,sha256=q_QKlETGKdb1r8aFKVfV4ZrMoVO1wFNAC2JNHCZ6rGE,43927 +pandas/core/internals/base.py,sha256=pO6sju5EIq7u23J7CGPZNTEotbL4KYKzRgyIEmBhqpg,11161 +pandas/core/internals/blocks.py,sha256=W7IYinBFN8KW5Rt0o7BJvmcgpgStQeLUq3PxpVEsrY8,98485 +pandas/core/internals/concat.py,sha256=Q_MnHIKSMBvIvA6DpMNkcsQSv8aU9DivUn1mlA_9zEs,19151 +pandas/core/internals/construction.py,sha256=IsWPruU6jDjeUAQqxsFJUIFr0MHUXNQNatP-AJYv2IA,33987 +pandas/core/internals/managers.py,sha256=toDgoWhpnOJiwytqyR_X5AmJkmqetYvBq6KbMR9T6-U,81576 +pandas/core/internals/ops.py,sha256=Rh2-gWjeSwXnjkiacohSNM5iNvqQqBiAqgblwP6rD9o,5145 +pandas/core/methods/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/core/methods/__pycache__/__init__.cpython-312.pyc,, +pandas/core/methods/__pycache__/describe.cpython-312.pyc,, +pandas/core/methods/__pycache__/selectn.cpython-312.pyc,, +pandas/core/methods/__pycache__/to_dict.cpython-312.pyc,, +pandas/core/methods/describe.py,sha256=IeCkAFDUdVNxoPPqP1R1HzDlKFQHvlg46AgIxntD5Cs,11961 +pandas/core/methods/selectn.py,sha256=oomBEebumUfbJ5OLi9vw7saH31vbiy3lK-i63VKWBOw,7696 +pandas/core/methods/to_dict.py,sha256=sep0EfimrQ5UNJu-KwC1uYzx1BvbrackOe2-qxl2F5Y,8649 +pandas/core/missing.py,sha256=x_XOmge6_k9uIij2tyJZBEFKpAju1xUS9knQhe5kleU,35270 +pandas/core/nanops.py,sha256=kJpYqWg4E-D89HOXcufquZH0_rPFRbgbmZAULygpDnU,50984 +pandas/core/ops/__init__.py,sha256=CQ7tQB-QPUxD6ZnbS2SzFVjjvCD7-ciglexkdbbn7y8,1620 +pandas/core/ops/__pycache__/__init__.cpython-312.pyc,, +pandas/core/ops/__pycache__/array_ops.cpython-312.pyc,, +pandas/core/ops/__pycache__/common.cpython-312.pyc,, +pandas/core/ops/__pycache__/dispatch.cpython-312.pyc,, +pandas/core/ops/__pycache__/docstrings.cpython-312.pyc,, +pandas/core/ops/__pycache__/invalid.cpython-312.pyc,, +pandas/core/ops/__pycache__/mask_ops.cpython-312.pyc,, +pandas/core/ops/__pycache__/missing.cpython-312.pyc,, +pandas/core/ops/array_ops.py,sha256=wNV7RL-HZoB_I61YlF5nskpH-4RxA2n3P_gj31i18FM,19079 +pandas/core/ops/common.py,sha256=jVf_L_oN6bKcUOuH6FgaKOx18se9C3Hl2JPd0Uoj4t4,3500 +pandas/core/ops/dispatch.py,sha256=5XFIr7HV1Dicohgm0ZJu-6argn2Qd0OwES2bBxQwCj0,635 +pandas/core/ops/docstrings.py,sha256=WlGWcWjNsldPW73krxbgRwQvkacmKqRqJsN4VVz-FXU,18448 +pandas/core/ops/invalid.py,sha256=5-gRzdBfk2F8qIZ_vzUlnI-vo1HsAh2F5BYJUEN--m0,1433 +pandas/core/ops/mask_ops.py,sha256=0sm9L1LB_USp8DxNBuCdoB8cJ_MzzvSAb_u3QQmQrKI,5409 +pandas/core/ops/missing.py,sha256=0WlqN_us0LU5RAdoitM-Ko_4xghJ_HBRkteLQ53fU14,5140 +pandas/core/resample.py,sha256=lckInCT6z43EuK8cf0Qono3W_LeJ2UqAoxMTWMxnvtQ,95578 +pandas/core/reshape/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/core/reshape/__pycache__/__init__.cpython-312.pyc,, +pandas/core/reshape/__pycache__/api.cpython-312.pyc,, +pandas/core/reshape/__pycache__/concat.cpython-312.pyc,, +pandas/core/reshape/__pycache__/encoding.cpython-312.pyc,, +pandas/core/reshape/__pycache__/melt.cpython-312.pyc,, +pandas/core/reshape/__pycache__/merge.cpython-312.pyc,, +pandas/core/reshape/__pycache__/pivot.cpython-312.pyc,, +pandas/core/reshape/__pycache__/reshape.cpython-312.pyc,, +pandas/core/reshape/__pycache__/tile.cpython-312.pyc,, +pandas/core/reshape/__pycache__/util.cpython-312.pyc,, +pandas/core/reshape/api.py,sha256=Qk5y-D5-OdRYKkCgc-ktcxKGNGSCPteISEsByXFWI9M,680 +pandas/core/reshape/concat.py,sha256=qwXsAlI9pnLld1pj9uqHf2zinXd-fj8GE3kZ-XNVacU,28253 +pandas/core/reshape/encoding.py,sha256=BN4hXm16hkz6bFQ56BgvoRb0YfdK-4CjWb4FcYRFBfk,18970 +pandas/core/reshape/melt.py,sha256=Zj6PSyI3Dbi_aQPhYyFTz_cWi9m8kIubwItq57JNCFQ,17400 +pandas/core/reshape/merge.py,sha256=K31zarc63I1cEl74TSPPITrLdpcVVAlpOslP2dTLYUo,99585 +pandas/core/reshape/pivot.py,sha256=ylkSVYQcoMmuxqvEoyEP6YHzeVtGL9y6ueAEfN6_RzY,28917 +pandas/core/reshape/reshape.py,sha256=_slnrYBb1ZFgqP1501D5JNF5LmWzD2PQGDtrzwk-eP0,34661 +pandas/core/reshape/tile.py,sha256=bDzSjjPydhiCce0DOJab1327a613mhs98PimwfIddjQ,21947 +pandas/core/reshape/util.py,sha256=zrShSZARSsWULoXI5tdWqwgZSLQ-u_3xNPS5cpB4QbY,2014 +pandas/core/roperator.py,sha256=ljko3iHhBm5ZvEVqrGEbwGV4z0cXd4TE1uSzf-LZlQ8,1114 +pandas/core/sample.py,sha256=QEPzbFmeMRMxAIqfkRrJLnIjUZgSupbP8YUEezW-Pcw,4626 +pandas/core/series.py,sha256=C6VAX8KkmsW-0T4ydIMqw7LoruFa5Yy14hcrvmTR8NA,213072 +pandas/core/shared_docs.py,sha256=Fdd7Xi1TQ_esZXq32Gu-ZPiShIHE2VROSSRtzet509s,30103 +pandas/core/sorting.py,sha256=kxr4Phz8HHAsEbyx9J5SCYZ4xENhoZnFmMEAUI-NpIU,22976 +pandas/core/sparse/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/core/sparse/__pycache__/__init__.cpython-312.pyc,, +pandas/core/sparse/__pycache__/api.cpython-312.pyc,, +pandas/core/sparse/api.py,sha256=y0onCpBKCj_5Iaybw5e-gxk8zAa9d1p5Zu58RLzPT1k,143 +pandas/core/strings/__init__.py,sha256=KYCMtwb7XWzZXsIZGijtjw9ofs2DIqE9psfKoxRsHuw,1087 +pandas/core/strings/__pycache__/__init__.cpython-312.pyc,, +pandas/core/strings/__pycache__/accessor.cpython-312.pyc,, +pandas/core/strings/__pycache__/base.cpython-312.pyc,, +pandas/core/strings/__pycache__/object_array.cpython-312.pyc,, +pandas/core/strings/accessor.py,sha256=_IAdGo_ZBjznkpvqStUZ2xyYyCuCoYtze9Trxb8Gd5I,112575 +pandas/core/strings/base.py,sha256=AdPlNkPgT218Mffx6Blt4aJF1GGxSYII3mem6EjWntY,5528 +pandas/core/strings/object_array.py,sha256=mCAo6lx6V1_UaoxcGWOgiAV0N-381rKcyAkqUHJ9kic,15438 +pandas/core/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/core/tools/__pycache__/__init__.cpython-312.pyc,, +pandas/core/tools/__pycache__/datetimes.cpython-312.pyc,, +pandas/core/tools/__pycache__/numeric.cpython-312.pyc,, +pandas/core/tools/__pycache__/timedeltas.cpython-312.pyc,, +pandas/core/tools/__pycache__/times.cpython-312.pyc,, +pandas/core/tools/datetimes.py,sha256=3KYS9voe_xTCyaVnZnqqEqt1lv1YVM9BzcvuV27sI_c,43404 +pandas/core/tools/numeric.py,sha256=f8HKUnKTNIPvlrFa4bbLy6pMH3ULSgce04qRzK5qV_Y,11025 +pandas/core/tools/timedeltas.py,sha256=kyDgKp9yRpw-gzucChvvekVQKy1sHu8J5qQwbwWaukg,8858 +pandas/core/tools/times.py,sha256=_-z5faRW4NA04LKN-eUgvklqOjRIncQyndFdSzwzDXI,5373 +pandas/core/util/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/core/util/__pycache__/__init__.cpython-312.pyc,, +pandas/core/util/__pycache__/hashing.cpython-312.pyc,, +pandas/core/util/__pycache__/numba_.cpython-312.pyc,, +pandas/core/util/hashing.py,sha256=LlYoJfn80z0zj0xNt5P3PYRVFJafXI3bRnSYV361Avs,9657 +pandas/core/util/numba_.py,sha256=U-2_obqjB_DwLc7Bu6swTCdPdNU62Z9l0QpxYM5Edng,2582 +pandas/core/window/__init__.py,sha256=DewB8XXkLGEDgtQqICYPmnkZZ3Y4tN6zPoTYvpNuJGE,450 +pandas/core/window/__pycache__/__init__.cpython-312.pyc,, +pandas/core/window/__pycache__/common.cpython-312.pyc,, +pandas/core/window/__pycache__/doc.cpython-312.pyc,, +pandas/core/window/__pycache__/ewm.cpython-312.pyc,, +pandas/core/window/__pycache__/expanding.cpython-312.pyc,, +pandas/core/window/__pycache__/numba_.cpython-312.pyc,, +pandas/core/window/__pycache__/online.cpython-312.pyc,, +pandas/core/window/__pycache__/rolling.cpython-312.pyc,, +pandas/core/window/common.py,sha256=LZBddjEy7C_nb-9gmsk2wQr-FsF1WBMsGKd8ptmMdug,6714 +pandas/core/window/doc.py,sha256=iCAs_hJ_pwstet2FHwSilVSXoTaKRuuMHwyZ9l2dz_c,4158 +pandas/core/window/ewm.py,sha256=nniOOhhrrx88wUd1iG2C2vyhT6mfd1N4UbDt4pY1F78,35190 +pandas/core/window/expanding.py,sha256=MnepmpreeY11OX9nQHj5TxgYdnOPJIRC-Cr3MyDnC38,27845 +pandas/core/window/numba_.py,sha256=7x9RvcIvPab0C5uXT4U9cP1VNaI7Yym0CevTsMIu27U,10606 +pandas/core/window/online.py,sha256=NKHkFpehR5QDT5VrCESEqjZ9a_Fq0JkchzmXFtzLRds,3735 +pandas/core/window/rolling.py,sha256=jj5NmCV28NgsWXMaBVqV-j8-JPwZOCu3heLi9AAbTMU,95504 +pandas/errors/__init__.py,sha256=DotJJfd-bS7FSQbnLC6SKWCfz_GqGYS6Gy6Fc9AJZg0,27164 +pandas/errors/__pycache__/__init__.cpython-312.pyc,, +pandas/io/__init__.py,sha256=4YJcSmLT6iTWceVgxGNSyRJq91wxhrgsNr47uc4Rw-I,293 +pandas/io/__pycache__/__init__.cpython-312.pyc,, +pandas/io/__pycache__/_util.cpython-312.pyc,, +pandas/io/__pycache__/api.cpython-312.pyc,, +pandas/io/__pycache__/clipboards.cpython-312.pyc,, +pandas/io/__pycache__/common.cpython-312.pyc,, +pandas/io/__pycache__/feather_format.cpython-312.pyc,, +pandas/io/__pycache__/gbq.cpython-312.pyc,, +pandas/io/__pycache__/html.cpython-312.pyc,, +pandas/io/__pycache__/orc.cpython-312.pyc,, +pandas/io/__pycache__/parquet.cpython-312.pyc,, +pandas/io/__pycache__/pickle.cpython-312.pyc,, +pandas/io/__pycache__/pytables.cpython-312.pyc,, +pandas/io/__pycache__/spss.cpython-312.pyc,, +pandas/io/__pycache__/sql.cpython-312.pyc,, +pandas/io/__pycache__/stata.cpython-312.pyc,, +pandas/io/__pycache__/xml.cpython-312.pyc,, +pandas/io/_util.py,sha256=0_dKFBocN0FV3XTzhOlDP55ToeHCre22RIKe6d6tRZs,961 +pandas/io/api.py,sha256=w7Ux3U8PI-SeP13hD3PMjWMf3YbOGog6zCDqj0nfnpI,1264 +pandas/io/clipboard/__init__.py,sha256=3aFzdoqbabE8XM-FYjdYIctTps_sTAJDJMrhEbDv_UU,24235 +pandas/io/clipboard/__pycache__/__init__.cpython-312.pyc,, +pandas/io/clipboards.py,sha256=t88NnxP8TOpmM1V438o6jgvlEMzlRLaqWBxUQiH_EQ8,6320 +pandas/io/common.py,sha256=hsjBpZc8i9O_aKMpCms0tuQ2jAqbkVzLXnUKI01TVcU,40615 +pandas/io/excel/__init__.py,sha256=w62gHQ9nF3XgBOmjhM8eHmV-YXF7gflz1lFqxFq7io8,486 +pandas/io/excel/__pycache__/__init__.cpython-312.pyc,, +pandas/io/excel/__pycache__/_base.cpython-312.pyc,, +pandas/io/excel/__pycache__/_calamine.cpython-312.pyc,, +pandas/io/excel/__pycache__/_odfreader.cpython-312.pyc,, +pandas/io/excel/__pycache__/_odswriter.cpython-312.pyc,, +pandas/io/excel/__pycache__/_openpyxl.cpython-312.pyc,, +pandas/io/excel/__pycache__/_pyxlsb.cpython-312.pyc,, +pandas/io/excel/__pycache__/_util.cpython-312.pyc,, +pandas/io/excel/__pycache__/_xlrd.cpython-312.pyc,, +pandas/io/excel/__pycache__/_xlsxwriter.cpython-312.pyc,, +pandas/io/excel/_base.py,sha256=tEBB5m3LcL8ZHv62Kv7G4Ul9MElr2X8JrkXvadypzC4,59073 +pandas/io/excel/_calamine.py,sha256=7O8I8yg-dpaK6OqdZflV14ggDbNDJrinhgAPxXgh9ro,3474 +pandas/io/excel/_odfreader.py,sha256=vMVZ-lNJpMB0vQ8cewanVpjj3-sFzUAS-I-w28nOmoY,8262 +pandas/io/excel/_odswriter.py,sha256=o7dP9MQYRyDO88kFeJMiyW5SmCxusykb8vew4QHMjsg,11210 +pandas/io/excel/_openpyxl.py,sha256=CshETVibZ0_rwbNq0y7sPkzSgnXpwI7FUtvAj8efU6Q,19861 +pandas/io/excel/_pyxlsb.py,sha256=74huu-7ISIsfvguwDID84B3KIooHtU53XOP3PFkX6ts,4358 +pandas/io/excel/_util.py,sha256=1fwMlNjLSd_qlCGLGBcXDPLnZ_SOpAZTIaUgYUVr0_0,8105 +pandas/io/excel/_xlrd.py,sha256=tddoGt7ugmyTTryMeqSvU6FE9vgajsMYfrSLQytMEV0,4556 +pandas/io/excel/_xlsxwriter.py,sha256=b0o_2MRgeTNG0loBRybT-xDoa65CjUeVC2wmuTUoR0M,9191 +pandas/io/feather_format.py,sha256=BjbwRVYhEvbp8w05sYd838VfgS4dNktb8T7m2PicjCM,4443 +pandas/io/formats/__init__.py,sha256=MGhPbyRcirFXg_uAGxyQ_q8Bky6ZUpBZ0nHXQa5LYd8,238 +pandas/io/formats/__pycache__/__init__.cpython-312.pyc,, +pandas/io/formats/__pycache__/_color_data.cpython-312.pyc,, +pandas/io/formats/__pycache__/console.cpython-312.pyc,, +pandas/io/formats/__pycache__/css.cpython-312.pyc,, +pandas/io/formats/__pycache__/csvs.cpython-312.pyc,, +pandas/io/formats/__pycache__/excel.cpython-312.pyc,, +pandas/io/formats/__pycache__/format.cpython-312.pyc,, +pandas/io/formats/__pycache__/html.cpython-312.pyc,, +pandas/io/formats/__pycache__/info.cpython-312.pyc,, +pandas/io/formats/__pycache__/printing.cpython-312.pyc,, +pandas/io/formats/__pycache__/string.cpython-312.pyc,, +pandas/io/formats/__pycache__/style.cpython-312.pyc,, +pandas/io/formats/__pycache__/style_render.cpython-312.pyc,, +pandas/io/formats/__pycache__/xml.cpython-312.pyc,, +pandas/io/formats/_color_data.py,sha256=fZ_QluvMFUNKUE4-T32x7Pn0nulQgxmsEMHB9URcBOY,4332 +pandas/io/formats/console.py,sha256=dcoFM-rirR8qdc1bvgJySPhZvk23S6Nkz3-2Lc30pMk,2748 +pandas/io/formats/css.py,sha256=gCSjRV6QatAMY-La26wnrQmyF78G4BruMfpWrDIKIkk,12793 +pandas/io/formats/csvs.py,sha256=JAI3kO6xKSMjsLxlYk4EijBuktOHRwU9U91a92OvYnQ,10526 +pandas/io/formats/excel.py,sha256=vW5_Pii4i_wv_VNVR0wn-7IFwdgf2tzROor4eThVO68,32994 +pandas/io/formats/format.py,sha256=FPeKW4UASjOLB-N73HfVZWVviqUbDPoBoVLCQxhJJjE,66127 +pandas/io/formats/html.py,sha256=AiROfWxTRrMT75LZsrBMJTIs3ky9n1x3nUnXzKpZILM,24165 +pandas/io/formats/info.py,sha256=heCm4flQPvNMNW6zecz_XUrfV5O-_zWdpam_dk3V2Tc,32621 +pandas/io/formats/printing.py,sha256=Hrs0vaaacrfswH7FuPCM9FnVg5kKL5vGYl8-ZxAQC4Q,17950 +pandas/io/formats/string.py,sha256=f6UNLnvUV-iO-7k7zXqWBOs7hOoU7_fWQzogyeY8c7I,6707 +pandas/io/formats/style.py,sha256=7bM9ookFZdRMVjamIUzCcyfiYAWM0ksTd9m3UnvyDA0,155854 +pandas/io/formats/style_render.py,sha256=TgyXK40A4dp8geKIeGWMwNm_v597jWQmJZH-H-TSSdQ,90899 +pandas/io/formats/templates/html.tpl,sha256=KA-w_npfnHM_1c5trtJtkd3OD9j8hqtoQAY4GCC5UgI,412 +pandas/io/formats/templates/html_style.tpl,sha256=_gCqktLyUGAo5TzL3I-UCp1Njj8KyeLCWunHz4nYHsE,694 +pandas/io/formats/templates/html_table.tpl,sha256=MJxwJFwOa4KNli-ix7vYAGjRzw59FLAmYKHMy9nC32k,1811 +pandas/io/formats/templates/latex.tpl,sha256=m-YMxqKVJ52kLd61CA9V2MiC_Dtwwa-apvU8YtH8TYU,127 +pandas/io/formats/templates/latex_longtable.tpl,sha256=opn-JNfuMX81g1UOWYFJLKdQSUwoSP_UAKbK4kYRph4,2877 +pandas/io/formats/templates/latex_table.tpl,sha256=YNvnvjtwYXrWFVXndQZdJqKFIXYTUj8f1YOUdMmxXmQ,2221 +pandas/io/formats/templates/string.tpl,sha256=Opr87f1tY8yp_G7GOY8ouFllR_7vffN_ok7Ndf98joE,344 +pandas/io/formats/xml.py,sha256=dLBpVLGltVRiOxYCIVLb4okLXwhPneRp7whi2VbV1gk,16029 +pandas/io/gbq.py,sha256=nkdYZ0w5ZetYdWpIIKALLh5_3nNhFE1hvVV9rJ5yyhk,9372 +pandas/io/html.py,sha256=E4rdZT6DVcMRSeDaceBsMpWrc-A9aAEvF5sbW4DstIg,39546 +pandas/io/json/__init__.py,sha256=ArWTQnIKhxDVaMI1j0Whgpk0ci6dP0mpUiGwMRqEdtY,270 +pandas/io/json/__pycache__/__init__.cpython-312.pyc,, +pandas/io/json/__pycache__/_json.cpython-312.pyc,, +pandas/io/json/__pycache__/_normalize.cpython-312.pyc,, +pandas/io/json/__pycache__/_table_schema.cpython-312.pyc,, +pandas/io/json/_json.py,sha256=KxErOL4x5IkSnFsgivvNBs6ZWuSxAWX29cKguk2OEQs,48572 +pandas/io/json/_normalize.py,sha256=rbyrEKwuxotrABiv6Jmb9JN6k6rCXd99ONrEZv2IbXI,17212 +pandas/io/json/_table_schema.py,sha256=Ld6OMQsdCutRvmGHPayKOTf08BNTnhuFwcQGRnlCq_w,11594 +pandas/io/orc.py,sha256=c6HnmrCBhfe6dzGu4LxfKwxDOraHckh-WYf9UNN_xno,8385 +pandas/io/parquet.py,sha256=npe1SKcJbfLM6vSPVDQDeoEsPIIfJzlAKstzNGmoOVQ,23835 +pandas/io/parsers/__init__.py,sha256=7BLx4kn9y5ipgfZUWZ4y_MLEUNgX6MQ5DyDwshhJxVM,204 +pandas/io/parsers/__pycache__/__init__.cpython-312.pyc,, +pandas/io/parsers/__pycache__/arrow_parser_wrapper.cpython-312.pyc,, +pandas/io/parsers/__pycache__/base_parser.cpython-312.pyc,, +pandas/io/parsers/__pycache__/c_parser_wrapper.cpython-312.pyc,, +pandas/io/parsers/__pycache__/python_parser.cpython-312.pyc,, +pandas/io/parsers/__pycache__/readers.cpython-312.pyc,, +pandas/io/parsers/arrow_parser_wrapper.py,sha256=XE5SuEdcu2M-wlEgAC8gAZaZDvA_O31_vJRFJqMAbWg,11409 +pandas/io/parsers/base_parser.py,sha256=HRzZBK2fm9dmi7OcBiljVtQdd6c-cWsCZcHLFRiblo0,49443 +pandas/io/parsers/c_parser_wrapper.py,sha256=yXK-ZrUOxZcXdZ9rtINgRl7l426tdoch8GyZIS_nCMI,14199 +pandas/io/parsers/python_parser.py,sha256=9fnAQ5iFQwBETy-6ptu66-3Ppu8tn81CGSRyYxhgE2I,48456 +pandas/io/parsers/readers.py,sha256=yP4xBAdreacpmmKamh7w6O4CTl0NQ5z0UVSuA7LSs0c,87157 +pandas/io/pickle.py,sha256=t4OulGy7CQL60LXTC8kebegWM7QaJOmudlynAgWxo4w,6582 +pandas/io/pytables.py,sha256=o8JItkD0B5Uewjks5IPtyv5JtHzqD94yUO6xVQ90kX8,177239 +pandas/io/sas/__init__.py,sha256=AIAudC9f784kcEzuho8GiXU63vj2ThRitKznl7Imkq4,69 +pandas/io/sas/__pycache__/__init__.cpython-312.pyc,, +pandas/io/sas/__pycache__/sas7bdat.cpython-312.pyc,, +pandas/io/sas/__pycache__/sas_constants.cpython-312.pyc,, +pandas/io/sas/__pycache__/sas_xport.cpython-312.pyc,, +pandas/io/sas/__pycache__/sasreader.cpython-312.pyc,, +pandas/io/sas/sas7bdat.py,sha256=IGdDWp_EivJPJYmfv3jbJNtt6BZtbupRskKPvQ16KIo,27534 +pandas/io/sas/sas_constants.py,sha256=CM1wSNzXn6nkjLMSTeBhBJlL6d0hU-1YdNwEO8HE-9U,8719 +pandas/io/sas/sas_xport.py,sha256=_N7sGHw4Z80u-emCxS4lv6UFs6N01eKj5CZkTzq7XiM,15134 +pandas/io/sas/sasreader.py,sha256=S7bRlsXahhpoTkKdsHoWY9TLo_jgzNJJdsb6gxpcfuY,4885 +pandas/io/spss.py,sha256=p4vW9rJEFLPBqEIHMR5fCmo2U-JBTvgnDNd74Y7DFuI,2182 +pandas/io/sql.py,sha256=AezlzGw76UejHVblu1x9tGpex6bmSg2_QuuvBVnSf0g,101704 +pandas/io/stata.py,sha256=dgPveWarql9uZ6oSOO02Dfnf7ZmiFye2LUG0-9IrEiY,135790 +pandas/io/xml.py,sha256=ZKHsFACIJhlNJqU8nNBpG-OjHZ2uE_wzh94OOBuj8iI,38656 +pandas/plotting/__init__.py,sha256=W_2wP9v02mNCK4lV5ekG1iJHYSF8dD1NbByJiNq3g8I,2826 +pandas/plotting/__pycache__/__init__.cpython-312.pyc,, +pandas/plotting/__pycache__/_core.cpython-312.pyc,, +pandas/plotting/__pycache__/_misc.cpython-312.pyc,, +pandas/plotting/_core.py,sha256=BLIzDrRcaDDYBpXj8nfw3aIXabos6YlwPjondYmh6II,66558 +pandas/plotting/_matplotlib/__init__.py,sha256=jGq_ouunQTV3zzX_crl9kCVX2ztk1p62McqD2WVRnAk,2044 +pandas/plotting/_matplotlib/__pycache__/__init__.cpython-312.pyc,, +pandas/plotting/_matplotlib/__pycache__/boxplot.cpython-312.pyc,, +pandas/plotting/_matplotlib/__pycache__/converter.cpython-312.pyc,, +pandas/plotting/_matplotlib/__pycache__/core.cpython-312.pyc,, +pandas/plotting/_matplotlib/__pycache__/groupby.cpython-312.pyc,, +pandas/plotting/_matplotlib/__pycache__/hist.cpython-312.pyc,, +pandas/plotting/_matplotlib/__pycache__/misc.cpython-312.pyc,, +pandas/plotting/_matplotlib/__pycache__/style.cpython-312.pyc,, +pandas/plotting/_matplotlib/__pycache__/timeseries.cpython-312.pyc,, +pandas/plotting/_matplotlib/__pycache__/tools.cpython-312.pyc,, +pandas/plotting/_matplotlib/boxplot.py,sha256=xzXBEoBmC1U9VGlYCvqXEfjAabAFgI69nUbOzHm9zmc,18261 +pandas/plotting/_matplotlib/converter.py,sha256=EcdgaqQPOqYIO2noB-6J2xkODsBwATamuwA315SCVog,37033 +pandas/plotting/_matplotlib/core.py,sha256=20oTgXZwzTQDfqBY6g_HT9CsGd1RkuNtnu0YE-rtO5U,71826 +pandas/plotting/_matplotlib/groupby.py,sha256=vg8RYC3SxN2Khc-34GDV3UpCVSPnawt4zwYqIuzb5HE,4343 +pandas/plotting/_matplotlib/hist.py,sha256=uljuycUD16A6u3GdktvZwXdU3qMKPfFLFMgYmBX4zQU,16816 +pandas/plotting/_matplotlib/misc.py,sha256=tzbAVRDGc1Ep6BR3QbYAEKEHgkX2vwMBX9k9uwN-j8c,13358 +pandas/plotting/_matplotlib/style.py,sha256=mKDcq4cBmYF9zDrBv3st3fNFvSn-91rYEH5cLXaYiw0,8368 +pandas/plotting/_matplotlib/timeseries.py,sha256=Mw3zTUVL8NR1bUCxWrait8kPCB9DHBkm8skT_RdEQ3k,11531 +pandas/plotting/_matplotlib/tools.py,sha256=7YrV3B-bXVm6AI-QekcC4CSKRLB9ZM7fakEOzm5gm1k,15389 +pandas/plotting/_misc.py,sha256=sbOaqkE9lA5HbikzcFBcXe9tdqHMVAxxMH3V9QfYr-c,20929 +pandas/pyproject.toml,sha256=zVYCcwPAWYkyN0xtApuGIQUfWpd-c-VJ55WzQNx2LkQ,24456 +pandas/testing.py,sha256=3XTHuY440lezW7rxw4LW9gfxzDEa7s0l16cdnkRYwwM,313 +pandas/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/__pycache__/test_aggregation.cpython-312.pyc,, +pandas/tests/__pycache__/test_algos.cpython-312.pyc,, +pandas/tests/__pycache__/test_common.cpython-312.pyc,, +pandas/tests/__pycache__/test_downstream.cpython-312.pyc,, +pandas/tests/__pycache__/test_errors.cpython-312.pyc,, +pandas/tests/__pycache__/test_expressions.cpython-312.pyc,, +pandas/tests/__pycache__/test_flags.cpython-312.pyc,, +pandas/tests/__pycache__/test_multilevel.cpython-312.pyc,, +pandas/tests/__pycache__/test_nanops.cpython-312.pyc,, +pandas/tests/__pycache__/test_optional_dependency.cpython-312.pyc,, +pandas/tests/__pycache__/test_register_accessor.cpython-312.pyc,, +pandas/tests/__pycache__/test_sorting.cpython-312.pyc,, +pandas/tests/__pycache__/test_take.cpython-312.pyc,, +pandas/tests/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/api/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/api/__pycache__/test_api.cpython-312.pyc,, +pandas/tests/api/__pycache__/test_types.cpython-312.pyc,, +pandas/tests/api/test_api.py,sha256=ZQI3_TgIuolTfuKy-a4eds0io74Q4kvy8fG6NZDoj-M,9394 +pandas/tests/api/test_types.py,sha256=ZR8n_efaY7HWGY6XnRZKNIiRWmaszpNU8p22kvAbyEQ,1711 +pandas/tests/apply/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/apply/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/apply/__pycache__/common.cpython-312.pyc,, +pandas/tests/apply/__pycache__/test_frame_apply.cpython-312.pyc,, +pandas/tests/apply/__pycache__/test_frame_apply_relabeling.cpython-312.pyc,, +pandas/tests/apply/__pycache__/test_frame_transform.cpython-312.pyc,, +pandas/tests/apply/__pycache__/test_invalid_arg.cpython-312.pyc,, +pandas/tests/apply/__pycache__/test_numba.cpython-312.pyc,, +pandas/tests/apply/__pycache__/test_series_apply.cpython-312.pyc,, +pandas/tests/apply/__pycache__/test_series_apply_relabeling.cpython-312.pyc,, +pandas/tests/apply/__pycache__/test_series_transform.cpython-312.pyc,, +pandas/tests/apply/__pycache__/test_str.cpython-312.pyc,, +pandas/tests/apply/common.py,sha256=A8TqjvKR4h4WaLtovGR9hDULpWs4rV-1Jx_Q4Zz5Dew,298 +pandas/tests/apply/test_frame_apply.py,sha256=MNA70UiPF9BisXVGpvQTt1SjZTMj7-J5p_43BaZ-4Ao,54256 +pandas/tests/apply/test_frame_apply_relabeling.py,sha256=jHfewakLcFvc1nartXtElv7HM5eGUIelIcm-McXX2KQ,3772 +pandas/tests/apply/test_frame_transform.py,sha256=bbAcYmXxlfEo8-zPQdxlp26s9LPlRbpVKpQu9yEVkCI,8020 +pandas/tests/apply/test_invalid_arg.py,sha256=4X6SQ_1Y21KMxhpn7CFx5l1Gdky70ERJwRClcGxYJwA,10983 +pandas/tests/apply/test_numba.py,sha256=XUiNthXaQTEB1mJSD_wkNEE_h0Blk1lMkVt9DBwBHCs,3810 +pandas/tests/apply/test_series_apply.py,sha256=Mak1zJWdYx6mX-0-OzHImTzYSGl9UVPSsPKVUAdMNoI,22485 +pandas/tests/apply/test_series_apply_relabeling.py,sha256=_HkoIybNJQFEpIaafHvD1Q0nx_U9J2aL8ualcwhp5Fs,1510 +pandas/tests/apply/test_series_transform.py,sha256=rrJO-C5HagNKJo542h32eB5TOWVDxirJv1u5PXJkh_I,2404 +pandas/tests/apply/test_str.py,sha256=k34l2s3s5p2NUzwUFOtW6sePl9ureo6Q8EaY5PEqy1w,11043 +pandas/tests/arithmetic/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/arithmetic/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/arithmetic/__pycache__/common.cpython-312.pyc,, +pandas/tests/arithmetic/__pycache__/conftest.cpython-312.pyc,, +pandas/tests/arithmetic/__pycache__/test_array_ops.cpython-312.pyc,, +pandas/tests/arithmetic/__pycache__/test_categorical.cpython-312.pyc,, +pandas/tests/arithmetic/__pycache__/test_datetime64.cpython-312.pyc,, +pandas/tests/arithmetic/__pycache__/test_interval.cpython-312.pyc,, +pandas/tests/arithmetic/__pycache__/test_numeric.cpython-312.pyc,, +pandas/tests/arithmetic/__pycache__/test_object.cpython-312.pyc,, +pandas/tests/arithmetic/__pycache__/test_period.cpython-312.pyc,, +pandas/tests/arithmetic/__pycache__/test_timedelta64.cpython-312.pyc,, +pandas/tests/arithmetic/common.py,sha256=C_s1Zc2_0U_oBciQNt5xJp-8FaLmkscEdmnX2Nq16UY,4362 +pandas/tests/arithmetic/conftest.py,sha256=uUtu5-T5FBdFQAo21vRLQSHPiNEjWkc69UwH6llpnsM,3473 +pandas/tests/arithmetic/test_array_ops.py,sha256=4lmZRZAlbJEnphzzwfcvsO4kEv1LG9l3uCmaF_8kcAA,1064 +pandas/tests/arithmetic/test_categorical.py,sha256=lK5fXv4cRIu69ocvOHfKL5bjeK0jDdW3psvrrssjDoA,742 +pandas/tests/arithmetic/test_datetime64.py,sha256=f97V90PrRZrFZ_IrBxfEtgDXvYI_JGqMsIl__9b0y9E,90255 +pandas/tests/arithmetic/test_interval.py,sha256=2TG1Lh4VZXaxwjs5y5RjXzIukOfoVetyLfPlOo5h4vQ,10951 +pandas/tests/arithmetic/test_numeric.py,sha256=569JY7Pjl453iXP_txrlktVyUyH1CR_3677due2sfwU,55511 +pandas/tests/arithmetic/test_object.py,sha256=PJ-_UpBqHXs1_29Q60xlhK4J0m2yX-HIvW_Auknsr98,13525 +pandas/tests/arithmetic/test_period.py,sha256=uxdkrPIpMM7BWUKmwloViCEE1JtOsxkXKCdfxLQ6E1A,59617 +pandas/tests/arithmetic/test_timedelta64.py,sha256=OH0dD4KNrVEf8FlC75MezthgEDohA8dyk3uxwouF8LM,78911 +pandas/tests/arrays/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/arrays/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/arrays/__pycache__/masked_shared.cpython-312.pyc,, +pandas/tests/arrays/__pycache__/test_array.cpython-312.pyc,, +pandas/tests/arrays/__pycache__/test_datetimelike.cpython-312.pyc,, +pandas/tests/arrays/__pycache__/test_datetimes.cpython-312.pyc,, +pandas/tests/arrays/__pycache__/test_ndarray_backed.cpython-312.pyc,, +pandas/tests/arrays/__pycache__/test_period.cpython-312.pyc,, +pandas/tests/arrays/__pycache__/test_timedeltas.cpython-312.pyc,, +pandas/tests/arrays/boolean/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/arrays/boolean/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/arrays/boolean/__pycache__/test_arithmetic.cpython-312.pyc,, +pandas/tests/arrays/boolean/__pycache__/test_astype.cpython-312.pyc,, +pandas/tests/arrays/boolean/__pycache__/test_comparison.cpython-312.pyc,, +pandas/tests/arrays/boolean/__pycache__/test_construction.cpython-312.pyc,, +pandas/tests/arrays/boolean/__pycache__/test_function.cpython-312.pyc,, +pandas/tests/arrays/boolean/__pycache__/test_indexing.cpython-312.pyc,, +pandas/tests/arrays/boolean/__pycache__/test_logical.cpython-312.pyc,, +pandas/tests/arrays/boolean/__pycache__/test_ops.cpython-312.pyc,, +pandas/tests/arrays/boolean/__pycache__/test_reduction.cpython-312.pyc,, +pandas/tests/arrays/boolean/__pycache__/test_repr.cpython-312.pyc,, +pandas/tests/arrays/boolean/test_arithmetic.py,sha256=C6y1v1C7kz73WCfl3MS-KleylT4aYdVUDdtP0RBxrg8,4241 +pandas/tests/arrays/boolean/test_astype.py,sha256=0AEVw8lNNjHomdqgpQ7ZYCauUb23QHvxY3NPDe7vIQw,1614 +pandas/tests/arrays/boolean/test_comparison.py,sha256=QIX85ffCwMvtzXtLkWePFQkso_mVtIffWpbgy4ykEz0,1976 +pandas/tests/arrays/boolean/test_construction.py,sha256=1KGaMjJ3FTmoisMbEnKUuxAkylVyzTsfuRXZV5UXlIk,12332 +pandas/tests/arrays/boolean/test_function.py,sha256=eAVsu1XUeokLh7Ko0-bDNUQqmVrGAyOvv9vJdWCQj0M,4061 +pandas/tests/arrays/boolean/test_indexing.py,sha256=BorrK8_ZJbN5HWcIX9fCP-BbTCaJsgAGUiza5IwhYr4,361 +pandas/tests/arrays/boolean/test_logical.py,sha256=7kJTl0KbLA7n8dOV0PZtiZ7gPm65Ggc3p0tHOF5i0d0,9335 +pandas/tests/arrays/boolean/test_ops.py,sha256=iM_FRYMtvvdEpMtLUSuBd_Ww5nHr284v2fRxHaydvIM,975 +pandas/tests/arrays/boolean/test_reduction.py,sha256=eBdonU5n9zsbC86AscHCLxF68XqiqhWWyBJV-7YCOdA,2183 +pandas/tests/arrays/boolean/test_repr.py,sha256=RRljPIDi6jDNhUdbjKMc75Mst-wm92l-H6b5Y-lCCJA,437 +pandas/tests/arrays/categorical/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/arrays/categorical/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/arrays/categorical/__pycache__/test_algos.cpython-312.pyc,, +pandas/tests/arrays/categorical/__pycache__/test_analytics.cpython-312.pyc,, +pandas/tests/arrays/categorical/__pycache__/test_api.cpython-312.pyc,, +pandas/tests/arrays/categorical/__pycache__/test_astype.cpython-312.pyc,, +pandas/tests/arrays/categorical/__pycache__/test_constructors.cpython-312.pyc,, +pandas/tests/arrays/categorical/__pycache__/test_dtypes.cpython-312.pyc,, +pandas/tests/arrays/categorical/__pycache__/test_indexing.cpython-312.pyc,, +pandas/tests/arrays/categorical/__pycache__/test_map.cpython-312.pyc,, +pandas/tests/arrays/categorical/__pycache__/test_missing.cpython-312.pyc,, +pandas/tests/arrays/categorical/__pycache__/test_operators.cpython-312.pyc,, +pandas/tests/arrays/categorical/__pycache__/test_replace.cpython-312.pyc,, +pandas/tests/arrays/categorical/__pycache__/test_repr.cpython-312.pyc,, +pandas/tests/arrays/categorical/__pycache__/test_sorting.cpython-312.pyc,, +pandas/tests/arrays/categorical/__pycache__/test_subclass.cpython-312.pyc,, +pandas/tests/arrays/categorical/__pycache__/test_take.cpython-312.pyc,, +pandas/tests/arrays/categorical/__pycache__/test_warnings.cpython-312.pyc,, +pandas/tests/arrays/categorical/test_algos.py,sha256=SLguZHlE5eyi14kRoMUGpIohPJM7jQqboKlnTvidpg0,2710 +pandas/tests/arrays/categorical/test_analytics.py,sha256=Bl7A_lPouoS7uK8EnybqvtMXp6WatI7U89OQwMecWVY,13213 +pandas/tests/arrays/categorical/test_api.py,sha256=Ivy3G6MW43fLMYwWn9QdE9wXRxLrpF8IFoUpB-TplCc,19879 +pandas/tests/arrays/categorical/test_astype.py,sha256=EJc8J2mrxN2Epg_6ufPxf3qLlqIsV66dyDbvjJoJDJg,5546 +pandas/tests/arrays/categorical/test_constructors.py,sha256=NFmmMYKBtBHxrM3d4nBxG0Zck1-n-uEQsbADePuAbl0,30705 +pandas/tests/arrays/categorical/test_dtypes.py,sha256=h1ZhuPvbHp9aFA4doAkmQ96zQW4A5UX6y6Yv2G5QTb8,5523 +pandas/tests/arrays/categorical/test_indexing.py,sha256=u43KuLMFtxe5ZAs0dphmGqpHsygyxtmTHxdGEfoDVQg,12972 +pandas/tests/arrays/categorical/test_map.py,sha256=TO6GY6B2n2dhkcNRQinbvID9eBfwtVnWsT1yexQg00U,5152 +pandas/tests/arrays/categorical/test_missing.py,sha256=5KdSj982_KUkfB8Cg-l7Jcir5I8n7Gz6SbnHnIqmu8A,7814 +pandas/tests/arrays/categorical/test_operators.py,sha256=NDc6FKDGOrGIdvSDpJ9Mq9O-aE0xw-LoI6L-rcrW0cI,15968 +pandas/tests/arrays/categorical/test_replace.py,sha256=I3jiQGmNSQ2i1WTLgVjIKcH-D919sf9EWTOm-hh_emE,4102 +pandas/tests/arrays/categorical/test_repr.py,sha256=4ft4OCt7r3qZDFd5CPrsyYSq7ZVrxcrRwpxQs91Mm-A,27107 +pandas/tests/arrays/categorical/test_sorting.py,sha256=gEhLklhDxhqf8UDOB17TMKhrabxS5n0evPg9DWSMd5s,5052 +pandas/tests/arrays/categorical/test_subclass.py,sha256=Y4nURd4hFM0Q3aVET1OO-z11pZzzZ0HFfl2s-9OWemw,903 +pandas/tests/arrays/categorical/test_take.py,sha256=O4g_LYDeK0NzHDId5cBBEp1ns_a762NsYHn088ocYzg,3501 +pandas/tests/arrays/categorical/test_warnings.py,sha256=XqvGeAb9lrXP1VdwKSOvbDuytqDuJ5VSDsLKQAa5gIk,682 +pandas/tests/arrays/datetimes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/arrays/datetimes/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/arrays/datetimes/__pycache__/test_constructors.cpython-312.pyc,, +pandas/tests/arrays/datetimes/__pycache__/test_cumulative.cpython-312.pyc,, +pandas/tests/arrays/datetimes/__pycache__/test_reductions.cpython-312.pyc,, +pandas/tests/arrays/datetimes/test_constructors.py,sha256=xZsxdsUxxbk7UCawlCS3_aAkhsuexX0-uf3XQMlvSA8,11050 +pandas/tests/arrays/datetimes/test_cumulative.py,sha256=X_SHtt9n_WzA_C2wPlRJHRS8LUmjNNmr2-XL6AszJd0,1307 +pandas/tests/arrays/datetimes/test_reductions.py,sha256=Cg1qwq8wASnMeOdZ5_wowrILL6e1ZT_j8m-rIOkwrkg,5787 +pandas/tests/arrays/floating/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/arrays/floating/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/arrays/floating/__pycache__/conftest.cpython-312.pyc,, +pandas/tests/arrays/floating/__pycache__/test_arithmetic.cpython-312.pyc,, +pandas/tests/arrays/floating/__pycache__/test_astype.cpython-312.pyc,, +pandas/tests/arrays/floating/__pycache__/test_comparison.cpython-312.pyc,, +pandas/tests/arrays/floating/__pycache__/test_concat.cpython-312.pyc,, +pandas/tests/arrays/floating/__pycache__/test_construction.cpython-312.pyc,, +pandas/tests/arrays/floating/__pycache__/test_contains.cpython-312.pyc,, +pandas/tests/arrays/floating/__pycache__/test_function.cpython-312.pyc,, +pandas/tests/arrays/floating/__pycache__/test_repr.cpython-312.pyc,, +pandas/tests/arrays/floating/__pycache__/test_to_numpy.cpython-312.pyc,, +pandas/tests/arrays/floating/conftest.py,sha256=PkAOd0oDvePBtXL-N0MnmEGCmDMP3_Dw-YwpxgNfl-k,1161 +pandas/tests/arrays/floating/test_arithmetic.py,sha256=z2y4ca3ntG7WZPntefFBO5IbwLSc98uNUHJeoKzi_Dc,8353 +pandas/tests/arrays/floating/test_astype.py,sha256=pvgAFQ0bTRyuoBpgmiyQza_zPOXBC7RYdGJc7F6tP4c,4047 +pandas/tests/arrays/floating/test_comparison.py,sha256=C-rwNTv5FtUvo3oWB8XNquCOa_XQHf6R9JRYX6JVAG0,2071 +pandas/tests/arrays/floating/test_concat.py,sha256=-RO-pwRRY93FQnOjBLs1fMVf7uBCoEGRkGWPAdX8ltU,573 +pandas/tests/arrays/floating/test_construction.py,sha256=weDvGh2hSfHmVnQ-6Kc5QmAUaGTF9mvEI3qtZSEHHAk,6455 +pandas/tests/arrays/floating/test_contains.py,sha256=oTsN_kyhRi7hHdKRzi9PzwSu2gHiE3EP4FkuR31BZFM,204 +pandas/tests/arrays/floating/test_function.py,sha256=YiXRdFHEU2iAGXwd68kDyfsjBZ8ztoC8fikZU6AnbRE,6403 +pandas/tests/arrays/floating/test_repr.py,sha256=N_BX7NbU8Pljiz2bouWMzrP22xh_6w_8pHePEB2ycVw,1157 +pandas/tests/arrays/floating/test_to_numpy.py,sha256=d0k_2WXrkIu4JOGkIQlzijmgsm7X-XW2XmobaN_3Q_s,4954 +pandas/tests/arrays/integer/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/arrays/integer/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/arrays/integer/__pycache__/conftest.cpython-312.pyc,, +pandas/tests/arrays/integer/__pycache__/test_arithmetic.cpython-312.pyc,, +pandas/tests/arrays/integer/__pycache__/test_comparison.cpython-312.pyc,, +pandas/tests/arrays/integer/__pycache__/test_concat.cpython-312.pyc,, +pandas/tests/arrays/integer/__pycache__/test_construction.cpython-312.pyc,, +pandas/tests/arrays/integer/__pycache__/test_dtypes.cpython-312.pyc,, +pandas/tests/arrays/integer/__pycache__/test_function.cpython-312.pyc,, +pandas/tests/arrays/integer/__pycache__/test_indexing.cpython-312.pyc,, +pandas/tests/arrays/integer/__pycache__/test_reduction.cpython-312.pyc,, +pandas/tests/arrays/integer/__pycache__/test_repr.cpython-312.pyc,, +pandas/tests/arrays/integer/conftest.py,sha256=TejO1KxvoPETsN-ZdefGePhwJ-szaoYanP9AQXHgY18,1555 +pandas/tests/arrays/integer/test_arithmetic.py,sha256=JAlNBmbw3cR-XhUVHqyRk61Xth__oFFq_h6H-88LRaI,12391 +pandas/tests/arrays/integer/test_comparison.py,sha256=jUr8dmk_6FQsTNjDkYsazWnioHis4cLi94noy4txG54,1212 +pandas/tests/arrays/integer/test_concat.py,sha256=TmHNsCxxvp-KDLD5SaTmeEuWJDzUS51Eg04uSWet9Pg,2351 +pandas/tests/arrays/integer/test_construction.py,sha256=jnzOs0w8i4X55JOrtXc0ylMaiBo8mhRl6uwrnEWr_0o,7768 +pandas/tests/arrays/integer/test_dtypes.py,sha256=5pq5zqlv9oEGyk8v0KOUF_BCwnsogS08uEnCV1cF0yk,8756 +pandas/tests/arrays/integer/test_function.py,sha256=hCqZIrrISPtn_7mlX92wpQNItAF1o-q-g56W93wnyhI,6627 +pandas/tests/arrays/integer/test_indexing.py,sha256=rgwcafGbwJztl_N4CalvAnW6FKfKVNzJcE-RjcXMpR8,498 +pandas/tests/arrays/integer/test_reduction.py,sha256=vOyzjEWQTpsGXLOa2H8ehkahUiYBBcKvEI__6YQtzdo,4215 +pandas/tests/arrays/integer/test_repr.py,sha256=fLTZusgFHPXO4orpygmHIOG6JQLzYcdbTJHRvvsN0sM,1652 +pandas/tests/arrays/interval/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/arrays/interval/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/arrays/interval/__pycache__/test_astype.cpython-312.pyc,, +pandas/tests/arrays/interval/__pycache__/test_formats.cpython-312.pyc,, +pandas/tests/arrays/interval/__pycache__/test_interval.cpython-312.pyc,, +pandas/tests/arrays/interval/__pycache__/test_interval_pyarrow.cpython-312.pyc,, +pandas/tests/arrays/interval/__pycache__/test_overlaps.cpython-312.pyc,, +pandas/tests/arrays/interval/test_astype.py,sha256=8rb7rssqvIoSztzCfFb5pY4oIH_GjDStKrXkC6bnUZk,776 +pandas/tests/arrays/interval/test_formats.py,sha256=AARSRfiyQa0Fu6jCBdhx83yJOXdCWtfs0q0Yd8mMxwg,317 +pandas/tests/arrays/interval/test_interval.py,sha256=cfZXy6J5AtUqwd5HY4m9lxTyu0m0xsZbD9FlcBebuio,8082 +pandas/tests/arrays/interval/test_interval_pyarrow.py,sha256=PkPTrpsrTLL_3Vd17ENP0I3NFE71XpSQi38HG09hXxo,5202 +pandas/tests/arrays/interval/test_overlaps.py,sha256=4QNJBVY5Fb150Rf3lS5a6p_ScHy8U-sAuWTWetbCmVc,3279 +pandas/tests/arrays/masked/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/arrays/masked/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/arrays/masked/__pycache__/test_arithmetic.cpython-312.pyc,, +pandas/tests/arrays/masked/__pycache__/test_arrow_compat.cpython-312.pyc,, +pandas/tests/arrays/masked/__pycache__/test_function.cpython-312.pyc,, +pandas/tests/arrays/masked/__pycache__/test_indexing.cpython-312.pyc,, +pandas/tests/arrays/masked/test_arithmetic.py,sha256=wchNK8BesRBPSclagK_egl_EG9J4KPCquzL9iRZOK20,8175 +pandas/tests/arrays/masked/test_arrow_compat.py,sha256=0uJbBERGPJs4G_BweVktYjW2Z82LD48zdx7rHGQybfM,7193 +pandas/tests/arrays/masked/test_function.py,sha256=qkFCkI5KNijaX2SurVoilnhtBFbismLBS4SyEybNXZ8,1954 +pandas/tests/arrays/masked/test_indexing.py,sha256=xjr8EECp7WStcIeEY8YNhmkZ90Q2o-l3izolkLpG2W0,1916 +pandas/tests/arrays/masked_shared.py,sha256=ANp_CU9Hcly9-NBxknm7g-uWxljstTmriq3S8f5kPsM,5194 +pandas/tests/arrays/numpy_/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/arrays/numpy_/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/arrays/numpy_/__pycache__/test_indexing.cpython-312.pyc,, +pandas/tests/arrays/numpy_/__pycache__/test_numpy.cpython-312.pyc,, +pandas/tests/arrays/numpy_/test_indexing.py,sha256=-0lB-Mw-gzM4Mpe-SRCj-w4C6QxLfp3BH65U_DVULNY,1452 +pandas/tests/arrays/numpy_/test_numpy.py,sha256=N4s8S8Kp8YwUZgtza6wUB5RnI_5WYaXMAFQxvEMOXKo,8764 +pandas/tests/arrays/period/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/arrays/period/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/arrays/period/__pycache__/test_arrow_compat.cpython-312.pyc,, +pandas/tests/arrays/period/__pycache__/test_astype.cpython-312.pyc,, +pandas/tests/arrays/period/__pycache__/test_constructors.cpython-312.pyc,, +pandas/tests/arrays/period/__pycache__/test_reductions.cpython-312.pyc,, +pandas/tests/arrays/period/test_arrow_compat.py,sha256=YuEM6oIOfRhdFaTFs5X0um9nLqygEkuxIZGl9V-qQcg,3709 +pandas/tests/arrays/period/test_astype.py,sha256=lKLDDqZSdU7s6PyHbrywkaCJnMJ4TKSphRqmno7BcbU,2344 +pandas/tests/arrays/period/test_constructors.py,sha256=C6J0nmKRSK5nyEja7-gZgf5tCZpPA0aZ9lux-z6gHxA,5089 +pandas/tests/arrays/period/test_reductions.py,sha256=gYiheQK3Z0Bwdo-0UaHIyfXGpmL1_UvoMP9FVIpztlM,1050 +pandas/tests/arrays/sparse/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/arrays/sparse/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/arrays/sparse/__pycache__/test_accessor.cpython-312.pyc,, +pandas/tests/arrays/sparse/__pycache__/test_arithmetics.cpython-312.pyc,, +pandas/tests/arrays/sparse/__pycache__/test_array.cpython-312.pyc,, +pandas/tests/arrays/sparse/__pycache__/test_astype.cpython-312.pyc,, +pandas/tests/arrays/sparse/__pycache__/test_combine_concat.cpython-312.pyc,, +pandas/tests/arrays/sparse/__pycache__/test_constructors.cpython-312.pyc,, +pandas/tests/arrays/sparse/__pycache__/test_dtype.cpython-312.pyc,, +pandas/tests/arrays/sparse/__pycache__/test_indexing.cpython-312.pyc,, +pandas/tests/arrays/sparse/__pycache__/test_libsparse.cpython-312.pyc,, +pandas/tests/arrays/sparse/__pycache__/test_reductions.cpython-312.pyc,, +pandas/tests/arrays/sparse/__pycache__/test_unary.cpython-312.pyc,, +pandas/tests/arrays/sparse/test_accessor.py,sha256=EReITkC1ib-_36L6gS5UfjWai_Brp8Iaf4w7WObJZjM,9025 +pandas/tests/arrays/sparse/test_arithmetics.py,sha256=TC2Af6gA4OkRIxDTWy_5jmHNIrgsqWGmOVF707wOn8M,20152 +pandas/tests/arrays/sparse/test_array.py,sha256=HbW0y7KLlWPz3QI6gtE44ZRZF5vS8ZwjM3IjOQfNNSQ,16794 +pandas/tests/arrays/sparse/test_astype.py,sha256=JwcFBWzfg2KOv9_6GsP0oV4WWDmFugT8dHrXDWCLZwM,4763 +pandas/tests/arrays/sparse/test_combine_concat.py,sha256=3NMQXaRQc7Bxn5HhSHffcUE24GZi_VYflnFLnixOgbs,2651 +pandas/tests/arrays/sparse/test_constructors.py,sha256=N5GJ8SrwVZ4hNGaM_QlALl283EM13nSVbtO8uBRSAwY,10835 +pandas/tests/arrays/sparse/test_dtype.py,sha256=xcZIrh0SPqvPzMt9EbMF04ADSu5Xueemvl81llkjq64,6122 +pandas/tests/arrays/sparse/test_indexing.py,sha256=8INC1paA06XrCp8L63FSllr0OK48pgiKda5sOgrUhf8,10425 +pandas/tests/arrays/sparse/test_libsparse.py,sha256=_hfr36t-jm-QOhI9Gwbd6sQZI5aVWMMixHY-OYOqKuM,19293 +pandas/tests/arrays/sparse/test_reductions.py,sha256=D7R_jhlFtmH8l-tERmhtP1K3KbcAyPuyIy_Y_gVcN6Q,9721 +pandas/tests/arrays/sparse/test_unary.py,sha256=GtqeMdylKdtu-0HPxmTDVjo32riOcEtqPhjI_XK5LkM,2864 +pandas/tests/arrays/string_/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/arrays/string_/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/arrays/string_/__pycache__/test_string.cpython-312.pyc,, +pandas/tests/arrays/string_/__pycache__/test_string_arrow.cpython-312.pyc,, +pandas/tests/arrays/string_/test_string.py,sha256=2Jb7cC3pDkGA0nhEwsIgW0tNJ4meEy8htFwX3zO9Flc,23988 +pandas/tests/arrays/string_/test_string_arrow.py,sha256=JbP4dLrozNXsjwzqiiTXzbrPo7CVGYFl9nsmAEPSkFg,9140 +pandas/tests/arrays/test_array.py,sha256=xFDZAd6Lls_wI782aoOOFJGPA-XEiyE5JvftU03RRkg,15663 +pandas/tests/arrays/test_datetimelike.py,sha256=SpCxw8hR_Ocbm5tNrUcGPTTIduEGdSJPxRkyEtKZLkI,45423 +pandas/tests/arrays/test_datetimes.py,sha256=FoODE0J_-8KIBbNS5ROkEWVgNnF3PwaToqJ38YtiAYU,29112 +pandas/tests/arrays/test_ndarray_backed.py,sha256=6unFuF9S6hG5FDJDjiqbKg3rL8ItzJQHwY9vMdju4-0,2331 +pandas/tests/arrays/test_period.py,sha256=S_7TMRLEmVamhGKlVO50qJIj3OFDWRzY_oxEcXzp3zs,5572 +pandas/tests/arrays/test_timedeltas.py,sha256=VdMdnCrOL5_oUa4RxL-gaVre6Qp3iu__qNMaUb7kqfE,10673 +pandas/tests/arrays/timedeltas/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/arrays/timedeltas/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/arrays/timedeltas/__pycache__/test_constructors.cpython-312.pyc,, +pandas/tests/arrays/timedeltas/__pycache__/test_cumulative.cpython-312.pyc,, +pandas/tests/arrays/timedeltas/__pycache__/test_reductions.cpython-312.pyc,, +pandas/tests/arrays/timedeltas/test_constructors.py,sha256=gwBy_iuOc-EEMusjK2bITGQhCyeeI9OzI9uI8xOact0,4248 +pandas/tests/arrays/timedeltas/test_cumulative.py,sha256=cRR6I-lIsefG95vEZb8TuXdvmw7pdPFedpBneLVKBG8,692 +pandas/tests/arrays/timedeltas/test_reductions.py,sha256=cw6I3Bxi0R2_DD2y1WD-AHTYR_ufAtN9ztCtDGypQnM,6520 +pandas/tests/base/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/base/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/base/__pycache__/common.cpython-312.pyc,, +pandas/tests/base/__pycache__/test_constructors.cpython-312.pyc,, +pandas/tests/base/__pycache__/test_conversion.cpython-312.pyc,, +pandas/tests/base/__pycache__/test_fillna.cpython-312.pyc,, +pandas/tests/base/__pycache__/test_misc.cpython-312.pyc,, +pandas/tests/base/__pycache__/test_transpose.cpython-312.pyc,, +pandas/tests/base/__pycache__/test_unique.cpython-312.pyc,, +pandas/tests/base/__pycache__/test_value_counts.cpython-312.pyc,, +pandas/tests/base/common.py,sha256=-cLXvhzuQi0XMfU-NdqTQAiruN0MU9A9HE2goo7ZzJQ,266 +pandas/tests/base/test_constructors.py,sha256=mFPWRfNgWYQyYqhYKErJ-obd1hVWfm50aMFH5wgHkU0,5309 +pandas/tests/base/test_conversion.py,sha256=fm58V7TCp45uXmJAQRyF7yz3e6ydG6JVCQ2oanNb7xY,17685 +pandas/tests/base/test_fillna.py,sha256=q9LZhUp2HXaVQw4wSxK0VU4Z9z62WI12r9ivsZu0gOg,1522 +pandas/tests/base/test_misc.py,sha256=FwzkBajbi3JLRuzaapLTrRI803DqKgME68WWo1jhhjc,6040 +pandas/tests/base/test_transpose.py,sha256=138_O_JwwdCmfmyjp47PSVa-4Sr7SOuLprr0PzRm6BQ,1694 +pandas/tests/base/test_unique.py,sha256=tMDzvNfhUYXRl2IOYvlHo0cuFbLrE-oR1bPJc0hFAio,4370 +pandas/tests/base/test_value_counts.py,sha256=e-OG-UOKyEIxOim4TXa4rGbaOzeb8l6XemkruFfDyw4,11778 +pandas/tests/computation/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/computation/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/computation/__pycache__/test_compat.cpython-312.pyc,, +pandas/tests/computation/__pycache__/test_eval.cpython-312.pyc,, +pandas/tests/computation/test_compat.py,sha256=dHstyvdaXybrwm1WQndV9aQBwOsOvCIVZb5pxLXsYfM,872 +pandas/tests/computation/test_eval.py,sha256=tAJDNLwur6BJUEqUzOce3Gr2UcrpJWFHE7dRDi4Hfb4,71739 +pandas/tests/config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/config/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/config/__pycache__/test_config.cpython-312.pyc,, +pandas/tests/config/__pycache__/test_localization.cpython-312.pyc,, +pandas/tests/config/test_config.py,sha256=T3PKV_lWTp_4ZU566fpWt_N9_tr3BfsxHlJ_vqnQiiQ,15858 +pandas/tests/config/test_localization.py,sha256=xC7SJfih_Kus5WGpSWZdwyAQR3ttgpsxxlNesbwrYfM,4479 +pandas/tests/construction/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/construction/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/construction/__pycache__/test_extract_array.cpython-312.pyc,, +pandas/tests/construction/test_extract_array.py,sha256=L3fEjATPsAy3a6zrdQJaXXaQ7FvR2LOeiPJMjGNkwKQ,637 +pandas/tests/copy_view/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/copy_view/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/copy_view/__pycache__/test_array.cpython-312.pyc,, +pandas/tests/copy_view/__pycache__/test_astype.cpython-312.pyc,, +pandas/tests/copy_view/__pycache__/test_chained_assignment_deprecation.cpython-312.pyc,, +pandas/tests/copy_view/__pycache__/test_clip.cpython-312.pyc,, +pandas/tests/copy_view/__pycache__/test_constructors.cpython-312.pyc,, +pandas/tests/copy_view/__pycache__/test_core_functionalities.cpython-312.pyc,, +pandas/tests/copy_view/__pycache__/test_functions.cpython-312.pyc,, +pandas/tests/copy_view/__pycache__/test_indexing.cpython-312.pyc,, +pandas/tests/copy_view/__pycache__/test_internals.cpython-312.pyc,, +pandas/tests/copy_view/__pycache__/test_interp_fillna.cpython-312.pyc,, +pandas/tests/copy_view/__pycache__/test_methods.cpython-312.pyc,, +pandas/tests/copy_view/__pycache__/test_replace.cpython-312.pyc,, +pandas/tests/copy_view/__pycache__/test_setitem.cpython-312.pyc,, +pandas/tests/copy_view/__pycache__/test_util.cpython-312.pyc,, +pandas/tests/copy_view/__pycache__/util.cpython-312.pyc,, +pandas/tests/copy_view/index/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/copy_view/index/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/copy_view/index/__pycache__/test_datetimeindex.cpython-312.pyc,, +pandas/tests/copy_view/index/__pycache__/test_index.cpython-312.pyc,, +pandas/tests/copy_view/index/__pycache__/test_periodindex.cpython-312.pyc,, +pandas/tests/copy_view/index/__pycache__/test_timedeltaindex.cpython-312.pyc,, +pandas/tests/copy_view/index/test_datetimeindex.py,sha256=Sl224XCNK_lx-N6k9heXS_g2_bwmqCJJyKDv7pE_HQw,1980 +pandas/tests/copy_view/index/test_index.py,sha256=B849E4vf72tsWv11NfixJU6vjX0gpMlyvHRKSBk0V1Q,5363 +pandas/tests/copy_view/index/test_periodindex.py,sha256=qSR4PUuAHEPq1o8NUeif_MSrN43rvSeWQtsmTK6I1a4,653 +pandas/tests/copy_view/index/test_timedeltaindex.py,sha256=L1fGDsy2dmZqf_y3bXVo9mUMr1Jsli9BdScChOEQkns,661 +pandas/tests/copy_view/test_array.py,sha256=t4Tk1_-bwXOpuE80MqCkVsEsb753CPq6A87ZCI3WJBo,5840 +pandas/tests/copy_view/test_astype.py,sha256=SHB7qM1GIjguoiqzO9tPosiPVG7cftVbIzlau9lgXW0,8935 +pandas/tests/copy_view/test_chained_assignment_deprecation.py,sha256=BJqJ30DdsTUeoUZZm2kZKFOwUoz9Rkmg5AH3R6nk0F4,5750 +pandas/tests/copy_view/test_clip.py,sha256=ahKf7EUwJeYahLnPVhUuNanG4Va53Ez5kULzCdzeX60,3077 +pandas/tests/copy_view/test_constructors.py,sha256=JMWj_yBB7tNSTkUxdbhtzTVyJ03jho9imeKMXZaTb38,13950 +pandas/tests/copy_view/test_core_functionalities.py,sha256=M-ExonPcx6W-8z_TLTaP16DJtelSVeQHZKO1aWObSuA,3506 +pandas/tests/copy_view/test_functions.py,sha256=FZP92GSOEUNCVogDxngdGS2eodNwhw7w7Xs6jQgZGyg,15505 +pandas/tests/copy_view/test_indexing.py,sha256=4OUGrcgMHlai3p7tQt0sXopNYTrGdEFSUaVf6S7ZzyI,42980 +pandas/tests/copy_view/test_internals.py,sha256=mBEJH08zBch3LBtSzU7wXqBKc01uH2GTzZgUx3otcC8,5020 +pandas/tests/copy_view/test_interp_fillna.py,sha256=ztjjLWcR07fHYSaaTrrSD6S5s6rrOvUt_2S1BE3tRlQ,15235 +pandas/tests/copy_view/test_methods.py,sha256=ka2yDAm6yXDQC5rpLyxLHYq80XIQGhXUt4RUURvYjSk,71109 +pandas/tests/copy_view/test_replace.py,sha256=5KVB1Xc1qTBOCrhTL0my-36NoDUDRozzZ5bV0oyJLVk,17120 +pandas/tests/copy_view/test_setitem.py,sha256=ewuJiYuD9VI2wuFZiDjGYVP7gnlP4H9uVFnjjelW55U,4822 +pandas/tests/copy_view/test_util.py,sha256=ClWLprMJhf6okUNu9AX6Ar9IXZgKkY0nNuDzHRO70Hk,385 +pandas/tests/copy_view/util.py,sha256=oNtCgxmTmkiM1DiUxjnzTeAxCj_7jjeewtby-3gdoo0,899 +pandas/tests/dtypes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/dtypes/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/dtypes/__pycache__/test_common.cpython-312.pyc,, +pandas/tests/dtypes/__pycache__/test_concat.cpython-312.pyc,, +pandas/tests/dtypes/__pycache__/test_dtypes.cpython-312.pyc,, +pandas/tests/dtypes/__pycache__/test_generic.cpython-312.pyc,, +pandas/tests/dtypes/__pycache__/test_inference.cpython-312.pyc,, +pandas/tests/dtypes/__pycache__/test_missing.cpython-312.pyc,, +pandas/tests/dtypes/cast/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/dtypes/cast/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/dtypes/cast/__pycache__/test_can_hold_element.cpython-312.pyc,, +pandas/tests/dtypes/cast/__pycache__/test_construct_from_scalar.cpython-312.pyc,, +pandas/tests/dtypes/cast/__pycache__/test_construct_ndarray.cpython-312.pyc,, +pandas/tests/dtypes/cast/__pycache__/test_construct_object_arr.cpython-312.pyc,, +pandas/tests/dtypes/cast/__pycache__/test_dict_compat.cpython-312.pyc,, +pandas/tests/dtypes/cast/__pycache__/test_downcast.cpython-312.pyc,, +pandas/tests/dtypes/cast/__pycache__/test_find_common_type.cpython-312.pyc,, +pandas/tests/dtypes/cast/__pycache__/test_infer_datetimelike.cpython-312.pyc,, +pandas/tests/dtypes/cast/__pycache__/test_infer_dtype.cpython-312.pyc,, +pandas/tests/dtypes/cast/__pycache__/test_maybe_box_native.cpython-312.pyc,, +pandas/tests/dtypes/cast/__pycache__/test_promote.cpython-312.pyc,, +pandas/tests/dtypes/cast/test_can_hold_element.py,sha256=2zASUgxB7l8ttG2fKjCpIjtt_TQ7j4NJ2L9xFzcyUPU,2408 +pandas/tests/dtypes/cast/test_construct_from_scalar.py,sha256=INdOiQ7MowXLr6ZReCiq0JykUeFvRWocxk3f-ilk9v0,1780 +pandas/tests/dtypes/cast/test_construct_ndarray.py,sha256=Z4tTuoWxUoXiMVq8sJx2PPGIyRoz1dzzRIC1w8npDKQ,1303 +pandas/tests/dtypes/cast/test_construct_object_arr.py,sha256=eOmUu4q0ihGTbYpCleoCnYtvwh1TBCEZQQjLeJaUMNA,717 +pandas/tests/dtypes/cast/test_dict_compat.py,sha256=qyn7kP5b14MywtqOUL5C-NOvjf2qK4PsXGpCvqmo-4E,476 +pandas/tests/dtypes/cast/test_downcast.py,sha256=CzuywDTWQ3xTi__4Nd36qgcx6mDs2tpYUsVztduVC9s,2778 +pandas/tests/dtypes/cast/test_find_common_type.py,sha256=c__GbgnRawwgqWut8g5Q928en8-_O3oTZEQVbqQ8MrE,5226 +pandas/tests/dtypes/cast/test_infer_datetimelike.py,sha256=6vor_eqEbMKcBLEkfayXzVzwwf5BZcCvQhFZuqhvyKU,603 +pandas/tests/dtypes/cast/test_infer_dtype.py,sha256=WCLts2TG3Zs4V69O2f_HYmuXEkSHPUXVTIuGpVvICuY,6001 +pandas/tests/dtypes/cast/test_maybe_box_native.py,sha256=uEkoLnSVi4kR8-c5FMhpEba7luZum3PeRIrxIdeGeM4,996 +pandas/tests/dtypes/cast/test_promote.py,sha256=B4dgs3EWIm8qKuoQMn6FNaGGf_qAm_EAm4l2X3cHDMM,20755 +pandas/tests/dtypes/test_common.py,sha256=8XCSOz1J9y9K9Dxe3c55YOy-ONRlvRc3CWF0EVaxEa8,26390 +pandas/tests/dtypes/test_concat.py,sha256=vlsumyKcJ7b8EdJKONU5txCA34zMaoKDvA0KmcuP8XU,1799 +pandas/tests/dtypes/test_dtypes.py,sha256=7GaJl1ZXzioL7mll-RdNie3eVoki91FogtyBYQdFRkQ,43847 +pandas/tests/dtypes/test_generic.py,sha256=TzUIinbvMdsyxH_y2VYQ2XCYLQXh005qij9LWWF9bDc,4842 +pandas/tests/dtypes/test_inference.py,sha256=uNEZEE9cgR2T3ZTe0pBld9rnbupyx5XilbQj_PmiB20,70781 +pandas/tests/dtypes/test_missing.py,sha256=_FPqIAM5yZbYSlcndWuaItNVkgs3ylKEPb-o63QRzEE,30750 +pandas/tests/extension/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/extension/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/extension/__pycache__/conftest.cpython-312.pyc,, +pandas/tests/extension/__pycache__/test_arrow.cpython-312.pyc,, +pandas/tests/extension/__pycache__/test_categorical.cpython-312.pyc,, +pandas/tests/extension/__pycache__/test_common.cpython-312.pyc,, +pandas/tests/extension/__pycache__/test_datetime.cpython-312.pyc,, +pandas/tests/extension/__pycache__/test_extension.cpython-312.pyc,, +pandas/tests/extension/__pycache__/test_interval.cpython-312.pyc,, +pandas/tests/extension/__pycache__/test_masked.cpython-312.pyc,, +pandas/tests/extension/__pycache__/test_numpy.cpython-312.pyc,, +pandas/tests/extension/__pycache__/test_period.cpython-312.pyc,, +pandas/tests/extension/__pycache__/test_sparse.cpython-312.pyc,, +pandas/tests/extension/__pycache__/test_string.cpython-312.pyc,, +pandas/tests/extension/array_with_attr/__init__.py,sha256=bXkwWSW6GRX8Xw221iMyaQOQVaWmyuRP3tGhvjXtiV8,149 +pandas/tests/extension/array_with_attr/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/extension/array_with_attr/__pycache__/array.cpython-312.pyc,, +pandas/tests/extension/array_with_attr/__pycache__/test_array_with_attr.cpython-312.pyc,, +pandas/tests/extension/array_with_attr/array.py,sha256=Vo6gYBpAJHAztlq8m3gH-9GqKUkxSOHg2fk6cApHgFE,2496 +pandas/tests/extension/array_with_attr/test_array_with_attr.py,sha256=TuuBA1lCxjVOgWsWM9jhgc-PyGuXzajO3UWWKZEquZA,1373 +pandas/tests/extension/base/__init__.py,sha256=5OjQDaQnbihqkwRdCBAV-eF-QRE8p3V4frJ764P5-jQ,4353 +pandas/tests/extension/base/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/extension/base/__pycache__/accumulate.cpython-312.pyc,, +pandas/tests/extension/base/__pycache__/base.cpython-312.pyc,, +pandas/tests/extension/base/__pycache__/casting.cpython-312.pyc,, +pandas/tests/extension/base/__pycache__/constructors.cpython-312.pyc,, +pandas/tests/extension/base/__pycache__/dim2.cpython-312.pyc,, +pandas/tests/extension/base/__pycache__/dtype.cpython-312.pyc,, +pandas/tests/extension/base/__pycache__/getitem.cpython-312.pyc,, +pandas/tests/extension/base/__pycache__/groupby.cpython-312.pyc,, +pandas/tests/extension/base/__pycache__/index.cpython-312.pyc,, +pandas/tests/extension/base/__pycache__/interface.cpython-312.pyc,, +pandas/tests/extension/base/__pycache__/io.cpython-312.pyc,, +pandas/tests/extension/base/__pycache__/methods.cpython-312.pyc,, +pandas/tests/extension/base/__pycache__/missing.cpython-312.pyc,, +pandas/tests/extension/base/__pycache__/ops.cpython-312.pyc,, +pandas/tests/extension/base/__pycache__/printing.cpython-312.pyc,, +pandas/tests/extension/base/__pycache__/reduce.cpython-312.pyc,, +pandas/tests/extension/base/__pycache__/reshaping.cpython-312.pyc,, +pandas/tests/extension/base/__pycache__/setitem.cpython-312.pyc,, +pandas/tests/extension/base/accumulate.py,sha256=66bubZOQfkzzpcca1jz2WVky4mxi4uGyq8TsQpV288k,1411 +pandas/tests/extension/base/base.py,sha256=aSfTPvuvzzQUxEIrGUASWuwcVv6Uw5bvkFXvqjhRV1M,35 +pandas/tests/extension/base/casting.py,sha256=KWGZGeC1Kh2mDXUto7Xap6lkSja8661Qi1g58HgFpSM,3077 +pandas/tests/extension/base/constructors.py,sha256=Y2Pny2SrEj7jsCEUN6KRKi_9G2HA7RIfVs5GVf9Nz5w,5609 +pandas/tests/extension/base/dim2.py,sha256=8Ni4nnBW5wxH3e6f0kX1yTDjecmd12sAZdkBt-1tTss,11992 +pandas/tests/extension/base/dtype.py,sha256=4v3RO3H-2xDIPujcTYdjb0AzWpctqALOXUHLHyHBLDg,4006 +pandas/tests/extension/base/getitem.py,sha256=leq9dxp_KexAv7mhexLCWXcIMKNBPOVfhFv6Nuc5PkQ,15673 +pandas/tests/extension/base/groupby.py,sha256=5A_X0G3x1MD13QXpX-v0nYABeU9TRINcvOwVhd3JBpQ,6465 +pandas/tests/extension/base/index.py,sha256=fD5Jugbt_39nZ1eVjPNdAgoDRuNXTcnZB9lA4w687vM,517 +pandas/tests/extension/base/interface.py,sha256=rdJUhxcnMwnHUoGzhj0_89ik5JETiTz0kjDmepTU5lU,4699 +pandas/tests/extension/base/io.py,sha256=SNvCa6LXo-4V92Bm6A1RZPXwfDdu3hTWLje8_D3Xwo8,1475 +pandas/tests/extension/base/methods.py,sha256=xQvGXCoxo-_1A-fonAgDbB7GyFeg22RAxbjxTeL2lnM,26723 +pandas/tests/extension/base/missing.py,sha256=D4by9EHLsc32icNeDutH7JdoGyHE8pD0XPM2o7FiGQU,6606 +pandas/tests/extension/base/ops.py,sha256=EmsLXfCMbJf4RAru_ewAhc_Epd-ZAROKygwjBf7EzYg,11058 +pandas/tests/extension/base/printing.py,sha256=pVwGn1id_vO_b9nrz3M9Q_Qh9vqDqC0eZHom0_oGr-A,1109 +pandas/tests/extension/base/reduce.py,sha256=IaF6nI-fMTYzG4fNVUoPei_lf9vCHHIf0NnKCssnYlk,5968 +pandas/tests/extension/base/reshaping.py,sha256=Hf8czQWubrTjZrkYTL3FdOh6h97pCQaN5fK49GbRyRA,13931 +pandas/tests/extension/base/setitem.py,sha256=VcSUUuSqnLftzeeaIlBJIeoo841vVenX_FL5JceS91g,15075 +pandas/tests/extension/conftest.py,sha256=nvR8zq82gsIqh5rbOWj7_sOYLgL8J3M0loXw_L-OGag,5061 +pandas/tests/extension/date/__init__.py,sha256=-pIaBe_vmgnM_ok6T_-t-wVHetXtNw30SOMWVWNDqLI,118 +pandas/tests/extension/date/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/extension/date/__pycache__/array.cpython-312.pyc,, +pandas/tests/extension/date/array.py,sha256=da7NoKcUFxS78IIEAsY6kXzL-mOCrV0yyhFWQUN6p8k,5971 +pandas/tests/extension/decimal/__init__.py,sha256=wgvjyfS3v3AHfh3sEfb5C8rSuOyo2satof8ESijM7bw,191 +pandas/tests/extension/decimal/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/extension/decimal/__pycache__/array.cpython-312.pyc,, +pandas/tests/extension/decimal/__pycache__/test_decimal.cpython-312.pyc,, +pandas/tests/extension/decimal/array.py,sha256=8YbmByqfIzEXW9i3-Ct6VM6M0QkmEEB9CQp79udfmYw,9694 +pandas/tests/extension/decimal/test_decimal.py,sha256=82ggMNpjkSYDu4Tk3vmS0zTwn8AZ3VqCi-MkQTP2paA,19459 +pandas/tests/extension/json/__init__.py,sha256=JvjCnVMfzIUSoHKL-umrkT9H5T8J3Alt8-QoKXMSB4I,146 +pandas/tests/extension/json/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/extension/json/__pycache__/array.cpython-312.pyc,, +pandas/tests/extension/json/__pycache__/test_json.cpython-312.pyc,, +pandas/tests/extension/json/array.py,sha256=Lt-hgallWZaJiaDjBbkE7ztKDM9S8FFj23GzxrxxIkY,8335 +pandas/tests/extension/json/test_json.py,sha256=usY52SN9Yd8lUugiCxI1B7DB06l2Lc8mr9tbxu9iOgI,17951 +pandas/tests/extension/list/__init__.py,sha256=FlpTrgdAMl_5puN2zDjvdmosw8aTvaCD-Hi2GtIK-k0,146 +pandas/tests/extension/list/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/extension/list/__pycache__/array.cpython-312.pyc,, +pandas/tests/extension/list/__pycache__/test_list.cpython-312.pyc,, +pandas/tests/extension/list/array.py,sha256=ngSHFQPRfmOkDOo54sX-l5JjQvr7ZTE9OzS9aPicc3o,4001 +pandas/tests/extension/list/test_list.py,sha256=VFPo5wGu-UvtAOFx3hoxILmRdI9kTOxCIIJM4fqgRBk,671 +pandas/tests/extension/test_arrow.py,sha256=Fil3KeJKWxmy9_Yb7Tkwl5uiK-3EwaSnptfpwpfCfm8,116199 +pandas/tests/extension/test_categorical.py,sha256=DhFKjDxrDfg4q6LXIgdIGVnv7VIK3IxUHsQ0rK_nQfE,6828 +pandas/tests/extension/test_common.py,sha256=4LO2slr0E0zODDK_Es4g9bPBH1U77nI8x9O1Mdddn1U,2975 +pandas/tests/extension/test_datetime.py,sha256=eBTSFWcQp2M1TgYzr01F-KQrdCJLHPrcPMGvuCsIj1s,4614 +pandas/tests/extension/test_extension.py,sha256=eyLZa4imT1Qdd7PCbDX9l0EtDu39T80eCrSre2wmTuE,559 +pandas/tests/extension/test_interval.py,sha256=TFLuAsCeXdkWLPfyYY2v4IdhvI7plwcaatL8LJl9kGI,2711 +pandas/tests/extension/test_masked.py,sha256=jrBlSzzwlXMAYj3fYXzDhiOKwUW7WBzyHLp-ce4VDf8,14338 +pandas/tests/extension/test_numpy.py,sha256=eFM6D2CiLgrsmwN5KQm_kYrzIdG7lmFXUuUiNoFrelE,15586 +pandas/tests/extension/test_period.py,sha256=e3RIO2xBPhF-PxPZtPM8VkVhkjYdUNtch9vcoRpHuEE,3528 +pandas/tests/extension/test_sparse.py,sha256=HIUEftSLmtr-LV7xrkP99vKwNj2zyXv4z1Ij_LWJd7Q,18011 +pandas/tests/extension/test_string.py,sha256=v3DaptVQ4lBckrCg_nLfWlJNBRHVaDjc8fE1bIlN4rU,8165 +pandas/tests/frame/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/frame/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/frame/__pycache__/common.cpython-312.pyc,, +pandas/tests/frame/__pycache__/conftest.cpython-312.pyc,, +pandas/tests/frame/__pycache__/test_alter_axes.cpython-312.pyc,, +pandas/tests/frame/__pycache__/test_api.cpython-312.pyc,, +pandas/tests/frame/__pycache__/test_arithmetic.cpython-312.pyc,, +pandas/tests/frame/__pycache__/test_arrow_interface.cpython-312.pyc,, +pandas/tests/frame/__pycache__/test_block_internals.cpython-312.pyc,, +pandas/tests/frame/__pycache__/test_constructors.cpython-312.pyc,, +pandas/tests/frame/__pycache__/test_cumulative.cpython-312.pyc,, +pandas/tests/frame/__pycache__/test_iteration.cpython-312.pyc,, +pandas/tests/frame/__pycache__/test_logical_ops.cpython-312.pyc,, +pandas/tests/frame/__pycache__/test_nonunique_indexes.cpython-312.pyc,, +pandas/tests/frame/__pycache__/test_npfuncs.cpython-312.pyc,, +pandas/tests/frame/__pycache__/test_query_eval.cpython-312.pyc,, +pandas/tests/frame/__pycache__/test_reductions.cpython-312.pyc,, +pandas/tests/frame/__pycache__/test_repr.cpython-312.pyc,, +pandas/tests/frame/__pycache__/test_stack_unstack.cpython-312.pyc,, +pandas/tests/frame/__pycache__/test_subclass.cpython-312.pyc,, +pandas/tests/frame/__pycache__/test_ufunc.cpython-312.pyc,, +pandas/tests/frame/__pycache__/test_unary.cpython-312.pyc,, +pandas/tests/frame/__pycache__/test_validate.cpython-312.pyc,, +pandas/tests/frame/common.py,sha256=BmnEMlREF7G0B5zdaJRsdzqIRdh8diiTisBbCVI6Fp0,1873 +pandas/tests/frame/conftest.py,sha256=q2Oh2Ej-YIJvDdhsPCNDGvtTr5XWPRKZ2sylqmv5dys,2644 +pandas/tests/frame/constructors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/frame/constructors/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/frame/constructors/__pycache__/test_from_dict.cpython-312.pyc,, +pandas/tests/frame/constructors/__pycache__/test_from_records.cpython-312.pyc,, +pandas/tests/frame/constructors/test_from_dict.py,sha256=CTTFXUB5bamlx91XWQnmmG3DIlY8v6Qnc1ycZvjClT8,8152 +pandas/tests/frame/constructors/test_from_records.py,sha256=O6NwCZK5wa9w9a8Om6LHA2kWSLfTerakkjrgYXGJIao,18601 +pandas/tests/frame/indexing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/frame/indexing/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/frame/indexing/__pycache__/test_coercion.cpython-312.pyc,, +pandas/tests/frame/indexing/__pycache__/test_delitem.cpython-312.pyc,, +pandas/tests/frame/indexing/__pycache__/test_get.cpython-312.pyc,, +pandas/tests/frame/indexing/__pycache__/test_get_value.cpython-312.pyc,, +pandas/tests/frame/indexing/__pycache__/test_getitem.cpython-312.pyc,, +pandas/tests/frame/indexing/__pycache__/test_indexing.cpython-312.pyc,, +pandas/tests/frame/indexing/__pycache__/test_insert.cpython-312.pyc,, +pandas/tests/frame/indexing/__pycache__/test_mask.cpython-312.pyc,, +pandas/tests/frame/indexing/__pycache__/test_set_value.cpython-312.pyc,, +pandas/tests/frame/indexing/__pycache__/test_setitem.cpython-312.pyc,, +pandas/tests/frame/indexing/__pycache__/test_take.cpython-312.pyc,, +pandas/tests/frame/indexing/__pycache__/test_where.cpython-312.pyc,, +pandas/tests/frame/indexing/__pycache__/test_xs.cpython-312.pyc,, +pandas/tests/frame/indexing/test_coercion.py,sha256=rHCkOLIlUkukh-P0XzPMtD4B8Lha3i1hqdvvZwCIAm8,5991 +pandas/tests/frame/indexing/test_delitem.py,sha256=-YERBfZbhTZ3eKzjmWln8AjoQEO7Yvae6elau4njhM0,1832 +pandas/tests/frame/indexing/test_get.py,sha256=N00_igU25_HjYuvAqDQKqBpqbz6HjB97o9Exvbo9BzM,662 +pandas/tests/frame/indexing/test_get_value.py,sha256=A-GbCHlbDfVPGB10dNGnGg4DtrKrlRbRspYfuDTUmPM,679 +pandas/tests/frame/indexing/test_getitem.py,sha256=9xogr1RzStjgP4HvWm_tm9VWUol660FgSmBwN-wC5Tw,15002 +pandas/tests/frame/indexing/test_indexing.py,sha256=XIcq7eJfuJgAsI1ZgZ-Eojw32fSBzhrQqQsb4aMycEk,70208 +pandas/tests/frame/indexing/test_insert.py,sha256=0XsNprKi0XQ9od6dOImwzQwh8YMdgdE0BZFGFHGPEYg,4074 +pandas/tests/frame/indexing/test_mask.py,sha256=1Bql-TBfyBDmlXkECYXk-ZH_y4SPSOZYjCR2Ex7Km1k,4862 +pandas/tests/frame/indexing/test_set_value.py,sha256=q0Bzs0u_q5G6VzFvU5mRSxohG5FTh4sw7sRrRdhY0YM,2622 +pandas/tests/frame/indexing/test_setitem.py,sha256=z5mPGNnxv5nNlpPJiDeXbtdURAQeXIeddsKzPd-3OWE,51434 +pandas/tests/frame/indexing/test_take.py,sha256=SMBM5BO7ybxTq8gTAX1Qg1UW8vcNiRrHTQwrt1f-Rig,3230 +pandas/tests/frame/indexing/test_where.py,sha256=Y3oOgYjYJxUAHn_PiW2eWHrz1tWNVuNXtJcf3RJE2PY,38125 +pandas/tests/frame/indexing/test_xs.py,sha256=86w_A-gePZXZETqs9UYKfEZrNKXyvmd0DwScTbHH9Dg,15980 +pandas/tests/frame/methods/__init__.py,sha256=M6dCS5d750Fzf9GX7xyNka-SZ2wJFCL66y5j-moHhwo,229 +pandas/tests/frame/methods/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_add_prefix_suffix.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_align.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_asfreq.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_asof.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_assign.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_astype.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_at_time.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_between_time.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_clip.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_combine.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_combine_first.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_compare.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_convert_dtypes.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_copy.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_count.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_cov_corr.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_describe.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_diff.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_dot.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_drop.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_drop_duplicates.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_droplevel.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_dropna.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_dtypes.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_duplicated.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_equals.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_explode.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_fillna.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_filter.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_first_and_last.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_first_valid_index.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_get_numeric_data.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_head_tail.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_infer_objects.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_info.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_interpolate.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_is_homogeneous_dtype.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_isetitem.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_isin.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_iterrows.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_join.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_map.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_matmul.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_nlargest.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_pct_change.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_pipe.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_pop.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_quantile.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_rank.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_reindex.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_reindex_like.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_rename.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_rename_axis.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_reorder_levels.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_replace.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_reset_index.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_round.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_sample.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_select_dtypes.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_set_axis.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_set_index.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_shift.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_size.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_sort_index.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_sort_values.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_swapaxes.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_swaplevel.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_to_csv.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_to_dict.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_to_dict_of_blocks.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_to_numpy.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_to_period.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_to_records.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_to_timestamp.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_transpose.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_truncate.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_tz_convert.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_tz_localize.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_update.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_value_counts.cpython-312.pyc,, +pandas/tests/frame/methods/__pycache__/test_values.cpython-312.pyc,, +pandas/tests/frame/methods/test_add_prefix_suffix.py,sha256=iPfzSPx0CArx79na7xcI9ZcPTAwq73IdOCcREVO7k4E,1910 +pandas/tests/frame/methods/test_align.py,sha256=FwQrqdCesXbgkQ8bfYPlf3LfK-Sdvud9pHEC2tCnwQ0,17941 +pandas/tests/frame/methods/test_asfreq.py,sha256=MCJkjukZtOVCauc4FZDbor1h99AvG4eMNfQZW8L1h5c,9341 +pandas/tests/frame/methods/test_asof.py,sha256=bkK2i5xcGvz2oy1MVbf_C1oVixMy_1qYqYcuOg-K2Bk,6732 +pandas/tests/frame/methods/test_assign.py,sha256=xFGREzLhP1wj3MowBimeYbMWBNiII0280DiOXI6WDB0,2982 +pandas/tests/frame/methods/test_astype.py,sha256=lIFj0WqQvEZVESqYOfP8flVquMoVEYp1ubJGYaCZJgQ,32102 +pandas/tests/frame/methods/test_at_time.py,sha256=JrQYFlNIIyW1xDvgmGE7zRfjXnmKMELh9Stiw0btGbM,4708 +pandas/tests/frame/methods/test_between_time.py,sha256=rD-k1a4LVOa-nMlLXOaZO7iTa3hL_C9tghqt8DWW0Qs,8083 +pandas/tests/frame/methods/test_clip.py,sha256=6h1zwE0SKP-uknyuE5Pi5X9vTS4L5ZBts_iSbs6cSL8,7554 +pandas/tests/frame/methods/test_combine.py,sha256=wNaQqokqHsJmrZ9NQIao58ZT0hSkkTH14I7_Oq8tADs,1359 +pandas/tests/frame/methods/test_combine_first.py,sha256=K0YQAGhGyaK_j5tmP9IbQx8zO56ID9GhbTaT9v-3T1M,19726 +pandas/tests/frame/methods/test_compare.py,sha256=j7Z_-yBVts4-xl1fVsJtOBAXYbLao2hwzI2x3aniFz0,9615 +pandas/tests/frame/methods/test_convert_dtypes.py,sha256=sLJ-7LM95vWBzEUFKuWfpj9j5TmX5339pRXZW3dFXCw,7958 +pandas/tests/frame/methods/test_copy.py,sha256=QeDoh44tS__y9LK7LwUBAc-SD5RS-phPA4eYWPl5yIg,1873 +pandas/tests/frame/methods/test_count.py,sha256=avzIu1dZ3pls4SM6g173M7Q4i8zMUzeAVI2EeIzWC0c,1083 +pandas/tests/frame/methods/test_cov_corr.py,sha256=ydpNMfWcjDf6zNVvLGFc8eOHWs_scU6rvMobn3EPm2U,17873 +pandas/tests/frame/methods/test_describe.py,sha256=DAY04ar1XixwEscl6taSddki4Y_rYnQnV8zF61-z1ZY,14500 +pandas/tests/frame/methods/test_diff.py,sha256=Dyz4lYFWrLVm5fN_B0Z1xZ_l8gyGFQhzwhmRKMuA6io,10099 +pandas/tests/frame/methods/test_dot.py,sha256=tfZD1HWlbO78DEgdjpBctgjWHtzjC3K9essVl_5XBMA,4623 +pandas/tests/frame/methods/test_drop.py,sha256=41RTmD-suQbCnZjpFcG56VlIx1ZP-ReC-j5YIhpJ3WA,20362 +pandas/tests/frame/methods/test_drop_duplicates.py,sha256=GSJ7VundpGtt6KBhl2mld6CwNc9La_pGRwXuNNiRE9Y,14503 +pandas/tests/frame/methods/test_droplevel.py,sha256=L1gAMjYYPB6eYmSppXfbwPVKa3HCNofqPVUZ3gxLldA,1253 +pandas/tests/frame/methods/test_dropna.py,sha256=9l8GBOLpvmEowzFaq0kRxN3815gJCuNamX4S5dn5Mmw,10315 +pandas/tests/frame/methods/test_dtypes.py,sha256=YrKxnM9gY4UlcsXjLBLJRTHE_8CthS71mIMZH3ubPpg,5093 +pandas/tests/frame/methods/test_duplicated.py,sha256=1DQFuK4KjfSpsl8W0jXne8PPUsL1nFe3lI_9VYBd33I,3305 +pandas/tests/frame/methods/test_equals.py,sha256=AFmbc9SmfgpQV0PD9hCXuktRCRkNvDF5S1Z7z31E2xE,2996 +pandas/tests/frame/methods/test_explode.py,sha256=ZK-uow3VD8csy96x6hkDItUOh4U2kkYSwrdH83tkjAM,8824 +pandas/tests/frame/methods/test_fillna.py,sha256=GAPSWMAQ8MXdOVwj3ionMLvG8W_N9FolE6cSnU39uSM,34156 +pandas/tests/frame/methods/test_filter.py,sha256=oT63-WLaQv3isFsWJFtqZwxiw2J-7xZwyOOxpn-kTNo,5422 +pandas/tests/frame/methods/test_first_and_last.py,sha256=hKvLBnx3YtQLilE_9PlL9804dAI6E7Hk2gHDgXqbcsU,5349 +pandas/tests/frame/methods/test_first_valid_index.py,sha256=DRoZKic0mpCom31NeygnBftZlxc6wsCT4-DN2KV5wWI,2574 +pandas/tests/frame/methods/test_get_numeric_data.py,sha256=jXqHisuyym78GRZTo0c2uN1U4YPcMkXUJ9eDRZE8BPA,3313 +pandas/tests/frame/methods/test_head_tail.py,sha256=quuFkpS5IgonJDSb9_Po4eO3Wi5wlcNKq723EMYL6Ns,1935 +pandas/tests/frame/methods/test_infer_objects.py,sha256=LNOf2VJsV17FDT9ogEDba6la414yUmm5z_7B97nLN24,1241 +pandas/tests/frame/methods/test_info.py,sha256=gvIGMmte029dnuvDjFxuVs20VblEBuQof2-xjzSe6EI,16867 +pandas/tests/frame/methods/test_interpolate.py,sha256=8A7qxsIgVgdC9-P_WClkvIWbRObBd4aw2Hf78elyx4c,20120 +pandas/tests/frame/methods/test_is_homogeneous_dtype.py,sha256=8Ndf_2Z07SAqrN0ookvH0PDAmECGVJkUieeqSaz2aRQ,1455 +pandas/tests/frame/methods/test_isetitem.py,sha256=VoxA-yXow_CRikJ1tlni1PsAAOT1D2X8PtTZyJOGQXU,1428 +pandas/tests/frame/methods/test_isin.py,sha256=P2TVUsL_p366aSxwWcq27VlT9zFstOXlsJSTFlw2n20,7599 +pandas/tests/frame/methods/test_iterrows.py,sha256=hfFRA20tRYmXJAoJZLGI04J131Z7QaaEbINm3FwfVbQ,338 +pandas/tests/frame/methods/test_join.py,sha256=oGHrJh9Gb6k8Cgg1iHNVoJuamkIHqnzs5EoU_XdY9hM,17523 +pandas/tests/frame/methods/test_map.py,sha256=UIY-wd0ozerUNyILMavuJ47qdWwp8dREjeKeeR8zvc8,5994 +pandas/tests/frame/methods/test_matmul.py,sha256=i1BG41S9da2R0nATvc3kZXsiwl5t6MHDFIb0IJ4lAbQ,3137 +pandas/tests/frame/methods/test_nlargest.py,sha256=6G_UUSJT858jxia3p92pf4jivcg6yhj4xiXRZ7EUeW0,8195 +pandas/tests/frame/methods/test_pct_change.py,sha256=s0Ho617mHdRHBEV-9cRAz3_Z_Q5BzTd_cd6MuobTlbo,6530 +pandas/tests/frame/methods/test_pipe.py,sha256=ts5ghk8g6PYXKpdsBdovBXxPGO2qq75FEVzBgjAVfRw,1023 +pandas/tests/frame/methods/test_pop.py,sha256=e0CBRelgiASCGdB1NFRMSr04BbaggjyHAZYvmUUh1sM,2223 +pandas/tests/frame/methods/test_quantile.py,sha256=HK6wwPSW-yLLkxbj8Cn0C1nPho8WBsjPhcvGtiDCaPM,36280 +pandas/tests/frame/methods/test_rank.py,sha256=SnZTqSgarPjHAFSnyLCSZevOBMyXNb13QAAI-qz0Z1c,17566 +pandas/tests/frame/methods/test_reindex.py,sha256=tmNvHk4dcGnrZ81EA5UGtPq6LdSa0Y64yQ5MzIZoKP8,48343 +pandas/tests/frame/methods/test_reindex_like.py,sha256=2qgqaHDSEKYO1hwE9MaPTFJhl4m7rejHyuOcrmvqaBg,1187 +pandas/tests/frame/methods/test_rename.py,sha256=P-SIwbh-n6QdPqFns4ebPtGFwdXd7vmeWt5_dwo0Kq4,15354 +pandas/tests/frame/methods/test_rename_axis.py,sha256=90QFtDi0p-8bxEdFfLs75EtJQtJEOTmCdXoiS7h9F-Y,4091 +pandas/tests/frame/methods/test_reorder_levels.py,sha256=VJVEdltyRoz89mQR1Xp0A9yKlTeEFIpsPaKWQujT-C8,2729 +pandas/tests/frame/methods/test_replace.py,sha256=IW0My1nADFCpbkjMFLWfXT36Nxbcs9m7FSiksSHM4jc,64846 +pandas/tests/frame/methods/test_reset_index.py,sha256=yo9nZBpcOblU-3bgfmTg3-CQT7p-p3mciEroMVDfmDE,27931 +pandas/tests/frame/methods/test_round.py,sha256=dcPlBxHqpKJ6JTBJskvw2CE3IYfa-Xt020jfSslwLjs,7978 +pandas/tests/frame/methods/test_sample.py,sha256=vPDSUU6oBD5X2C5rKUhIHk6o2xftm0zzMTwvuipelRM,13431 +pandas/tests/frame/methods/test_select_dtypes.py,sha256=SsvEwmjNFFwfOqxMlA-Z72qHJDtxNtvFWbtV-sbIODg,16638 +pandas/tests/frame/methods/test_set_axis.py,sha256=xiyZyjgDIO0B5HWGLeV_fVDyXj3YMDBfLyEDh5rQvcw,4608 +pandas/tests/frame/methods/test_set_index.py,sha256=h2a7zL_ZgN6zoRNcAV3QrgfqI59PR5jsiPPGC-V8F_U,26598 +pandas/tests/frame/methods/test_shift.py,sha256=unBlSwoV0OwFfysSr8ZKrqrrfoH7FRbPlGp18XW84OQ,27731 +pandas/tests/frame/methods/test_size.py,sha256=zFzVSvOpjHkA9_tEB2mPnfq9PJIBuBa4lCi6BvXbBDE,571 +pandas/tests/frame/methods/test_sort_index.py,sha256=BbCjfh_Zke1R7M9fPoRASORNfXS2KZ0IgWOF6jNnor0,34826 +pandas/tests/frame/methods/test_sort_values.py,sha256=NTmGhvm_flc6gzdtOeAOXsO3ai6K3peyH476Sj-qfLA,32982 +pandas/tests/frame/methods/test_swapaxes.py,sha256=-IuPIvjEz7X8-qxnWy1no5hG2WklPn6qERkmQQ-gAv0,1466 +pandas/tests/frame/methods/test_swaplevel.py,sha256=Y8npUpIQM0lSdIwY7auGcLJaF21JOb-KlVU3cvSLsOg,1277 +pandas/tests/frame/methods/test_to_csv.py,sha256=xkx76kpxWG7ZK6hcTEb0etllFg5_uSy0dLo1O6kfugI,51721 +pandas/tests/frame/methods/test_to_dict.py,sha256=BEKNs7rUFnd_cZZ7wQz0AmKJ7U-7KsEI6V3eApb1chw,18640 +pandas/tests/frame/methods/test_to_dict_of_blocks.py,sha256=dFL2fLKCQl-GXp2ephKiYgwjuQI_SEvsrG13RUIo1gE,2524 +pandas/tests/frame/methods/test_to_numpy.py,sha256=47-d29xA6qzZYnd08lBaKK3yj9aBZ9TKkoqgguGl1oQ,1795 +pandas/tests/frame/methods/test_to_period.py,sha256=Xiebi3IA_vUKrFNftLBkhF4N0gMbpI76ZCQpqhgO4iU,2863 +pandas/tests/frame/methods/test_to_records.py,sha256=35K3btxiApCcRVPG429FZAqqXIKRHKx4bVc8Sg3DCmE,18553 +pandas/tests/frame/methods/test_to_timestamp.py,sha256=1j6yjp4_WlxcDXSBKOk-IfrEbWtC4HvbIIHeM2x25ys,5866 +pandas/tests/frame/methods/test_transpose.py,sha256=JNhwvci37DlDMYHBaJz4Km998vw8NGfl7f4UYwwnsmM,6830 +pandas/tests/frame/methods/test_truncate.py,sha256=ZTnK8yZYqEhG3pe8KVwmJf4K890RMu8a60A4nC_qznM,5216 +pandas/tests/frame/methods/test_tz_convert.py,sha256=vsJm9M19ciCPqG0t5d_BlxuCmDphDkgb75SuYPtOhmE,4707 +pandas/tests/frame/methods/test_tz_localize.py,sha256=rMvd0K3W7N24qn7Q_tTkvbz7dOemIv3w89hthc6c5Y0,2084 +pandas/tests/frame/methods/test_update.py,sha256=npFHtPQmLMdhHa5xHbEL_zxXBuL4YK23CAnfIhTGn1k,6904 +pandas/tests/frame/methods/test_value_counts.py,sha256=YpYs0AZ8YgJE75W84O1KMfhd5oqpiuIJvLjz_YIz2KE,5556 +pandas/tests/frame/methods/test_values.py,sha256=ASljAwM9CEBMX6bA3FqWoSv4sOcRjuz8ZTfLSjo_F6Y,9406 +pandas/tests/frame/test_alter_axes.py,sha256=yHyCho1zs84UETsGGtw-gf3eTIyPj9zYUUA7wHTdRVk,873 +pandas/tests/frame/test_api.py,sha256=tn9xTbXzsDYRjqK3QJmBh64vNA8eV0JoGV2YAJrxTnU,12439 +pandas/tests/frame/test_arithmetic.py,sha256=xS3sOPjFEzALlHpwe-TMARDdV0xkpwBQ8NkqKMdhA9I,73152 +pandas/tests/frame/test_arrow_interface.py,sha256=KpAkREuJwWnlDBC45RvqogU_o1NSG0k44oMQAbgWCNw,1273 +pandas/tests/frame/test_block_internals.py,sha256=eG32ki-zsd8rMs7mI6Lc7j_2gM4Ga5aNqh2M1rTCZNA,16432 +pandas/tests/frame/test_constructors.py,sha256=_IWa5cMtmTMZcqH8Cs5M36Et2npqCskwgvQbx1oXbb8,123649 +pandas/tests/frame/test_cumulative.py,sha256=Ku20LYWW1hrycH8gslF8oNwXMv88RmaJC7x0a5GPbYw,2389 +pandas/tests/frame/test_iteration.py,sha256=BuyW6QePxoNZl-Cgxp5WLah_e-kSK2hsN8Gud_g0aoc,5077 +pandas/tests/frame/test_logical_ops.py,sha256=pUkVXQdIekK-OcmaqprYMp7cwND3t84Y2U25aMtYUq0,7352 +pandas/tests/frame/test_nonunique_indexes.py,sha256=wtBZpClv_46EwBSk59H1iXay2SR6Wv7m4ajh0tjisJg,11937 +pandas/tests/frame/test_npfuncs.py,sha256=DRLl7MSP7e5vRrVs3FgOooI4pZNmECurbVqkAAqvlUI,2751 +pandas/tests/frame/test_query_eval.py,sha256=Pe0xN0v1H4YPz55WIZs5tyV6zJwRkrLak_x1xXH6dBQ,54906 +pandas/tests/frame/test_reductions.py,sha256=j2qm6fsJfT8z0B1aeto9QbbY4LRSA36158tVk-033QY,77136 +pandas/tests/frame/test_repr.py,sha256=ycc0HNsBrKxPA88FIQturuMotBtqJco-ppO1NZE4JaI,16942 +pandas/tests/frame/test_stack_unstack.py,sha256=oRnTFLWq9JneBPR8vOElUmVgAVE120r71F1Sf5fhKhI,97375 +pandas/tests/frame/test_subclass.py,sha256=XqNKwBK-Zj06S4ATYGd59nKPzrzu8jmk_VbpStvB7ts,27880 +pandas/tests/frame/test_ufunc.py,sha256=YcUXnFE2n7lO5XN9aUvOJfeJyGqIDui0VhH-H1gUf1I,10554 +pandas/tests/frame/test_unary.py,sha256=fkB8LKCsctsyM9WS0g4JDiTD56gJy2l-cK7NcIQ2FHc,6603 +pandas/tests/frame/test_validate.py,sha256=hSQAfdZOKBe2MnbTBgWULmtA459zctixj7Qjy6bRg20,1094 +pandas/tests/generic/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/generic/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/generic/__pycache__/test_duplicate_labels.cpython-312.pyc,, +pandas/tests/generic/__pycache__/test_finalize.cpython-312.pyc,, +pandas/tests/generic/__pycache__/test_frame.cpython-312.pyc,, +pandas/tests/generic/__pycache__/test_generic.cpython-312.pyc,, +pandas/tests/generic/__pycache__/test_label_or_level_utils.cpython-312.pyc,, +pandas/tests/generic/__pycache__/test_series.cpython-312.pyc,, +pandas/tests/generic/__pycache__/test_to_xarray.cpython-312.pyc,, +pandas/tests/generic/test_duplicate_labels.py,sha256=-t-hhIiI3E1Byv1-jjvXDRAS8_tJzZaOIf-EsK6hrXg,14506 +pandas/tests/generic/test_finalize.py,sha256=HWv668IFuaSNElG3g1J5DL-wMHpU5T_iQYTOkaJA80U,28852 +pandas/tests/generic/test_frame.py,sha256=h6r5f3L-_V4JV5pP0AoFyvjtJP1ng7DJplN6Rrx4gzI,7332 +pandas/tests/generic/test_generic.py,sha256=MUhx9EVhCuo-fTOYRH2nzhQH8ip9-5QaNMjEPWx-NI4,17447 +pandas/tests/generic/test_label_or_level_utils.py,sha256=PhsVWjYjOHPZRqX4mwUc7jlOH3tnd7p9pkMFh87CtKU,10244 +pandas/tests/generic/test_series.py,sha256=oyFxVdh9G2GCBiTQktXNuafAw0wrbXs6Af8UnwUUiow,5677 +pandas/tests/generic/test_to_xarray.py,sha256=qGohtFgMtA8FS5y9AtnJQsd_-4Mg_Oach84Z8qDbHeg,4265 +pandas/tests/groupby/__init__.py,sha256=O41hwVGLyFtIhv-zbe2JBZiqD3heGA7LOk10RuxfcKc,659 +pandas/tests/groupby/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/groupby/__pycache__/conftest.cpython-312.pyc,, +pandas/tests/groupby/__pycache__/test_all_methods.cpython-312.pyc,, +pandas/tests/groupby/__pycache__/test_api.cpython-312.pyc,, +pandas/tests/groupby/__pycache__/test_apply.cpython-312.pyc,, +pandas/tests/groupby/__pycache__/test_apply_mutate.cpython-312.pyc,, +pandas/tests/groupby/__pycache__/test_bin_groupby.cpython-312.pyc,, +pandas/tests/groupby/__pycache__/test_categorical.cpython-312.pyc,, +pandas/tests/groupby/__pycache__/test_counting.cpython-312.pyc,, +pandas/tests/groupby/__pycache__/test_cumulative.cpython-312.pyc,, +pandas/tests/groupby/__pycache__/test_filters.cpython-312.pyc,, +pandas/tests/groupby/__pycache__/test_groupby.cpython-312.pyc,, +pandas/tests/groupby/__pycache__/test_groupby_dropna.cpython-312.pyc,, +pandas/tests/groupby/__pycache__/test_groupby_subclass.cpython-312.pyc,, +pandas/tests/groupby/__pycache__/test_grouping.cpython-312.pyc,, +pandas/tests/groupby/__pycache__/test_index_as_string.cpython-312.pyc,, +pandas/tests/groupby/__pycache__/test_indexing.cpython-312.pyc,, +pandas/tests/groupby/__pycache__/test_libgroupby.cpython-312.pyc,, +pandas/tests/groupby/__pycache__/test_missing.cpython-312.pyc,, +pandas/tests/groupby/__pycache__/test_numba.cpython-312.pyc,, +pandas/tests/groupby/__pycache__/test_numeric_only.cpython-312.pyc,, +pandas/tests/groupby/__pycache__/test_pipe.cpython-312.pyc,, +pandas/tests/groupby/__pycache__/test_raises.cpython-312.pyc,, +pandas/tests/groupby/__pycache__/test_reductions.cpython-312.pyc,, +pandas/tests/groupby/__pycache__/test_timegrouper.cpython-312.pyc,, +pandas/tests/groupby/aggregate/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/groupby/aggregate/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/groupby/aggregate/__pycache__/test_aggregate.cpython-312.pyc,, +pandas/tests/groupby/aggregate/__pycache__/test_cython.cpython-312.pyc,, +pandas/tests/groupby/aggregate/__pycache__/test_numba.cpython-312.pyc,, +pandas/tests/groupby/aggregate/__pycache__/test_other.cpython-312.pyc,, +pandas/tests/groupby/aggregate/test_aggregate.py,sha256=4S6PEKvRgk0ULSozn37cOsh6ohnEHZ3yaSGEp0Dmh9k,55554 +pandas/tests/groupby/aggregate/test_cython.py,sha256=XWKVeZTdLnpbaKlU128KkVmtIHntdpu_auCaHyyapXg,12800 +pandas/tests/groupby/aggregate/test_numba.py,sha256=Ba1zZzFC2-cjXE4OMOAStDvh_CeHy3hZwUhwDLDGkcY,13039 +pandas/tests/groupby/aggregate/test_other.py,sha256=LAuSm_tjHQlp_0oekNc14NCbj2gei4RR9jowjb_u65o,20669 +pandas/tests/groupby/conftest.py,sha256=uxnebcMXbaC_tH4Pg2wRZvXlWMZ_WnNIUeX8ftK7gWo,4785 +pandas/tests/groupby/methods/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/groupby/methods/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/groupby/methods/__pycache__/test_corrwith.cpython-312.pyc,, +pandas/tests/groupby/methods/__pycache__/test_describe.cpython-312.pyc,, +pandas/tests/groupby/methods/__pycache__/test_groupby_shift_diff.cpython-312.pyc,, +pandas/tests/groupby/methods/__pycache__/test_is_monotonic.cpython-312.pyc,, +pandas/tests/groupby/methods/__pycache__/test_nlargest_nsmallest.cpython-312.pyc,, +pandas/tests/groupby/methods/__pycache__/test_nth.cpython-312.pyc,, +pandas/tests/groupby/methods/__pycache__/test_quantile.cpython-312.pyc,, +pandas/tests/groupby/methods/__pycache__/test_rank.cpython-312.pyc,, +pandas/tests/groupby/methods/__pycache__/test_sample.cpython-312.pyc,, +pandas/tests/groupby/methods/__pycache__/test_size.cpython-312.pyc,, +pandas/tests/groupby/methods/__pycache__/test_skew.cpython-312.pyc,, +pandas/tests/groupby/methods/__pycache__/test_value_counts.cpython-312.pyc,, +pandas/tests/groupby/methods/test_corrwith.py,sha256=nseP6eDkLjiNIOSxm2EDFTkemTqNFUNqvvNJpMiNZVY,615 +pandas/tests/groupby/methods/test_describe.py,sha256=KFu1CeWWqpy3NWNh9IbzsirR36OKB9Q27yEUGZaM7og,9672 +pandas/tests/groupby/methods/test_groupby_shift_diff.py,sha256=4XMAhqV0JrGeXQn1_07ec9Nu25Dy1LOcDfojo4qEhNI,7925 +pandas/tests/groupby/methods/test_is_monotonic.py,sha256=OpnlOamR5gX1S7MVtZFGxnbt1Fem_wWH1Irc5aqkdq4,2566 +pandas/tests/groupby/methods/test_nlargest_nsmallest.py,sha256=MFS6cWChs3aBw3vb-n234pOV8_YYet2jOdDNN0lrMkg,3401 +pandas/tests/groupby/methods/test_nth.py,sha256=k2Pe1sTNCELszUfRU3SVf-54WHRjigSp0D51Cq2pmRA,28189 +pandas/tests/groupby/methods/test_quantile.py,sha256=deK9SMCVErwfQUDF_bc9DTH3fhxRBxuCxC4OHc3G6q0,16354 +pandas/tests/groupby/methods/test_rank.py,sha256=NE_ciV_TwLbTGoq1OFUFX5yadyiYoP3m5ppVOoD5264,24263 +pandas/tests/groupby/methods/test_sample.py,sha256=n_dLYblQo9MWnpngMRIIGLZFGEGOeAfEqsL9c9gLCKg,5155 +pandas/tests/groupby/methods/test_size.py,sha256=PQ2op8vrqyDhNYwQyM2x19v2jJzrTvUH0GCSv0xE_eU,4250 +pandas/tests/groupby/methods/test_skew.py,sha256=_FTlnXtE_fic6ZZ322S583IXUY5hEQggi-3Xbuboahw,841 +pandas/tests/groupby/methods/test_value_counts.py,sha256=8awMEsjBh7R_8s-w5roAq-mZrhl_9NrWQOU8lV8CYPs,39874 +pandas/tests/groupby/test_all_methods.py,sha256=eQsLKoyDyGZNPecbxC1HRzdIwW_DBEp0x_r3gD620pw,3077 +pandas/tests/groupby/test_api.py,sha256=IpMVl4g9F2317jWVTSiHoAsZKaOQWFx0Oi_jLWfv_DQ,8481 +pandas/tests/groupby/test_apply.py,sha256=z0nCK9dbF8ww3RoA3MhwZX5_BE-WF8AAF8672e2YnVs,54859 +pandas/tests/groupby/test_apply_mutate.py,sha256=b5rtOE-IwkLsEp5VTcyPDtKfCTB9MYw95a0U8ThKLE0,5047 +pandas/tests/groupby/test_bin_groupby.py,sha256=nZGe01NsuZmS88cMqq8fGFbKl-umvmWjXd8BGmR3jTo,1769 +pandas/tests/groupby/test_categorical.py,sha256=73Njrb3YH6fGMnL-9x1_rlT4_jGROdM5sVIQeCRaU-A,74271 +pandas/tests/groupby/test_counting.py,sha256=59N0fV7J8XRijMaIU0Cu5-odZmaoS73cGvzCuuOKrBA,13623 +pandas/tests/groupby/test_cumulative.py,sha256=c6C7ZNo0O5DH9SowsAXp4j_SF-wskjrUlNtfDJomjxQ,10588 +pandas/tests/groupby/test_filters.py,sha256=uFvXjXF2fpQJSwZUhGOUfguyJk7xoXYyL0ShN2KfXx8,21870 +pandas/tests/groupby/test_groupby.py,sha256=btnUSXmk3LKZJ5WBvaWe8ylA2FyaLbc1na2X6S3DTtU,108274 +pandas/tests/groupby/test_groupby_dropna.py,sha256=8OcPba3g6S_FwrxLBsrF8QGFO3Y9VH-bAPeu15h50eQ,23530 +pandas/tests/groupby/test_groupby_subclass.py,sha256=b13F2oZyPfzGngygHRHtBK_vlPfzdik-DiLaWWDcKZ8,4568 +pandas/tests/groupby/test_grouping.py,sha256=0KurYwG--QzwakI0Da37ZfP82qEpMYp22y-wWrvIll8,45862 +pandas/tests/groupby/test_index_as_string.py,sha256=bwAMXa4aSzVDUY1t3HmzK4y-jO5jIwbbRu85Jmb8-U0,2274 +pandas/tests/groupby/test_indexing.py,sha256=Ln_43WnuxtAVrWoaUHWh1IqUSY0i42nY9VnEnw86oXg,9521 +pandas/tests/groupby/test_libgroupby.py,sha256=xiFJcUw_cwTUpQh6E9L47EZm8HopmDrKuYSTI0gHnDs,10457 +pandas/tests/groupby/test_missing.py,sha256=u6mv6_D1ydhkK3jLXqfvidDlOXYdUsN44ySzFksaIlU,5358 +pandas/tests/groupby/test_numba.py,sha256=B2ygkBddeTyLE7a6okHM_CbFwsOaqMceHh4h6fmmQNg,3260 +pandas/tests/groupby/test_numeric_only.py,sha256=gmxCGXKDLN_AZr4dQ2lA4zKrU84uDtwfFrdAUdFzDNA,18573 +pandas/tests/groupby/test_pipe.py,sha256=BpMDqw-ZGT-tHUJN7k6XoWz2H46sBqSxmouppbWMHsU,2098 +pandas/tests/groupby/test_raises.py,sha256=lzNGHyOBhvWL71QarVtajs8_ZSjIvSBJIwrk4-YbgdY,22214 +pandas/tests/groupby/test_reductions.py,sha256=vcpcNtIckgBbcv2gCCA6b3pP3WoUquLqnutMpNwAOMA,36833 +pandas/tests/groupby/test_timegrouper.py,sha256=5EdFromkRWltGo9xlkfRRb2eSwIpNrV834F3P_bqnNI,34779 +pandas/tests/groupby/transform/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/groupby/transform/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/groupby/transform/__pycache__/test_numba.cpython-312.pyc,, +pandas/tests/groupby/transform/__pycache__/test_transform.cpython-312.pyc,, +pandas/tests/groupby/transform/test_numba.py,sha256=6GJOeWL6kOIJQQaBCAD9ajv_-m6NmCrpxB9wwoCSr0A,9684 +pandas/tests/groupby/transform/test_transform.py,sha256=0rG5_Lma8MEIs_l_GS8g0eDhALsk5_wokg9IuzRvSRs,57218 +pandas/tests/indexes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/indexes/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/indexes/__pycache__/conftest.cpython-312.pyc,, +pandas/tests/indexes/__pycache__/test_any_index.cpython-312.pyc,, +pandas/tests/indexes/__pycache__/test_base.cpython-312.pyc,, +pandas/tests/indexes/__pycache__/test_common.cpython-312.pyc,, +pandas/tests/indexes/__pycache__/test_datetimelike.cpython-312.pyc,, +pandas/tests/indexes/__pycache__/test_engines.cpython-312.pyc,, +pandas/tests/indexes/__pycache__/test_frozen.cpython-312.pyc,, +pandas/tests/indexes/__pycache__/test_index_new.cpython-312.pyc,, +pandas/tests/indexes/__pycache__/test_indexing.cpython-312.pyc,, +pandas/tests/indexes/__pycache__/test_numpy_compat.cpython-312.pyc,, +pandas/tests/indexes/__pycache__/test_old_base.cpython-312.pyc,, +pandas/tests/indexes/__pycache__/test_setops.cpython-312.pyc,, +pandas/tests/indexes/__pycache__/test_subclass.cpython-312.pyc,, +pandas/tests/indexes/base_class/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/indexes/base_class/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/indexes/base_class/__pycache__/test_constructors.cpython-312.pyc,, +pandas/tests/indexes/base_class/__pycache__/test_formats.cpython-312.pyc,, +pandas/tests/indexes/base_class/__pycache__/test_indexing.cpython-312.pyc,, +pandas/tests/indexes/base_class/__pycache__/test_pickle.cpython-312.pyc,, +pandas/tests/indexes/base_class/__pycache__/test_reshape.cpython-312.pyc,, +pandas/tests/indexes/base_class/__pycache__/test_setops.cpython-312.pyc,, +pandas/tests/indexes/base_class/__pycache__/test_where.cpython-312.pyc,, +pandas/tests/indexes/base_class/test_constructors.py,sha256=c4hEi_fFI9WNCKw-HhXTtb6fX9bV7RmL4IoTxag5GH4,2763 +pandas/tests/indexes/base_class/test_formats.py,sha256=TfviPEyXl7e0N6iiySBiaPBiMaNc8hDpiY7iEpBXXcE,6329 +pandas/tests/indexes/base_class/test_indexing.py,sha256=1zbBHv-nJCIfXRicDPXPtyLBL3Iy-LvH5bkamnoFGrI,3687 +pandas/tests/indexes/base_class/test_pickle.py,sha256=ANKn2SirZRA2AHaZoCDHCB1AjLEuUTgXU2mXI6n3Tvw,309 +pandas/tests/indexes/base_class/test_reshape.py,sha256=F5i0CHj5vPBP1Xvg71l7bTt9u2krFVqltowHc_2l9FA,3168 +pandas/tests/indexes/base_class/test_setops.py,sha256=X84dGTmkrEJ2oSQfr-WfozQA3moGUpnmbhkTYzJWH7k,9076 +pandas/tests/indexes/base_class/test_where.py,sha256=uq7oB-lk7rsgYQer8qeUsqD5aSECtRPSEUfKzn91BiE,341 +pandas/tests/indexes/categorical/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/indexes/categorical/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/indexes/categorical/__pycache__/test_append.cpython-312.pyc,, +pandas/tests/indexes/categorical/__pycache__/test_astype.cpython-312.pyc,, +pandas/tests/indexes/categorical/__pycache__/test_category.cpython-312.pyc,, +pandas/tests/indexes/categorical/__pycache__/test_constructors.cpython-312.pyc,, +pandas/tests/indexes/categorical/__pycache__/test_equals.cpython-312.pyc,, +pandas/tests/indexes/categorical/__pycache__/test_fillna.cpython-312.pyc,, +pandas/tests/indexes/categorical/__pycache__/test_formats.cpython-312.pyc,, +pandas/tests/indexes/categorical/__pycache__/test_indexing.cpython-312.pyc,, +pandas/tests/indexes/categorical/__pycache__/test_map.cpython-312.pyc,, +pandas/tests/indexes/categorical/__pycache__/test_reindex.cpython-312.pyc,, +pandas/tests/indexes/categorical/__pycache__/test_setops.cpython-312.pyc,, +pandas/tests/indexes/categorical/test_append.py,sha256=LjLMq8GkNrsIVNfTrujLv_TlKo79oA_XbpNUFs-pqVQ,2191 +pandas/tests/indexes/categorical/test_astype.py,sha256=mQjQ9hbRf940DjzvC9OD6t8BzwphBXJdrROyEul1tzU,2860 +pandas/tests/indexes/categorical/test_category.py,sha256=-jO0jW9IJDvFWnl41MkcGuiWgkeuedUdRDIgVyoyB38,14683 +pandas/tests/indexes/categorical/test_constructors.py,sha256=g3hEVtOS576z11miVwakwud3cLXkFI2ErImUaFW9N6U,5536 +pandas/tests/indexes/categorical/test_equals.py,sha256=AIrr-W5WeqDj5KbELqjHm3-hqqx3q8YxBrv1z2oco94,3569 +pandas/tests/indexes/categorical/test_fillna.py,sha256=sH68aWCabI2qy5dbgxQCXeTfvn1NQgDfM1OT4ojFmaU,1850 +pandas/tests/indexes/categorical/test_formats.py,sha256=Rw-qSZ8zLRJkATk1UhPNAuVJMbbHBpuoALCXUDPR5PM,6297 +pandas/tests/indexes/categorical/test_indexing.py,sha256=zBvryPgX3VF5P4HqUQ1h1FD2warHLfSvb0nBq6rxjrc,14978 +pandas/tests/indexes/categorical/test_map.py,sha256=VHsSFGWEBmgQLvvquC6-y3QDq3lwzSpqPWZHTLiGdzw,4664 +pandas/tests/indexes/categorical/test_reindex.py,sha256=vPCV9O582vxJpubqCm33UHcaOKMZNg8OMzDF3lQQDiM,2938 +pandas/tests/indexes/categorical/test_setops.py,sha256=YiBoQN3Dor2p32HCUColWIZBH620H1aPa4easA5FMgc,462 +pandas/tests/indexes/conftest.py,sha256=aP9iTl0n1HpZWIP_02i__XxFnSMJF8iCM5Ein2MRK80,987 +pandas/tests/indexes/datetimelike_/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/indexes/datetimelike_/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/indexes/datetimelike_/__pycache__/test_drop_duplicates.cpython-312.pyc,, +pandas/tests/indexes/datetimelike_/__pycache__/test_equals.cpython-312.pyc,, +pandas/tests/indexes/datetimelike_/__pycache__/test_indexing.cpython-312.pyc,, +pandas/tests/indexes/datetimelike_/__pycache__/test_is_monotonic.cpython-312.pyc,, +pandas/tests/indexes/datetimelike_/__pycache__/test_nat.cpython-312.pyc,, +pandas/tests/indexes/datetimelike_/__pycache__/test_sort_values.cpython-312.pyc,, +pandas/tests/indexes/datetimelike_/__pycache__/test_value_counts.cpython-312.pyc,, +pandas/tests/indexes/datetimelike_/test_drop_duplicates.py,sha256=UEmTzsZerSOIE6mPfaw4kQd7UFEo02H-EW5GOPpDTKU,2600 +pandas/tests/indexes/datetimelike_/test_equals.py,sha256=7Jnk1MjPYvI-I_YMRNRF29-g5CLaFmU3ZqQ6aO9KqIE,6348 +pandas/tests/indexes/datetimelike_/test_indexing.py,sha256=QoTXbCiqjK4tBDHUbq1TKPp0NroYkeheFjRq-VxlsP0,1310 +pandas/tests/indexes/datetimelike_/test_is_monotonic.py,sha256=_5PXF7mVilu1S4EJv7F-XMYIoz40kBkdSs4RJ8jTVdI,1522 +pandas/tests/indexes/datetimelike_/test_nat.py,sha256=6-Yr-n4JskfsjbaEPFgaRPKX4S7R-LhQOEQSC7cBybw,1335 +pandas/tests/indexes/datetimelike_/test_sort_values.py,sha256=iIhZOW7CEwVD3KuJUFEOM2z18KORCx04W09bwsdKSNs,11463 +pandas/tests/indexes/datetimelike_/test_value_counts.py,sha256=o090A9QuhmahJjH0WgKBIxXdBVxPkAc8vikXqZLuoD4,3150 +pandas/tests/indexes/datetimes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/indexes/datetimes/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/indexes/datetimes/__pycache__/test_arithmetic.cpython-312.pyc,, +pandas/tests/indexes/datetimes/__pycache__/test_constructors.cpython-312.pyc,, +pandas/tests/indexes/datetimes/__pycache__/test_date_range.cpython-312.pyc,, +pandas/tests/indexes/datetimes/__pycache__/test_datetime.cpython-312.pyc,, +pandas/tests/indexes/datetimes/__pycache__/test_formats.cpython-312.pyc,, +pandas/tests/indexes/datetimes/__pycache__/test_freq_attr.cpython-312.pyc,, +pandas/tests/indexes/datetimes/__pycache__/test_indexing.cpython-312.pyc,, +pandas/tests/indexes/datetimes/__pycache__/test_iter.cpython-312.pyc,, +pandas/tests/indexes/datetimes/__pycache__/test_join.cpython-312.pyc,, +pandas/tests/indexes/datetimes/__pycache__/test_npfuncs.cpython-312.pyc,, +pandas/tests/indexes/datetimes/__pycache__/test_ops.cpython-312.pyc,, +pandas/tests/indexes/datetimes/__pycache__/test_partial_slicing.cpython-312.pyc,, +pandas/tests/indexes/datetimes/__pycache__/test_pickle.cpython-312.pyc,, +pandas/tests/indexes/datetimes/__pycache__/test_reindex.cpython-312.pyc,, +pandas/tests/indexes/datetimes/__pycache__/test_scalar_compat.cpython-312.pyc,, +pandas/tests/indexes/datetimes/__pycache__/test_setops.cpython-312.pyc,, +pandas/tests/indexes/datetimes/__pycache__/test_timezones.cpython-312.pyc,, +pandas/tests/indexes/datetimes/methods/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/indexes/datetimes/methods/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/indexes/datetimes/methods/__pycache__/test_asof.cpython-312.pyc,, +pandas/tests/indexes/datetimes/methods/__pycache__/test_astype.cpython-312.pyc,, +pandas/tests/indexes/datetimes/methods/__pycache__/test_delete.cpython-312.pyc,, +pandas/tests/indexes/datetimes/methods/__pycache__/test_factorize.cpython-312.pyc,, +pandas/tests/indexes/datetimes/methods/__pycache__/test_fillna.cpython-312.pyc,, +pandas/tests/indexes/datetimes/methods/__pycache__/test_insert.cpython-312.pyc,, +pandas/tests/indexes/datetimes/methods/__pycache__/test_isocalendar.cpython-312.pyc,, +pandas/tests/indexes/datetimes/methods/__pycache__/test_map.cpython-312.pyc,, +pandas/tests/indexes/datetimes/methods/__pycache__/test_normalize.cpython-312.pyc,, +pandas/tests/indexes/datetimes/methods/__pycache__/test_repeat.cpython-312.pyc,, +pandas/tests/indexes/datetimes/methods/__pycache__/test_resolution.cpython-312.pyc,, +pandas/tests/indexes/datetimes/methods/__pycache__/test_round.cpython-312.pyc,, +pandas/tests/indexes/datetimes/methods/__pycache__/test_shift.cpython-312.pyc,, +pandas/tests/indexes/datetimes/methods/__pycache__/test_snap.cpython-312.pyc,, +pandas/tests/indexes/datetimes/methods/__pycache__/test_to_frame.cpython-312.pyc,, +pandas/tests/indexes/datetimes/methods/__pycache__/test_to_julian_date.cpython-312.pyc,, +pandas/tests/indexes/datetimes/methods/__pycache__/test_to_period.cpython-312.pyc,, +pandas/tests/indexes/datetimes/methods/__pycache__/test_to_pydatetime.cpython-312.pyc,, +pandas/tests/indexes/datetimes/methods/__pycache__/test_to_series.cpython-312.pyc,, +pandas/tests/indexes/datetimes/methods/__pycache__/test_tz_convert.cpython-312.pyc,, +pandas/tests/indexes/datetimes/methods/__pycache__/test_tz_localize.cpython-312.pyc,, +pandas/tests/indexes/datetimes/methods/__pycache__/test_unique.cpython-312.pyc,, +pandas/tests/indexes/datetimes/methods/test_asof.py,sha256=gd-nBXLe-Dc5Voc_Ksgmq9mOU6S_I5ZZqlXcapgKzfE,738 +pandas/tests/indexes/datetimes/methods/test_astype.py,sha256=S04yQ6BdlxSXpR5DFRCsWlDTO4IUEDNSHIuMP1Jk6Zw,12201 +pandas/tests/indexes/datetimes/methods/test_delete.py,sha256=JaaHDwYuTarkta3Qd2fbteZd9k0oOzJsWCPEHUHHG4k,4441 +pandas/tests/indexes/datetimes/methods/test_factorize.py,sha256=Mif09gcfRfIO2uhCqNN9OC_NXggKizbuwaz6ScGzMUE,4468 +pandas/tests/indexes/datetimes/methods/test_fillna.py,sha256=eESnVTQ8J3iBL24bWKt7TmHxC5FJiLZMpKjw1V376qY,2004 +pandas/tests/indexes/datetimes/methods/test_insert.py,sha256=StmxdK3meNNEDO_CGzVIqltbXxwfX0pQxsngnPQfdtA,9343 +pandas/tests/indexes/datetimes/methods/test_isocalendar.py,sha256=JEABIm6LNySCbSUq6HLS-_qTGK3HgVcScSXLpDsrJ8o,908 +pandas/tests/indexes/datetimes/methods/test_map.py,sha256=1JR2lb_zk_8aIgRqnuWHfeXRPZBsFtdT4tRXeTDNqsQ,1358 +pandas/tests/indexes/datetimes/methods/test_normalize.py,sha256=rztamd3kwUZMcVQjeR1JcaIKr7pT0ACFcU4-FFynZkA,3041 +pandas/tests/indexes/datetimes/methods/test_repeat.py,sha256=GN-wTWws2sjodNibctZOi_NDX85y36Lr2BBmAs3LLMM,2740 +pandas/tests/indexes/datetimes/methods/test_resolution.py,sha256=RzkIL8IX63X1fgwr8o4_xuKvdOtPHdodPbsS75u9BRM,785 +pandas/tests/indexes/datetimes/methods/test_round.py,sha256=Ic1FFoRHdPv4TF1dSnOWVzVX90GowbXumbuNgTFPYlM,7822 +pandas/tests/indexes/datetimes/methods/test_shift.py,sha256=NhyUs0PMDuzSM573tqUamx3THf03WUNKz0nSOzDta5M,5933 +pandas/tests/indexes/datetimes/methods/test_snap.py,sha256=smwfWvN33B6UgLagKaBQkllTuGAm7Wiaq87M9nxu8g8,1305 +pandas/tests/indexes/datetimes/methods/test_to_frame.py,sha256=C6glyGdxSs-hMDQSt9jkftmRlTGPMCGdIQlfChR9iGk,998 +pandas/tests/indexes/datetimes/methods/test_to_julian_date.py,sha256=u6JLYazILIdltbe1uZE3iBAqE_ixXwx0oqwS6T-Mpng,1608 +pandas/tests/indexes/datetimes/methods/test_to_period.py,sha256=IIzHPLsk8BR43Ib5-8-EVxLQc_rkTcGBSk1M4-9OhYw,7986 +pandas/tests/indexes/datetimes/methods/test_to_pydatetime.py,sha256=sM22b33Cxwrpc5nShAp5QH2KQPOlEpi5d8G6fM3vVI8,1345 +pandas/tests/indexes/datetimes/methods/test_to_series.py,sha256=8ZW3AxMkHj3IV1wVgM797SH_rRLKQ9zld1UVkhk1C8Q,493 +pandas/tests/indexes/datetimes/methods/test_tz_convert.py,sha256=-Tuxq1egpSCBnBB7E_rAj1FudFgTm2DDYQ_wPMKgzwQ,11295 +pandas/tests/indexes/datetimes/methods/test_tz_localize.py,sha256=Q7A54lsovDxBDEqU7XNBJql3PoNLF7NVeXwvMFgrVI0,14830 +pandas/tests/indexes/datetimes/methods/test_unique.py,sha256=qZorAPI_oWcz5WdBEr0nQuT_mrApTgShqg3JVlzpVKU,2096 +pandas/tests/indexes/datetimes/test_arithmetic.py,sha256=l2q_n3zBT98OvI4gV7XZOZMCvo54xgM9frByNKCsbyU,1796 +pandas/tests/indexes/datetimes/test_constructors.py,sha256=zzICypvVbu8_PCfL3jiDGjSJWSflWjJbpqS5iNkd1kA,43922 +pandas/tests/indexes/datetimes/test_date_range.py,sha256=2CECH8fOYUP7LxyqlehEHVme2oSN4ZvEl3hjH8t-TDY,61363 +pandas/tests/indexes/datetimes/test_datetime.py,sha256=Q_dwJTXtSuVYTlMmnGhiNGCRrqHONu9wu2N5wgZw4pY,7305 +pandas/tests/indexes/datetimes/test_formats.py,sha256=rN90ZOq3e83t7X6uyd-cR1czM4A01nr3z_GIJJ0sy0k,12738 +pandas/tests/indexes/datetimes/test_freq_attr.py,sha256=oX_cweTcpKd27ywN976KCYpg0oFe77MeDWqnRJQwVRo,1732 +pandas/tests/indexes/datetimes/test_indexing.py,sha256=MncSVI_l914qEW2CUg_livQrJ6AcOxvzmaiNOdzlOoA,25241 +pandas/tests/indexes/datetimes/test_iter.py,sha256=7r3wuHLeCBHfX8kaHNK-4Ecr6ZqR89Dhzkisx2C7jOI,2590 +pandas/tests/indexes/datetimes/test_join.py,sha256=LUV-a1_kCQ4BCr8R-iBWU7VmlOhYK4OZYIgDd-9E0cg,4742 +pandas/tests/indexes/datetimes/test_npfuncs.py,sha256=YJihZytss-MVNprp4p5pAL_emeC5pb3hBwtaS3yMCcU,384 +pandas/tests/indexes/datetimes/test_ops.py,sha256=h9MI1sM5I_T4a7kEPdZs2QuXTdlcnvKQJdI5jh6j4h4,1340 +pandas/tests/indexes/datetimes/test_partial_slicing.py,sha256=OlC1IDbJ2y_qjp-HCFERReBOHb07DnlPZ3lMlhwMSLA,16495 +pandas/tests/indexes/datetimes/test_pickle.py,sha256=cpuQl8fsaqJhP4qroLU0LUQjqFQ0uaX3sHql2UYOSg4,1358 +pandas/tests/indexes/datetimes/test_reindex.py,sha256=s1pt3OlK_JdWcaHsxlsvSh34mqFsR4wrONAwFBo5yVw,2145 +pandas/tests/indexes/datetimes/test_scalar_compat.py,sha256=pJz6r8-pnr5nl_KkUaCkTu2A3SGzJbH_0dpTFRjUUz8,11156 +pandas/tests/indexes/datetimes/test_setops.py,sha256=HThtofPALvrCNqwnFk-tqdvCIe_ij2f-VOObJfZQ93w,23574 +pandas/tests/indexes/datetimes/test_timezones.py,sha256=LfELNHXgQN5-7zwBW5OweUZm6y8Ogtm-ir7l-RQAJpQ,8046 +pandas/tests/indexes/interval/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/indexes/interval/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/indexes/interval/__pycache__/test_astype.cpython-312.pyc,, +pandas/tests/indexes/interval/__pycache__/test_constructors.cpython-312.pyc,, +pandas/tests/indexes/interval/__pycache__/test_equals.cpython-312.pyc,, +pandas/tests/indexes/interval/__pycache__/test_formats.cpython-312.pyc,, +pandas/tests/indexes/interval/__pycache__/test_indexing.cpython-312.pyc,, +pandas/tests/indexes/interval/__pycache__/test_interval.cpython-312.pyc,, +pandas/tests/indexes/interval/__pycache__/test_interval_range.cpython-312.pyc,, +pandas/tests/indexes/interval/__pycache__/test_interval_tree.cpython-312.pyc,, +pandas/tests/indexes/interval/__pycache__/test_join.cpython-312.pyc,, +pandas/tests/indexes/interval/__pycache__/test_pickle.cpython-312.pyc,, +pandas/tests/indexes/interval/__pycache__/test_setops.cpython-312.pyc,, +pandas/tests/indexes/interval/test_astype.py,sha256=7h7n8euKiXPnRU2d-4FYTAf-6iqPDR703dU7Oq10qwM,8809 +pandas/tests/indexes/interval/test_constructors.py,sha256=THCXDlRG7AncX5wzRlp9w1RNrYA0bTpWmzErMVfT0-w,19853 +pandas/tests/indexes/interval/test_equals.py,sha256=a7GA_whLbOiS4WxUdtDrqKOUhsfqq3TL0nkhqPccuss,1226 +pandas/tests/indexes/interval/test_formats.py,sha256=sUwFbFSiq-BBvB73vB1gZ4G5BTqQHWRRsmZzdx372DI,3921 +pandas/tests/indexes/interval/test_indexing.py,sha256=ig3f396aAkl3Lh1VX-MWOrDCn5t8bOop7xjOWjuCF7U,25320 +pandas/tests/indexes/interval/test_interval.py,sha256=L4Zo4GWIMVzHpOQ3Q09-GH_0Ixtge5ATR6kIgMYYjoc,34741 +pandas/tests/indexes/interval/test_interval_range.py,sha256=z_ZiNlL_7esHwH4Kd77k2gPm5Ev0Zy_NgACSkKoy4vA,13758 +pandas/tests/indexes/interval/test_interval_tree.py,sha256=yHyolu5v8YRazksfOBRgWd3O3eFVtzPc6NePpcV0ceU,7560 +pandas/tests/indexes/interval/test_join.py,sha256=HQJQLS9-RT7de6nBHsw50lBo4arBmXEVZhVMt4iuHyg,1148 +pandas/tests/indexes/interval/test_pickle.py,sha256=Jsmm_p3_qQpfJ9OqCpD3uLMzBkpsxufj1w6iUorYqmk,435 +pandas/tests/indexes/interval/test_setops.py,sha256=nwBz1MHuHiM7JQc74w2doEpgTSwg3NYfGwGbQFXWKw8,8346 +pandas/tests/indexes/multi/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/indexes/multi/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/indexes/multi/__pycache__/conftest.cpython-312.pyc,, +pandas/tests/indexes/multi/__pycache__/test_analytics.cpython-312.pyc,, +pandas/tests/indexes/multi/__pycache__/test_astype.cpython-312.pyc,, +pandas/tests/indexes/multi/__pycache__/test_compat.cpython-312.pyc,, +pandas/tests/indexes/multi/__pycache__/test_constructors.cpython-312.pyc,, +pandas/tests/indexes/multi/__pycache__/test_conversion.cpython-312.pyc,, +pandas/tests/indexes/multi/__pycache__/test_copy.cpython-312.pyc,, +pandas/tests/indexes/multi/__pycache__/test_drop.cpython-312.pyc,, +pandas/tests/indexes/multi/__pycache__/test_duplicates.cpython-312.pyc,, +pandas/tests/indexes/multi/__pycache__/test_equivalence.cpython-312.pyc,, +pandas/tests/indexes/multi/__pycache__/test_formats.cpython-312.pyc,, +pandas/tests/indexes/multi/__pycache__/test_get_level_values.cpython-312.pyc,, +pandas/tests/indexes/multi/__pycache__/test_get_set.cpython-312.pyc,, +pandas/tests/indexes/multi/__pycache__/test_indexing.cpython-312.pyc,, +pandas/tests/indexes/multi/__pycache__/test_integrity.cpython-312.pyc,, +pandas/tests/indexes/multi/__pycache__/test_isin.cpython-312.pyc,, +pandas/tests/indexes/multi/__pycache__/test_join.cpython-312.pyc,, +pandas/tests/indexes/multi/__pycache__/test_lexsort.cpython-312.pyc,, +pandas/tests/indexes/multi/__pycache__/test_missing.cpython-312.pyc,, +pandas/tests/indexes/multi/__pycache__/test_monotonic.cpython-312.pyc,, +pandas/tests/indexes/multi/__pycache__/test_names.cpython-312.pyc,, +pandas/tests/indexes/multi/__pycache__/test_partial_indexing.cpython-312.pyc,, +pandas/tests/indexes/multi/__pycache__/test_pickle.cpython-312.pyc,, +pandas/tests/indexes/multi/__pycache__/test_reindex.cpython-312.pyc,, +pandas/tests/indexes/multi/__pycache__/test_reshape.cpython-312.pyc,, +pandas/tests/indexes/multi/__pycache__/test_setops.cpython-312.pyc,, +pandas/tests/indexes/multi/__pycache__/test_sorting.cpython-312.pyc,, +pandas/tests/indexes/multi/__pycache__/test_take.cpython-312.pyc,, +pandas/tests/indexes/multi/conftest.py,sha256=42mdJqtqvX3PlBSdch1Y6jRBvhe0IzZxOoLt-BGX03Q,698 +pandas/tests/indexes/multi/test_analytics.py,sha256=FeKERG9vHP-fAeGhlrzKO3IfAFpOOQnxQD7fRu2ycLY,6710 +pandas/tests/indexes/multi/test_astype.py,sha256=YmTnPF6qXwvYY82wZfQ8XFwVwOYYsIls3LSrdADDW-4,924 +pandas/tests/indexes/multi/test_compat.py,sha256=q53DVV5fYOKRVEQBl_2ws6WXrNsrGr5w4FXvXLUBeuQ,3918 +pandas/tests/indexes/multi/test_constructors.py,sha256=LP51k4lUfQgpfu7tjeIvvxaFgv-x_6VapDDx9I-y00I,26775 +pandas/tests/indexes/multi/test_conversion.py,sha256=8okPvlaOQgJzneUiy3MTwHU4Z9_th4cadqAxPiV-nLc,4957 +pandas/tests/indexes/multi/test_copy.py,sha256=9Xperk7a4yBTQKo8fgk3gCa2SwJr30mH2JYYMYWguWY,2405 +pandas/tests/indexes/multi/test_drop.py,sha256=Mv5FB-riRSuwwvVFJ60GwxRGbuFkU_LU5DPW8KY8NTk,6089 +pandas/tests/indexes/multi/test_duplicates.py,sha256=7_FP6fYuzDdffF2Wvgl8VKW4Auzq0xJ5ZVfp5Evnm3A,11559 +pandas/tests/indexes/multi/test_equivalence.py,sha256=LKBMAg82PbzkuMMy18u6Iktjzuavo1PIY-IxtPGBpZE,8530 +pandas/tests/indexes/multi/test_formats.py,sha256=Ra7L6T0N4zh6rZUg3gFP6bGC902uhBKV4kyLku7HCuI,9538 +pandas/tests/indexes/multi/test_get_level_values.py,sha256=WFSDmHIAXZ1RvDl-mK2HtXmWRO6IwSX5F0J7j5z0cm8,3971 +pandas/tests/indexes/multi/test_get_set.py,sha256=S3n29xb_Em0uKOsd6MPc_HR2bCQ54DHSdGi1bj1RSAE,12801 +pandas/tests/indexes/multi/test_indexing.py,sha256=lbx9kPQFf5EFfdCZ-yg1nGSqmJOYcpuHCBMC6vs_ZvA,36399 +pandas/tests/indexes/multi/test_integrity.py,sha256=VzyV3RrhWkQxwWzzLeLT6Lmc-njl4FnpoAIshI1BFW8,9031 +pandas/tests/indexes/multi/test_isin.py,sha256=OtlwJ9zZDvwgZOgbeY_oidWPOUmii_JBCCBpHnLw8us,3426 +pandas/tests/indexes/multi/test_join.py,sha256=aRp18UCIgoSXazdYdirOwGV0k8Gj4o5eNRJL56p56Bc,8440 +pandas/tests/indexes/multi/test_lexsort.py,sha256=KbwMnYF6GTIdefQ7eACQusNNuehbtiuqzBMqsOSfDU0,1358 +pandas/tests/indexes/multi/test_missing.py,sha256=hHjKWxl5vkG5k9B9fxglrYB4eQldKamkMbACAu6OvUY,3348 +pandas/tests/indexes/multi/test_monotonic.py,sha256=5xlESrQOEcFWdr0iB3OipJtA6-RzriU3Yq2OQGgP0M4,7007 +pandas/tests/indexes/multi/test_names.py,sha256=zx_8kapVXzDS_SsylRzTFia2OrNJeEq3kmNHUA4RVPM,6601 +pandas/tests/indexes/multi/test_partial_indexing.py,sha256=sVNIk9_NxMDsHuRQzPCernPmchTF5INAUFkzQV7t8T0,4765 +pandas/tests/indexes/multi/test_pickle.py,sha256=ZJVZo0DcXDtV6BAUuPAKbwMV8aGfazJLU7Lw6lRmBcw,259 +pandas/tests/indexes/multi/test_reindex.py,sha256=ww8fSIx426wfqBTogkJrKS533CjKorf-B4bhyKdEnD4,5856 +pandas/tests/indexes/multi/test_reshape.py,sha256=yRcnTGS0M5749jUZGEZA8_UxSZ-CeOeCsWYBbTS0nTY,6711 +pandas/tests/indexes/multi/test_setops.py,sha256=74Ob19TAIflChAm-jfGmi5KTC8fnQkHvprHBxLYSELM,25466 +pandas/tests/indexes/multi/test_sorting.py,sha256=69C8BENuzyUvnQXEbjVvADmBAr5G6wzM-ELHOMLV2Do,10745 +pandas/tests/indexes/multi/test_take.py,sha256=4MaxPM4ZJQPXJKiqgwEwhZ71TyH4KQfIs5LgS40vvLM,2487 +pandas/tests/indexes/numeric/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/indexes/numeric/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/indexes/numeric/__pycache__/test_astype.cpython-312.pyc,, +pandas/tests/indexes/numeric/__pycache__/test_indexing.cpython-312.pyc,, +pandas/tests/indexes/numeric/__pycache__/test_join.cpython-312.pyc,, +pandas/tests/indexes/numeric/__pycache__/test_numeric.cpython-312.pyc,, +pandas/tests/indexes/numeric/__pycache__/test_setops.cpython-312.pyc,, +pandas/tests/indexes/numeric/test_astype.py,sha256=P19W9zZl8tN0EK-PaEi2gIFHLwCbruTMEUm7_ALGH9Q,3618 +pandas/tests/indexes/numeric/test_indexing.py,sha256=nDzkrokWvcmHkeHWjE8umPfxX4lR6AnQorAV7ppElCI,22761 +pandas/tests/indexes/numeric/test_join.py,sha256=OuSnYPH-jIM4UZRUKQ9NFxxd8Ot1HEP7KA3_ZpPX3Ks,15039 +pandas/tests/indexes/numeric/test_numeric.py,sha256=mEAFY8sSQdkVA0rJCTZb8cqjVAsTvL6mXzQSEXyxEgc,18586 +pandas/tests/indexes/numeric/test_setops.py,sha256=nO-3m7tb_ytjXx0Z8SqBkPSAnPVDz_PL3r2fzWtE7fg,5874 +pandas/tests/indexes/object/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/indexes/object/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/indexes/object/__pycache__/test_astype.cpython-312.pyc,, +pandas/tests/indexes/object/__pycache__/test_indexing.cpython-312.pyc,, +pandas/tests/indexes/object/test_astype.py,sha256=id6izR4uYcs_9q9ej3-_07n7uvIh8eC_qb9ZFVYjT0s,1060 +pandas/tests/indexes/object/test_indexing.py,sha256=pyc-tiHJlAMkIj67-wN3k079C_gtiNQVJWpxklRJ5l4,9410 +pandas/tests/indexes/period/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/indexes/period/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/indexes/period/__pycache__/test_constructors.cpython-312.pyc,, +pandas/tests/indexes/period/__pycache__/test_formats.cpython-312.pyc,, +pandas/tests/indexes/period/__pycache__/test_freq_attr.cpython-312.pyc,, +pandas/tests/indexes/period/__pycache__/test_indexing.cpython-312.pyc,, +pandas/tests/indexes/period/__pycache__/test_join.cpython-312.pyc,, +pandas/tests/indexes/period/__pycache__/test_monotonic.cpython-312.pyc,, +pandas/tests/indexes/period/__pycache__/test_partial_slicing.cpython-312.pyc,, +pandas/tests/indexes/period/__pycache__/test_period.cpython-312.pyc,, +pandas/tests/indexes/period/__pycache__/test_period_range.cpython-312.pyc,, +pandas/tests/indexes/period/__pycache__/test_pickle.cpython-312.pyc,, +pandas/tests/indexes/period/__pycache__/test_resolution.cpython-312.pyc,, +pandas/tests/indexes/period/__pycache__/test_scalar_compat.cpython-312.pyc,, +pandas/tests/indexes/period/__pycache__/test_searchsorted.cpython-312.pyc,, +pandas/tests/indexes/period/__pycache__/test_setops.cpython-312.pyc,, +pandas/tests/indexes/period/__pycache__/test_tools.cpython-312.pyc,, +pandas/tests/indexes/period/methods/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/indexes/period/methods/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/indexes/period/methods/__pycache__/test_asfreq.cpython-312.pyc,, +pandas/tests/indexes/period/methods/__pycache__/test_astype.cpython-312.pyc,, +pandas/tests/indexes/period/methods/__pycache__/test_factorize.cpython-312.pyc,, +pandas/tests/indexes/period/methods/__pycache__/test_fillna.cpython-312.pyc,, +pandas/tests/indexes/period/methods/__pycache__/test_insert.cpython-312.pyc,, +pandas/tests/indexes/period/methods/__pycache__/test_is_full.cpython-312.pyc,, +pandas/tests/indexes/period/methods/__pycache__/test_repeat.cpython-312.pyc,, +pandas/tests/indexes/period/methods/__pycache__/test_shift.cpython-312.pyc,, +pandas/tests/indexes/period/methods/__pycache__/test_to_timestamp.cpython-312.pyc,, +pandas/tests/indexes/period/methods/test_asfreq.py,sha256=PAqk5Zktd2OvLYwNoUGeXOh39HIIz9-5FqXnzrH6rtA,7080 +pandas/tests/indexes/period/methods/test_astype.py,sha256=k_xiGDPZOip3iw26LcB2E7UiRGHBZ39EOrsJxQoci6k,5469 +pandas/tests/indexes/period/methods/test_factorize.py,sha256=FXQh6VmGkuGkB2IAT4Y-2V5UaD2LCUNjQZ6amfBao80,1425 +pandas/tests/indexes/period/methods/test_fillna.py,sha256=jAYnaWGMuUaG993yxLwr1eT6J1ut43CcBaKds4Ce3-0,1125 +pandas/tests/indexes/period/methods/test_insert.py,sha256=JT9lBhbF90m2zRgIwarhPqPtVbrvkLiihZxO-4WHvTU,482 +pandas/tests/indexes/period/methods/test_is_full.py,sha256=RqIErBofIn5Ewh-MomVePHOn0hViZbe4laMC2vh8nPs,570 +pandas/tests/indexes/period/methods/test_repeat.py,sha256=1Nwn-ePYBEXWY4N9pFdHaqcZoKhWuinKdFJ-EjZtFlY,772 +pandas/tests/indexes/period/methods/test_shift.py,sha256=P7XDpMkLEYarH06RLBglFJKoGPkax4oLdiuI676KLek,4405 +pandas/tests/indexes/period/methods/test_to_timestamp.py,sha256=DCFf_Dt5cNsuSWJnYQAGfJrx1y2Z0GQiSTh0ajQJhjA,4888 +pandas/tests/indexes/period/test_constructors.py,sha256=LkRK-O65VdhX3EDQJHDdeGVQHfA6BQHT_PCi97M4xIs,27175 +pandas/tests/indexes/period/test_formats.py,sha256=DFLAMAPFzX2DI1iAAEjVY_nM9TuoYmCje3m7Q17A0EU,13259 +pandas/tests/indexes/period/test_freq_attr.py,sha256=KL1xaip5r7nY-3oLW16bmogfkYljsGJEJGKxn6w72Fo,646 +pandas/tests/indexes/period/test_indexing.py,sha256=jms77VvgkIgm0bSCHX-IMOtYuR0w2jd5uW3UoC2fm_4,27893 +pandas/tests/indexes/period/test_join.py,sha256=mwVL-OKx7tKTvMeSLNTh8jv6ViU6-NXcWr5O4hCmkOc,1835 +pandas/tests/indexes/period/test_monotonic.py,sha256=9Sb4WOykj99hn3MQOfm_MqYRxO5kADZt6OuakhSukp4,1258 +pandas/tests/indexes/period/test_partial_slicing.py,sha256=gXvS-qB0jPHYLKvjaP2rBW4p2UAm-ahM6KCCpT-u7ns,7433 +pandas/tests/indexes/period/test_period.py,sha256=91AawBQiPn_J3b6aG4sEzU24VaNJBTMn8shm_qkcE2g,7861 +pandas/tests/indexes/period/test_period_range.py,sha256=PB_VIuobx3NgnGOSmYZ0fyk79Zpoop22oYDP-TW-36Y,8979 +pandas/tests/indexes/period/test_pickle.py,sha256=l9A79u5PTcoa70g26wFPLTGnbvYpe76hPk1Iv334gb0,692 +pandas/tests/indexes/period/test_resolution.py,sha256=0TmnJeZCOaTWneeWA66DlxKgaUZJTfP0jKgLAY1jiyg,571 +pandas/tests/indexes/period/test_scalar_compat.py,sha256=CJuW0w6SdwDPtlk2Dl14g0ewuCcsIKPwtnmIMBSYEuc,1350 +pandas/tests/indexes/period/test_searchsorted.py,sha256=_u7DlvBnFx0_c8u3FIKYVOUcjlvN7p0gojLl9fZDkMQ,2604 +pandas/tests/indexes/period/test_setops.py,sha256=BcwDXv1-fnqOJLtzNqY2rEOye97Smyk2iXMnZx_IQE8,12547 +pandas/tests/indexes/period/test_tools.py,sha256=DFoxBsCYRWqodmNaDNPnQrZTTXiaSvwNZkwrybe7cl0,1361 +pandas/tests/indexes/ranges/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/indexes/ranges/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/indexes/ranges/__pycache__/test_constructors.cpython-312.pyc,, +pandas/tests/indexes/ranges/__pycache__/test_indexing.cpython-312.pyc,, +pandas/tests/indexes/ranges/__pycache__/test_join.cpython-312.pyc,, +pandas/tests/indexes/ranges/__pycache__/test_range.cpython-312.pyc,, +pandas/tests/indexes/ranges/__pycache__/test_setops.cpython-312.pyc,, +pandas/tests/indexes/ranges/test_constructors.py,sha256=ceX79fbjGyc5VNkmz29Q1N7WGXLj40BvTuz5PfNAw4I,5328 +pandas/tests/indexes/ranges/test_indexing.py,sha256=WCJFjnEzFIqQUv_i2cy-wHRQ4Txfi8uq4UBp20s4LRw,5171 +pandas/tests/indexes/ranges/test_join.py,sha256=lniHRyuEJWY7UGc0TpJ20xzUftn6BpYJbZQPo2I0dxE,6268 +pandas/tests/indexes/ranges/test_range.py,sha256=AaoOQ_PufgrgnOmS7ARYRydbdU1jsb6-DKu2oX52LuI,20937 +pandas/tests/indexes/ranges/test_setops.py,sha256=yuiXAKlZJ5c3LkjPzFltAKFQmhVqaBleiJ7nzXs4_eA,17534 +pandas/tests/indexes/test_any_index.py,sha256=QgHuIfkF_E3BFaNveFThmGAbrMpyR_UL-KQ0FhPFTyY,5131 +pandas/tests/indexes/test_base.py,sha256=Te-rph8bo9g9lXscCEsVesaT6SYu2OB_8jIBOVsSN5Q,60624 +pandas/tests/indexes/test_common.py,sha256=sFM-TarVMvFsE8VGYMxcKVikmviNNPJSDxwGGijBvPY,17899 +pandas/tests/indexes/test_datetimelike.py,sha256=6ue74lBTp8Es6PZoE1e_5Fo6k3j7Hq_HkpLnBjAYspE,5598 +pandas/tests/indexes/test_engines.py,sha256=rq3JzDXNc2mZS5ZC2mQLpTeydheOX9OLoq1FLR53wbI,6699 +pandas/tests/indexes/test_frozen.py,sha256=ocwmaa3rzwC7UrU2Ng6o9xxQgxc8lDnrlAhlGNvQE0E,3125 +pandas/tests/indexes/test_index_new.py,sha256=6tO12VIGCoGKN3uk1SlPdPXn5vQaOJ9tECa3oVyWC8c,14923 +pandas/tests/indexes/test_indexing.py,sha256=jwcq_dujP7z8tfnLqQ-G2NoJ0CxrDIa33jWwRLKk-8w,11309 +pandas/tests/indexes/test_numpy_compat.py,sha256=fnrc8fNrV7v3BRTY7Huu9cyrBw2aNUrv5i4UUEublFE,5776 +pandas/tests/indexes/test_old_base.py,sha256=NnfN4Wb-Ua9i1WlibiNBrsaI6-YCLi760URJjJJJD0Q,39926 +pandas/tests/indexes/test_setops.py,sha256=0q7sa-WferJk9rjM9Lz-J4bWTac3O8WK_yYd9OV2O_U,32938 +pandas/tests/indexes/test_subclass.py,sha256=lmZHuQ8OSlwP3xcR8Xy2Mfvjxp2ry2zUL4DO2P4hbnk,1058 +pandas/tests/indexes/timedeltas/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/indexes/timedeltas/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/indexes/timedeltas/__pycache__/test_arithmetic.cpython-312.pyc,, +pandas/tests/indexes/timedeltas/__pycache__/test_constructors.cpython-312.pyc,, +pandas/tests/indexes/timedeltas/__pycache__/test_delete.cpython-312.pyc,, +pandas/tests/indexes/timedeltas/__pycache__/test_formats.cpython-312.pyc,, +pandas/tests/indexes/timedeltas/__pycache__/test_freq_attr.cpython-312.pyc,, +pandas/tests/indexes/timedeltas/__pycache__/test_indexing.cpython-312.pyc,, +pandas/tests/indexes/timedeltas/__pycache__/test_join.cpython-312.pyc,, +pandas/tests/indexes/timedeltas/__pycache__/test_ops.cpython-312.pyc,, +pandas/tests/indexes/timedeltas/__pycache__/test_pickle.cpython-312.pyc,, +pandas/tests/indexes/timedeltas/__pycache__/test_scalar_compat.cpython-312.pyc,, +pandas/tests/indexes/timedeltas/__pycache__/test_searchsorted.cpython-312.pyc,, +pandas/tests/indexes/timedeltas/__pycache__/test_setops.cpython-312.pyc,, +pandas/tests/indexes/timedeltas/__pycache__/test_timedelta.cpython-312.pyc,, +pandas/tests/indexes/timedeltas/__pycache__/test_timedelta_range.cpython-312.pyc,, +pandas/tests/indexes/timedeltas/methods/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/indexes/timedeltas/methods/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/indexes/timedeltas/methods/__pycache__/test_astype.cpython-312.pyc,, +pandas/tests/indexes/timedeltas/methods/__pycache__/test_factorize.cpython-312.pyc,, +pandas/tests/indexes/timedeltas/methods/__pycache__/test_fillna.cpython-312.pyc,, +pandas/tests/indexes/timedeltas/methods/__pycache__/test_insert.cpython-312.pyc,, +pandas/tests/indexes/timedeltas/methods/__pycache__/test_repeat.cpython-312.pyc,, +pandas/tests/indexes/timedeltas/methods/__pycache__/test_shift.cpython-312.pyc,, +pandas/tests/indexes/timedeltas/methods/test_astype.py,sha256=gnbDreTvP4IrdYSzScM0jlpj9SJdzvTRt2sOL54hA8E,6129 +pandas/tests/indexes/timedeltas/methods/test_factorize.py,sha256=aqhhwRKZvfGxa3v09X5vZ7uBup8n5OjaUadfJpV6FoI,1292 +pandas/tests/indexes/timedeltas/methods/test_fillna.py,sha256=F7fBoEG-mnu16ypWYmK5wbIovQJKL0h86C1MzGkhPoE,597 +pandas/tests/indexes/timedeltas/methods/test_insert.py,sha256=fDYCuOIefgjNBJ7zhAUYniNVl5SltSs275XaNoL0S-s,4713 +pandas/tests/indexes/timedeltas/methods/test_repeat.py,sha256=vPcNBkY4H2RxsykW1bjTg-FSlTlQ2H1yLb-ZsYffsEg,926 +pandas/tests/indexes/timedeltas/methods/test_shift.py,sha256=MzVVupnLHEvuwlVCn6mR7LQ9pLeNiWM2lWwNlIwoo98,2756 +pandas/tests/indexes/timedeltas/test_arithmetic.py,sha256=YocDQIovXnrpXEzz3Ac-3l2PdGaDf2_sF8UPcLVF1Z8,1561 +pandas/tests/indexes/timedeltas/test_constructors.py,sha256=atU_oy_1oyUtMWRg47A94j3S4nPJbDRRgUhDCW6TO6M,10600 +pandas/tests/indexes/timedeltas/test_delete.py,sha256=-5uYhDUCD55zv5I3Z8aVFEBzdChSWtbPNSP05nqUEiA,2398 +pandas/tests/indexes/timedeltas/test_formats.py,sha256=4yUVmL5NEabGi9AXPA5isM3c4F3Rgslk4zqcfS-ua3s,3807 +pandas/tests/indexes/timedeltas/test_freq_attr.py,sha256=gYGl9w9UdtcfN26KUx1QyY4mjh6A0m4Csk3gsCIcdos,2176 +pandas/tests/indexes/timedeltas/test_indexing.py,sha256=9C-U4bwBd7D1GnaKgi51Jlgod7KhONIlgrA9t7jSQ80,12160 +pandas/tests/indexes/timedeltas/test_join.py,sha256=7JUirtgNGJMRL1-k2gekrvondwYuIVvuI2548v4nfIo,1396 +pandas/tests/indexes/timedeltas/test_ops.py,sha256=nfGyNJvNy7_jmWebKjevLKhyAMNvI5jytkZTNlpEC-g,393 +pandas/tests/indexes/timedeltas/test_pickle.py,sha256=QesBThE22Ba17eUdG21lWNqPRvBhyupLnPsXueLazHw,302 +pandas/tests/indexes/timedeltas/test_scalar_compat.py,sha256=hldSSTxREuBBuLAhvLTjX7FUmJ9DzcJxmMqzaClnErg,4573 +pandas/tests/indexes/timedeltas/test_searchsorted.py,sha256=kCE0PkuPk1CxkZHODe3aZ54V-Hc1AiHkyNNVjN5REIM,967 +pandas/tests/indexes/timedeltas/test_setops.py,sha256=Y6OwY82XC1hDgME55I_9q_UzGZdKhAhI1sxXS8bzr1w,9503 +pandas/tests/indexes/timedeltas/test_timedelta.py,sha256=UxobS6Dhfoqy4bnoAuMlLO8acpNrCDGsYWl4vGbDO8Q,1934 +pandas/tests/indexes/timedeltas/test_timedelta_range.py,sha256=tZqv_j045dPD3K2sbqdhdvEb-qE7szf9S7DJNX5Ri3o,6220 +pandas/tests/indexing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/indexing/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/indexing/__pycache__/common.cpython-312.pyc,, +pandas/tests/indexing/__pycache__/conftest.cpython-312.pyc,, +pandas/tests/indexing/__pycache__/test_at.cpython-312.pyc,, +pandas/tests/indexing/__pycache__/test_categorical.cpython-312.pyc,, +pandas/tests/indexing/__pycache__/test_chaining_and_caching.cpython-312.pyc,, +pandas/tests/indexing/__pycache__/test_check_indexer.cpython-312.pyc,, +pandas/tests/indexing/__pycache__/test_coercion.cpython-312.pyc,, +pandas/tests/indexing/__pycache__/test_datetime.cpython-312.pyc,, +pandas/tests/indexing/__pycache__/test_floats.cpython-312.pyc,, +pandas/tests/indexing/__pycache__/test_iat.cpython-312.pyc,, +pandas/tests/indexing/__pycache__/test_iloc.cpython-312.pyc,, +pandas/tests/indexing/__pycache__/test_indexers.cpython-312.pyc,, +pandas/tests/indexing/__pycache__/test_indexing.cpython-312.pyc,, +pandas/tests/indexing/__pycache__/test_loc.cpython-312.pyc,, +pandas/tests/indexing/__pycache__/test_na_indexing.cpython-312.pyc,, +pandas/tests/indexing/__pycache__/test_partial.cpython-312.pyc,, +pandas/tests/indexing/__pycache__/test_scalar.cpython-312.pyc,, +pandas/tests/indexing/common.py,sha256=LtCDO4TeMhLWAiTGiJET3YP8RO6T3OQqmdpJ8JH391g,1021 +pandas/tests/indexing/conftest.py,sha256=9C84qvdnHzbM5C0KIVw3ueQhHzuUMoAlw07dVJqCAmQ,2677 +pandas/tests/indexing/interval/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/indexing/interval/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/indexing/interval/__pycache__/test_interval.cpython-312.pyc,, +pandas/tests/indexing/interval/__pycache__/test_interval_new.cpython-312.pyc,, +pandas/tests/indexing/interval/test_interval.py,sha256=pB8gTluRFlmZZVCcRDtjXUygjSJegI3YRYI3XIPgsy0,7482 +pandas/tests/indexing/interval/test_interval_new.py,sha256=IkPyCHTHvwyHf25ljz4o6Q0CnHVpnLD2jVUF3TbtLS4,7976 +pandas/tests/indexing/multiindex/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/indexing/multiindex/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/indexing/multiindex/__pycache__/test_chaining_and_caching.cpython-312.pyc,, +pandas/tests/indexing/multiindex/__pycache__/test_datetime.cpython-312.pyc,, +pandas/tests/indexing/multiindex/__pycache__/test_getitem.cpython-312.pyc,, +pandas/tests/indexing/multiindex/__pycache__/test_iloc.cpython-312.pyc,, +pandas/tests/indexing/multiindex/__pycache__/test_indexing_slow.cpython-312.pyc,, +pandas/tests/indexing/multiindex/__pycache__/test_loc.cpython-312.pyc,, +pandas/tests/indexing/multiindex/__pycache__/test_multiindex.cpython-312.pyc,, +pandas/tests/indexing/multiindex/__pycache__/test_partial.cpython-312.pyc,, +pandas/tests/indexing/multiindex/__pycache__/test_setitem.cpython-312.pyc,, +pandas/tests/indexing/multiindex/__pycache__/test_slice.cpython-312.pyc,, +pandas/tests/indexing/multiindex/__pycache__/test_sorted.cpython-312.pyc,, +pandas/tests/indexing/multiindex/test_chaining_and_caching.py,sha256=hPcMvvPamIHI8AeSL7xvqs3eOT-5ONMjLy2XK2Mgt4Q,2922 +pandas/tests/indexing/multiindex/test_datetime.py,sha256=tl1yr3h50R0t7uvwTcfsRW-jt1n9vsqf4BWp4dNTdd8,1234 +pandas/tests/indexing/multiindex/test_getitem.py,sha256=wNftnfXLfiyjduEYeq8MSfE8K1OKaZG0WpmKWBqWk6o,13230 +pandas/tests/indexing/multiindex/test_iloc.py,sha256=G2CUPRhd5pRImZpH0uOVIPid7fzB4OuJZjH8arQMrE0,4918 +pandas/tests/indexing/multiindex/test_indexing_slow.py,sha256=nMfW1LQn7YlJauNceeR-uo_yPxRG2E8hcbgqTBMxaH4,3335 +pandas/tests/indexing/multiindex/test_loc.py,sha256=aVvEHILvJS7cYhNKEka_QiJlEcPim76s29FQlNbFYRw,32795 +pandas/tests/indexing/multiindex/test_multiindex.py,sha256=bIihrEIUXO1s8wAnKof9ATiwqAvwuLIWzE_oZlMxlOs,8065 +pandas/tests/indexing/multiindex/test_partial.py,sha256=05MXMJmAevJ31bqHIVikEL14x6s7IUASxLaw62w44mQ,8858 +pandas/tests/indexing/multiindex/test_setitem.py,sha256=cn0FPeh4oKRpI0o01tFx24VOoNQr90GCiKIMo8cBaE0,19840 +pandas/tests/indexing/multiindex/test_slice.py,sha256=7JcyCAq91OpruPy1awmdQmblxPzQF4UrnUN2XHrahbY,27104 +pandas/tests/indexing/multiindex/test_sorted.py,sha256=xCdmS_0DBN2yoTVcSB-x6Ecwcw93p6erw3bTiU6_J3s,5192 +pandas/tests/indexing/test_at.py,sha256=Vnv3lP2MkIjLvaj5LTsPvZN_GTsaGDl7c4dzdHbEZBI,8194 +pandas/tests/indexing/test_categorical.py,sha256=JPn8mSo7FSTuFaHzpiELgVBwTsqmjISLnGoxloy6SjU,19699 +pandas/tests/indexing/test_chaining_and_caching.py,sha256=-T0e9bh8ktgrHrB8CXd-MjcvLnckuiSSyBC8Cr6q-uE,23479 +pandas/tests/indexing/test_check_indexer.py,sha256=tfr2a1h6uokN2MJDE7TKiZ0iRaHvfSWPPC-86RqaaDU,3159 +pandas/tests/indexing/test_coercion.py,sha256=RxeenIaFXLT9bPDEnV7zfpcPv5UC5QMVjp-hb0Igv9g,32629 +pandas/tests/indexing/test_datetime.py,sha256=Gj5Fo4ywd4md3H-zbk11bSbNEmktbnlHORVRzBfN0oE,5703 +pandas/tests/indexing/test_floats.py,sha256=KG_T_POIEc5nnVL7Zi8zSwamhahbfjUxBYrC3ilRlEI,20603 +pandas/tests/indexing/test_iat.py,sha256=cQrMr1MYQv5LZS5E34NumdqqeK8hvcN6duLRTaeZ6Go,1492 +pandas/tests/indexing/test_iloc.py,sha256=Y6LdDIOVnWoLlcqVI8eLoRwS4TgpNNRipr0Q-90FbM0,51335 +pandas/tests/indexing/test_indexers.py,sha256=agN_MCo403fOvqapKi_WYQli9AkDFAk4TDB5XpbJ8js,1661 +pandas/tests/indexing/test_indexing.py,sha256=a3ChWUgSad-7yXrvRU4wr6lY5Oqt24308Z_kMkqFxxg,40042 +pandas/tests/indexing/test_loc.py,sha256=mV2746VJwKa8-Rwn4iK-nvDl_CuMcHoDPOxP3wfJ5V8,119354 +pandas/tests/indexing/test_na_indexing.py,sha256=Ek_7A7ctm_WB-32NePbODbQ5LDMZBAmCvDgPKbIUOcg,2322 +pandas/tests/indexing/test_partial.py,sha256=f32wptlfPdvAdRSgt2N5USZQdtNt-GM31QoQpJSpXeA,25256 +pandas/tests/indexing/test_scalar.py,sha256=BuLsr0F1OA4IeA816BzuLFiSNGppPoALpieV2_8Nfg8,9643 +pandas/tests/interchange/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/interchange/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/interchange/__pycache__/test_impl.cpython-312.pyc,, +pandas/tests/interchange/__pycache__/test_spec_conformance.cpython-312.pyc,, +pandas/tests/interchange/__pycache__/test_utils.cpython-312.pyc,, +pandas/tests/interchange/test_impl.py,sha256=Exl81IoyGGPYt1Nz2ipJQ79cDXqlVZnJuFhCuIfKt5Q,19878 +pandas/tests/interchange/test_spec_conformance.py,sha256=JnE2kQOLr4EjUCH6Nzc1fCEXhbZ52WzKbioW6f6EVxo,5593 +pandas/tests/interchange/test_utils.py,sha256=15liIDJirQDoP7TxxQkmZJ9gCAVNCd2BwShW_GlwL2A,2965 +pandas/tests/internals/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/internals/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/internals/__pycache__/test_api.cpython-312.pyc,, +pandas/tests/internals/__pycache__/test_internals.cpython-312.pyc,, +pandas/tests/internals/__pycache__/test_managers.cpython-312.pyc,, +pandas/tests/internals/test_api.py,sha256=7s-n3jyp-e0ikVxkIqxf3xRtxk3aBV4h5FsnMIcStMY,2166 +pandas/tests/internals/test_internals.py,sha256=jeWXqpUIEnygO0BwnHdQZNsolBusoxvRSNndgaCnuUE,49657 +pandas/tests/internals/test_managers.py,sha256=uIuBmkOCjbFuGGNOodZ7ITijw4CfsG4aOUqRLCEfg-s,3556 +pandas/tests/io/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/io/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/io/__pycache__/conftest.cpython-312.pyc,, +pandas/tests/io/__pycache__/generate_legacy_storage_files.cpython-312.pyc,, +pandas/tests/io/__pycache__/test_clipboard.cpython-312.pyc,, +pandas/tests/io/__pycache__/test_common.cpython-312.pyc,, +pandas/tests/io/__pycache__/test_compression.cpython-312.pyc,, +pandas/tests/io/__pycache__/test_feather.cpython-312.pyc,, +pandas/tests/io/__pycache__/test_fsspec.cpython-312.pyc,, +pandas/tests/io/__pycache__/test_gbq.cpython-312.pyc,, +pandas/tests/io/__pycache__/test_gcs.cpython-312.pyc,, +pandas/tests/io/__pycache__/test_html.cpython-312.pyc,, +pandas/tests/io/__pycache__/test_http_headers.cpython-312.pyc,, +pandas/tests/io/__pycache__/test_orc.cpython-312.pyc,, +pandas/tests/io/__pycache__/test_parquet.cpython-312.pyc,, +pandas/tests/io/__pycache__/test_pickle.cpython-312.pyc,, +pandas/tests/io/__pycache__/test_s3.cpython-312.pyc,, +pandas/tests/io/__pycache__/test_spss.cpython-312.pyc,, +pandas/tests/io/__pycache__/test_sql.cpython-312.pyc,, +pandas/tests/io/__pycache__/test_stata.cpython-312.pyc,, +pandas/tests/io/conftest.py,sha256=F72gAcQcyFdyv07CQkjbTT8dOkXSVHtwDQaIHFTB9xY,6406 +pandas/tests/io/excel/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/io/excel/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/io/excel/__pycache__/test_odf.cpython-312.pyc,, +pandas/tests/io/excel/__pycache__/test_odswriter.cpython-312.pyc,, +pandas/tests/io/excel/__pycache__/test_openpyxl.cpython-312.pyc,, +pandas/tests/io/excel/__pycache__/test_readers.cpython-312.pyc,, +pandas/tests/io/excel/__pycache__/test_style.cpython-312.pyc,, +pandas/tests/io/excel/__pycache__/test_writers.cpython-312.pyc,, +pandas/tests/io/excel/__pycache__/test_xlrd.cpython-312.pyc,, +pandas/tests/io/excel/__pycache__/test_xlsxwriter.cpython-312.pyc,, +pandas/tests/io/excel/test_odf.py,sha256=DoE6DfjKkIKGJtRUG8uvBNNGBOvoqVZnL8Jr_I1vOLQ,1999 +pandas/tests/io/excel/test_odswriter.py,sha256=2SmPARRnXiOAstiUaEFaVfGu2kVQ5vVHGODlozrlUFI,3268 +pandas/tests/io/excel/test_openpyxl.py,sha256=wnADQLARvjB4BMYgd2fMs5jsvYm8DQvqFngJVnhSH1Q,15227 +pandas/tests/io/excel/test_readers.py,sha256=Qn8L41hKdO_2xkpTNBi2eqpUx0OAV3BfqeUKoSOn0aM,63198 +pandas/tests/io/excel/test_style.py,sha256=mQ7roFc4ZfBfrjc4Das0lNnYXIcV1cO1AOuXVRw1Dqw,11284 +pandas/tests/io/excel/test_writers.py,sha256=udzFSri-07QXgV0v-xHJ3Cx8wKvJJaoCByyAwwIg6gM,54899 +pandas/tests/io/excel/test_xlrd.py,sha256=e5QrByVFVm6rEZbdSifYBBCY-czTzWZZ5y7OyfrPksw,1977 +pandas/tests/io/excel/test_xlsxwriter.py,sha256=DUmibvRcUD6O2OcD_YcMymQPvMgkckIH92NjYsamyOE,2773 +pandas/tests/io/formats/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/io/formats/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/io/formats/__pycache__/test_console.cpython-312.pyc,, +pandas/tests/io/formats/__pycache__/test_css.cpython-312.pyc,, +pandas/tests/io/formats/__pycache__/test_eng_formatting.cpython-312.pyc,, +pandas/tests/io/formats/__pycache__/test_format.cpython-312.pyc,, +pandas/tests/io/formats/__pycache__/test_ipython_compat.cpython-312.pyc,, +pandas/tests/io/formats/__pycache__/test_printing.cpython-312.pyc,, +pandas/tests/io/formats/__pycache__/test_to_csv.cpython-312.pyc,, +pandas/tests/io/formats/__pycache__/test_to_excel.cpython-312.pyc,, +pandas/tests/io/formats/__pycache__/test_to_html.cpython-312.pyc,, +pandas/tests/io/formats/__pycache__/test_to_latex.cpython-312.pyc,, +pandas/tests/io/formats/__pycache__/test_to_markdown.cpython-312.pyc,, +pandas/tests/io/formats/__pycache__/test_to_string.cpython-312.pyc,, +pandas/tests/io/formats/style/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/io/formats/style/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/io/formats/style/__pycache__/test_bar.cpython-312.pyc,, +pandas/tests/io/formats/style/__pycache__/test_exceptions.cpython-312.pyc,, +pandas/tests/io/formats/style/__pycache__/test_format.cpython-312.pyc,, +pandas/tests/io/formats/style/__pycache__/test_highlight.cpython-312.pyc,, +pandas/tests/io/formats/style/__pycache__/test_html.cpython-312.pyc,, +pandas/tests/io/formats/style/__pycache__/test_matplotlib.cpython-312.pyc,, +pandas/tests/io/formats/style/__pycache__/test_non_unique.cpython-312.pyc,, +pandas/tests/io/formats/style/__pycache__/test_style.cpython-312.pyc,, +pandas/tests/io/formats/style/__pycache__/test_to_latex.cpython-312.pyc,, +pandas/tests/io/formats/style/__pycache__/test_to_string.cpython-312.pyc,, +pandas/tests/io/formats/style/__pycache__/test_tooltip.cpython-312.pyc,, +pandas/tests/io/formats/style/test_bar.py,sha256=czy40UZacoi9uyzM-w-AC5lMu2z2cwKwyE9Ml0i6x_k,12014 +pandas/tests/io/formats/style/test_exceptions.py,sha256=qm62Nu_E61TOrGXzxMSYm5Ciqm7qKhCFaTDP0QJmjJo,1002 +pandas/tests/io/formats/style/test_format.py,sha256=9siaXSHvCrA-YEuRI0-zun0gwQf2fVZwSPMIrb7CLTE,21154 +pandas/tests/io/formats/style/test_highlight.py,sha256=p2vRhU8aefAfmqLptxNO4XYbrVsccERvFQRd1OowC10,7003 +pandas/tests/io/formats/style/test_html.py,sha256=FvW0Zh6U8CkOKo0Plvz8W-udOgsczg9qawyVq-xzKqc,32702 +pandas/tests/io/formats/style/test_matplotlib.py,sha256=KPTvs_DbJlT5u7xQiQW3Ct-0jmpFHuah_lfQgZkiuQw,11649 +pandas/tests/io/formats/style/test_non_unique.py,sha256=JG_rE5A5Zk5exlfivZHnOI3Upzm8dJjmKKHkwEje4LQ,4366 +pandas/tests/io/formats/style/test_style.py,sha256=x7r8-nhnYdifw_PjopT0a4t99MTGzlOBv-g38HOHxik,58095 +pandas/tests/io/formats/style/test_to_latex.py,sha256=EbsBCluJ-2eVLSxXHgLo6Uus6VsnrbzqO9sYaRuewgs,33008 +pandas/tests/io/formats/style/test_to_string.py,sha256=w1GvLm3FtKQd9t2nwN3vF55X5f0GQKGCGXpYFZxITpA,1910 +pandas/tests/io/formats/style/test_tooltip.py,sha256=GMqwXrXi9Ppp0khfZHEwgeRqahwju5U2iIhZan3ndZE,2899 +pandas/tests/io/formats/test_console.py,sha256=jAk1wudhPiLBhhtydTNRlZ43961LqFu3uYt6cVA_jV0,2435 +pandas/tests/io/formats/test_css.py,sha256=YFHK3UFe2jcnz6AhmOFb7ZU1jd5Y_LYxIx5PBrJXNLQ,8669 +pandas/tests/io/formats/test_eng_formatting.py,sha256=QqFZJMUBVnU5SpZB63tCOHX3CqZbjgesOZc6nxbhp4c,8454 +pandas/tests/io/formats/test_format.py,sha256=10Nmscrr_GplWPa9t7nAluixTS73AqJfCNbiX4Kf5HI,83181 +pandas/tests/io/formats/test_ipython_compat.py,sha256=pRAOUIZ3Vsb2LVYywzk30d834GzqLH9N8kjTGlf2MXc,3055 +pandas/tests/io/formats/test_printing.py,sha256=hLBoT3FE7J2VjxCJIAS_N24g6pMoQcyQphGTnwt0Ehc,4499 +pandas/tests/io/formats/test_to_csv.py,sha256=mThYTrnKefL4fWiqsLmJP9nsJcKx9ejdPNXndW6ADzo,27541 +pandas/tests/io/formats/test_to_excel.py,sha256=ecNeSrVd2mSPsdIqm3lM911b4mPwLIVkoz3MnJFZE3g,15320 +pandas/tests/io/formats/test_to_html.py,sha256=elbKQSMvV8p3qWEFVFA_nneSjdXl432QYDlha1cGVGw,38699 +pandas/tests/io/formats/test_to_latex.py,sha256=ka8kOxa7dLP3wQf7b4dGHLNP9lc6TI1MCepsLSfYoTQ,41660 +pandas/tests/io/formats/test_to_markdown.py,sha256=2DUY7KrRVUu_OU6q4biW8rNFEINN6fPSkqs8VzY8rlE,2757 +pandas/tests/io/formats/test_to_string.py,sha256=aCcTOFjwdLQbEZ3JLEvlUySigpY-M4Gp8pV8ue-S0Ig,39371 +pandas/tests/io/generate_legacy_storage_files.py,sha256=c-J8fZLOyR7FRP8ijI6WcJrqequzwHJBZPs_1xC3bHI,9853 +pandas/tests/io/json/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/io/json/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/io/json/__pycache__/conftest.cpython-312.pyc,, +pandas/tests/io/json/__pycache__/test_compression.cpython-312.pyc,, +pandas/tests/io/json/__pycache__/test_deprecated_kwargs.cpython-312.pyc,, +pandas/tests/io/json/__pycache__/test_json_table_schema.cpython-312.pyc,, +pandas/tests/io/json/__pycache__/test_json_table_schema_ext_dtype.cpython-312.pyc,, +pandas/tests/io/json/__pycache__/test_normalize.cpython-312.pyc,, +pandas/tests/io/json/__pycache__/test_pandas.cpython-312.pyc,, +pandas/tests/io/json/__pycache__/test_readlines.cpython-312.pyc,, +pandas/tests/io/json/__pycache__/test_ujson.cpython-312.pyc,, +pandas/tests/io/json/conftest.py,sha256=Zp83o90PvZ56MbhNRr1NZEPTpho7jRHcLYiEA9R_BZw,205 +pandas/tests/io/json/test_compression.py,sha256=PNaQlGwVdCL8K6ujRinmALn9O28tNZbxgelGcK-6MSo,4506 +pandas/tests/io/json/test_deprecated_kwargs.py,sha256=DKuEh2V2IkJOu-BnurWvax8Mq5EcQHtG-K-zncGZRpo,690 +pandas/tests/io/json/test_json_table_schema.py,sha256=lWCSq6HZNqPpjffejfkqc9JKjhRPUUVuLPWyWTyXDG4,30676 +pandas/tests/io/json/test_json_table_schema_ext_dtype.py,sha256=mTwJ_IpOBewvrLU98eLo-_yibYtOqD64LKLI_WIr5n0,9500 +pandas/tests/io/json/test_normalize.py,sha256=eOQoJQBGjAqFcswdNBipHoGMGBgLiwLFNIzTuZ5XSkI,30816 +pandas/tests/io/json/test_pandas.py,sha256=JlBn9DVzXvHbDKPYfyF6dt8njaNOeQ6mkR3vdJFUM_I,78323 +pandas/tests/io/json/test_readlines.py,sha256=NaIeCB9w7iM_Ptamx4IoLMRwIG9eUQxsTJpU2cBB5y0,18819 +pandas/tests/io/json/test_ujson.py,sha256=UYh87hxO7ySZ60Q8ycDjbEqzcbBD51mV9qIlMCDA_Fc,36424 +pandas/tests/io/parser/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/io/parser/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/io/parser/__pycache__/conftest.cpython-312.pyc,, +pandas/tests/io/parser/__pycache__/test_c_parser_only.cpython-312.pyc,, +pandas/tests/io/parser/__pycache__/test_comment.cpython-312.pyc,, +pandas/tests/io/parser/__pycache__/test_compression.cpython-312.pyc,, +pandas/tests/io/parser/__pycache__/test_concatenate_chunks.cpython-312.pyc,, +pandas/tests/io/parser/__pycache__/test_converters.cpython-312.pyc,, +pandas/tests/io/parser/__pycache__/test_dialect.cpython-312.pyc,, +pandas/tests/io/parser/__pycache__/test_encoding.cpython-312.pyc,, +pandas/tests/io/parser/__pycache__/test_header.cpython-312.pyc,, +pandas/tests/io/parser/__pycache__/test_index_col.cpython-312.pyc,, +pandas/tests/io/parser/__pycache__/test_mangle_dupes.cpython-312.pyc,, +pandas/tests/io/parser/__pycache__/test_multi_thread.cpython-312.pyc,, +pandas/tests/io/parser/__pycache__/test_na_values.cpython-312.pyc,, +pandas/tests/io/parser/__pycache__/test_network.cpython-312.pyc,, +pandas/tests/io/parser/__pycache__/test_parse_dates.cpython-312.pyc,, +pandas/tests/io/parser/__pycache__/test_python_parser_only.cpython-312.pyc,, +pandas/tests/io/parser/__pycache__/test_quoting.cpython-312.pyc,, +pandas/tests/io/parser/__pycache__/test_read_fwf.cpython-312.pyc,, +pandas/tests/io/parser/__pycache__/test_skiprows.cpython-312.pyc,, +pandas/tests/io/parser/__pycache__/test_textreader.cpython-312.pyc,, +pandas/tests/io/parser/__pycache__/test_unsupported.cpython-312.pyc,, +pandas/tests/io/parser/__pycache__/test_upcast.cpython-312.pyc,, +pandas/tests/io/parser/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/io/parser/common/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/io/parser/common/__pycache__/test_chunksize.cpython-312.pyc,, +pandas/tests/io/parser/common/__pycache__/test_common_basic.cpython-312.pyc,, +pandas/tests/io/parser/common/__pycache__/test_data_list.cpython-312.pyc,, +pandas/tests/io/parser/common/__pycache__/test_decimal.cpython-312.pyc,, +pandas/tests/io/parser/common/__pycache__/test_file_buffer_url.cpython-312.pyc,, +pandas/tests/io/parser/common/__pycache__/test_float.cpython-312.pyc,, +pandas/tests/io/parser/common/__pycache__/test_index.cpython-312.pyc,, +pandas/tests/io/parser/common/__pycache__/test_inf.cpython-312.pyc,, +pandas/tests/io/parser/common/__pycache__/test_ints.cpython-312.pyc,, +pandas/tests/io/parser/common/__pycache__/test_iterator.cpython-312.pyc,, +pandas/tests/io/parser/common/__pycache__/test_read_errors.cpython-312.pyc,, +pandas/tests/io/parser/common/__pycache__/test_verbose.cpython-312.pyc,, +pandas/tests/io/parser/common/test_chunksize.py,sha256=IEeKcV5GYLee7U0ACIyDJtR-Q176X9zfRg7YqrdeWx8,11103 +pandas/tests/io/parser/common/test_common_basic.py,sha256=7RdM9Bh71Qmpf2bkUPu2MiHDwdUb2NJIwmxK3g1QEBc,30872 +pandas/tests/io/parser/common/test_data_list.py,sha256=XTWzTbtaLRGFdrjfRTJH3TTedD8Y0uCWRzji1qnrdk4,2228 +pandas/tests/io/parser/common/test_decimal.py,sha256=6WZy1C7G2vNpSo165GZAoRFGiy9OMgKygAIEYNalQ-Y,1932 +pandas/tests/io/parser/common/test_file_buffer_url.py,sha256=Gr7jx2idDJrMKF6tdwe-hxd9ewWxRokSAlFYjAeRgfM,14007 +pandas/tests/io/parser/common/test_float.py,sha256=5XM0Cndv31L4_7ER2MOB-Bnk9_GELTpakFp1-dNRjyM,2582 +pandas/tests/io/parser/common/test_index.py,sha256=kNF9uReFUMb4YaK9Cz10zUWnUXxT3OpZIhiy1fZTu_4,8234 +pandas/tests/io/parser/common/test_inf.py,sha256=yXUF6DrDhiPKEfEXJLnb71bZnycbo4CKXkl14Vyv3QY,2114 +pandas/tests/io/parser/common/test_ints.py,sha256=K49T03jXs77ktsxIFFQqBisPI3z042A8GATZcn1Tq44,7243 +pandas/tests/io/parser/common/test_iterator.py,sha256=FljWxY67UNOCedqg_as_nY4GtkU4HDwqwgpLkxU00Aw,3702 +pandas/tests/io/parser/common/test_read_errors.py,sha256=Aas1e5CM0ohMBXNQ2tSZao7jZbWTk9LA85FglJ8CRLE,9592 +pandas/tests/io/parser/common/test_verbose.py,sha256=kil5N51khhQifV9az-x2ijMr3wGtddKrU5oAbr0b1hs,2339 +pandas/tests/io/parser/conftest.py,sha256=PW00EmO-nd14_zUV7Uf8EO5tezaI-_zFcn2jP-Msxow,8725 +pandas/tests/io/parser/dtypes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/io/parser/dtypes/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/io/parser/dtypes/__pycache__/test_categorical.cpython-312.pyc,, +pandas/tests/io/parser/dtypes/__pycache__/test_dtypes_basic.cpython-312.pyc,, +pandas/tests/io/parser/dtypes/__pycache__/test_empty.cpython-312.pyc,, +pandas/tests/io/parser/dtypes/test_categorical.py,sha256=H8HO6IYwkJryJV87hKep0rtyx4XmXAHh1ICuprkmYjM,9836 +pandas/tests/io/parser/dtypes/test_dtypes_basic.py,sha256=9IoehDR7qPsZOIAHDcsX4ekahkeU8MEkpeYMqb_fwAg,18502 +pandas/tests/io/parser/dtypes/test_empty.py,sha256=bFuG8P_48stM0rEB8J0pF-sRl3kezS-9wB3fycgCjFo,5258 +pandas/tests/io/parser/test_c_parser_only.py,sha256=qSNbMmaYlQG4ddezez8HkcFtSEtH8BCnL7UM1FwANbU,20534 +pandas/tests/io/parser/test_comment.py,sha256=QO0E262p5tnOpm9oxqTO1rwl0KU-mKMP_jydlahyFMM,7560 +pandas/tests/io/parser/test_compression.py,sha256=hW1GxllxvM8sUQhmTVibkkqdj0JcAAR9b7nKCxuXblk,6403 +pandas/tests/io/parser/test_concatenate_chunks.py,sha256=RD1MUklgLBtBNvJu5J92cVZbrO3n38UzdQvh4BAvAqI,1128 +pandas/tests/io/parser/test_converters.py,sha256=iA5iv_5YSfwloTccNfdgE-9QO-Zm9Z_taDspYeRvAF4,7453 +pandas/tests/io/parser/test_dialect.py,sha256=tgsdnhEkYBtjIKd-9BKAyQ8ATTSivnzIkiWiuLi513M,5844 +pandas/tests/io/parser/test_encoding.py,sha256=Og-q60V-nd-8xl5VBWDPtYqxGeemrs8rYCoCCWKdjmc,10782 +pandas/tests/io/parser/test_header.py,sha256=zvSu-S51vJaIGPOdZgdC2IeHd2Y_1FTId-QGJc_7BWU,21029 +pandas/tests/io/parser/test_index_col.py,sha256=deEpoBpT2KvbrcUgpnSmylzdpdAY5uhPtPRKzhJyUcE,11501 +pandas/tests/io/parser/test_mangle_dupes.py,sha256=Xwci86pIvocxp6Gc0hT2bk0QahFZwTQdhKewCrC-W38,5390 +pandas/tests/io/parser/test_multi_thread.py,sha256=x40FWVAiCprn9T83Tu7cVaiUcGIcSSOgp7lauIUsdjo,4315 +pandas/tests/io/parser/test_na_values.py,sha256=P4mcmVpprWhd0TsFdABCJnNPQrkrLLFwIrpKaHe8bJo,22138 +pandas/tests/io/parser/test_network.py,sha256=8bNvzZHJ6r_m1WEJ7qt6fZtUbxLkxWP_aGqGnrtk_Po,12319 +pandas/tests/io/parser/test_parse_dates.py,sha256=lnel1CZGmZqmRdTG5ltGwVKl9GChQU07v8uyG25ci1k,69737 +pandas/tests/io/parser/test_python_parser_only.py,sha256=wkXRjsAKI6pZnvhetAyLanvn_0pjIxQBALYDHazXrf8,15897 +pandas/tests/io/parser/test_quoting.py,sha256=7g4XLvgjtkRf9qgl7eksjwJ-N42e4dq-nCEPWP9hS9g,6244 +pandas/tests/io/parser/test_read_fwf.py,sha256=DM-YTi6mkb6dup72srdA4GTIx6nzDiBoQqIGfwa1BQg,30107 +pandas/tests/io/parser/test_skiprows.py,sha256=D0dm01x-53YqSXXvj1jczRV5SWEDNkNP87tquehyn9w,9457 +pandas/tests/io/parser/test_textreader.py,sha256=R_yeB-k6g45i6ZTQ-PdF8DIJYdodhH059OGrRdM8IOM,10672 +pandas/tests/io/parser/test_unsupported.py,sha256=149HYApTOEJP9xEXuXuncyS2zq_lpF_AyBfu_SIjjes,7986 +pandas/tests/io/parser/test_upcast.py,sha256=XEjHUvgExlKwxTCSjSfWMxjwge0HeW9q2BMIQGuxfTk,3141 +pandas/tests/io/parser/usecols/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/io/parser/usecols/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/io/parser/usecols/__pycache__/test_parse_dates.cpython-312.pyc,, +pandas/tests/io/parser/usecols/__pycache__/test_strings.cpython-312.pyc,, +pandas/tests/io/parser/usecols/__pycache__/test_usecols_basic.cpython-312.pyc,, +pandas/tests/io/parser/usecols/test_parse_dates.py,sha256=7PYxerT3Eok6kVV6dfU2e-qlBpde-gfCGMg1NEht8cM,5469 +pandas/tests/io/parser/usecols/test_strings.py,sha256=-ZUBWSpxMgoxqRfGAa0mgb5motUoKveF06V9LUH-nQg,2588 +pandas/tests/io/parser/usecols/test_usecols_basic.py,sha256=BKr0EIu8g1aLiF6a_g61zF2NHPVY8Cl6CRcNnHLQ_4o,17646 +pandas/tests/io/pytables/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/io/pytables/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/io/pytables/__pycache__/common.cpython-312.pyc,, +pandas/tests/io/pytables/__pycache__/conftest.cpython-312.pyc,, +pandas/tests/io/pytables/__pycache__/test_append.cpython-312.pyc,, +pandas/tests/io/pytables/__pycache__/test_categorical.cpython-312.pyc,, +pandas/tests/io/pytables/__pycache__/test_compat.cpython-312.pyc,, +pandas/tests/io/pytables/__pycache__/test_complex.cpython-312.pyc,, +pandas/tests/io/pytables/__pycache__/test_errors.cpython-312.pyc,, +pandas/tests/io/pytables/__pycache__/test_file_handling.cpython-312.pyc,, +pandas/tests/io/pytables/__pycache__/test_keys.cpython-312.pyc,, +pandas/tests/io/pytables/__pycache__/test_put.cpython-312.pyc,, +pandas/tests/io/pytables/__pycache__/test_pytables_missing.cpython-312.pyc,, +pandas/tests/io/pytables/__pycache__/test_read.cpython-312.pyc,, +pandas/tests/io/pytables/__pycache__/test_retain_attributes.cpython-312.pyc,, +pandas/tests/io/pytables/__pycache__/test_round_trip.cpython-312.pyc,, +pandas/tests/io/pytables/__pycache__/test_select.cpython-312.pyc,, +pandas/tests/io/pytables/__pycache__/test_store.cpython-312.pyc,, +pandas/tests/io/pytables/__pycache__/test_subclass.cpython-312.pyc,, +pandas/tests/io/pytables/__pycache__/test_time_series.cpython-312.pyc,, +pandas/tests/io/pytables/__pycache__/test_timezones.cpython-312.pyc,, +pandas/tests/io/pytables/common.py,sha256=m3IH26TCzLDpS8ctvzJKLA8x414ur5jlX3sdT4sB4m8,1264 +pandas/tests/io/pytables/conftest.py,sha256=vQgspEHypJUvbAU3P0I5BDBW2vRK4CgmcNqY5ZXksns,136 +pandas/tests/io/pytables/test_append.py,sha256=BlbvStEsoiOGbA85pP9hw8ufZXrqixjTjGaD6msIJuE,36668 +pandas/tests/io/pytables/test_categorical.py,sha256=l_Xyc15J7E4f3_jA9Lr_sz1AhW_jyIBm9xWuRb3X154,6994 +pandas/tests/io/pytables/test_compat.py,sha256=qsaDgIDMQOOMA_ZYv7r9r9sBUUbA9Fe2jb2j8XAeY_s,2547 +pandas/tests/io/pytables/test_complex.py,sha256=CUEEEU3zJh6pmj-gws7ahyhsHJTxO0W9MKraXeFg89A,5948 +pandas/tests/io/pytables/test_errors.py,sha256=Eqf2Jad_QDt2W3SCgUf6KpS_yH5HncsmLK2K-dFpggs,8372 +pandas/tests/io/pytables/test_file_handling.py,sha256=31di39gTGo5oNr7JRGGDdRBTsOOUPMX4LZoGPpTwyZk,14592 +pandas/tests/io/pytables/test_keys.py,sha256=trCYnTHa2LhD2xnqVJ6iv1BEahHpG4FDMNbjw_MG07w,2671 +pandas/tests/io/pytables/test_put.py,sha256=OHoalcEnIuqiGN0URxnagPXjguU6-sRAjeEucN2bboA,12335 +pandas/tests/io/pytables/test_pytables_missing.py,sha256=mK_l-tuF_TeoK4gZqRncm-FCe2PUgk2AS3q6q0M1YIU,345 +pandas/tests/io/pytables/test_read.py,sha256=coGLYjldztQ7XDYywtSWM6oQnG71J2q5Jg_yohuhHSg,13242 +pandas/tests/io/pytables/test_retain_attributes.py,sha256=WY5rbnlT_NqERl4OSJ9C2iWLtFpZZCW57iNiF-UbZDM,2970 +pandas/tests/io/pytables/test_round_trip.py,sha256=LTTDrvuzkO5INLmxunr4aHtQLhec_CN0kzB0fHm6Yv0,18753 +pandas/tests/io/pytables/test_select.py,sha256=ogfh1U88Zm1e0huKtgNGG16VBwgprZxaorDU09BF_ZA,37100 +pandas/tests/io/pytables/test_store.py,sha256=fL4f1vm40WPdrJ1YXlMd4hzc-DkyQksRMdKEdGc27Dw,37378 +pandas/tests/io/pytables/test_subclass.py,sha256=fgiunpfa4hECpAXsZrq4nB1a1z5txJxEj9MqyOBI3fQ,1369 +pandas/tests/io/pytables/test_time_series.py,sha256=hduw-GMBvahyZHh6JVrLKrxvU3NR0vl0cWTWamlgZw4,2481 +pandas/tests/io/pytables/test_timezones.py,sha256=3wUurqaoR-UdgndFKyPxmluEzl4euTPBFDcL6nV2IqM,11804 +pandas/tests/io/sas/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/io/sas/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/io/sas/__pycache__/test_byteswap.cpython-312.pyc,, +pandas/tests/io/sas/__pycache__/test_sas.cpython-312.pyc,, +pandas/tests/io/sas/__pycache__/test_sas7bdat.cpython-312.pyc,, +pandas/tests/io/sas/__pycache__/test_xport.cpython-312.pyc,, +pandas/tests/io/sas/test_byteswap.py,sha256=fIqzF9LZs3TLm7JI4tEk4JxkynmWqZ5TydCmc12sGQs,1987 +pandas/tests/io/sas/test_sas.py,sha256=M9OeR39l3-DGJSBr84IVmnYMpMs_3xVfCgSSR8u7m-k,1057 +pandas/tests/io/sas/test_sas7bdat.py,sha256=DynUdplEj6lJUo7R6rmq5CAS1i3Jdp24AMhGBcDtaqc,14807 +pandas/tests/io/sas/test_xport.py,sha256=-gNRR9_2QZS2dQ7Zu756Omg5Bpaz-2I5nCovqEqJVwU,5728 +pandas/tests/io/test_clipboard.py,sha256=ePzhUM4NAeDeLs5HtICUJsGjJiuq1C5GZjN6fkmRND4,13302 +pandas/tests/io/test_common.py,sha256=ZCxsN-pvRYpdcw0-Dcy5wLL0MFM3fesBwQvhXcXCjnM,23778 +pandas/tests/io/test_compression.py,sha256=OGRUhXoSSY1uAUP2VUb45eRleOCuHTct3wRXVSdvpR8,12343 +pandas/tests/io/test_feather.py,sha256=uQRnNCEaENrmSWimfpgO88LAMS0p1bKy4Q4v0LrOBfY,9222 +pandas/tests/io/test_fsspec.py,sha256=fbJmi7UQIG-RDb0dw8zil2dj43OzyG0tyNww5Fiw6a4,10418 +pandas/tests/io/test_gbq.py,sha256=9tA62qL0uGbSKMZdxMwNjANpxaNB4buEdKfqAQej0HQ,401 +pandas/tests/io/test_gcs.py,sha256=gBXSRHyfJnkdVD5bI64KzG-NSv0dWDpKAOY3_rm82nM,7362 +pandas/tests/io/test_html.py,sha256=mY8-tmi5bqBngXwhfTNS2x_1fwncLP3io0TwHwT-n5k,56862 +pandas/tests/io/test_http_headers.py,sha256=_p5LsnX0QXVk7RGMU7TG-NqBWpPGVA-W79eeugdpXoU,4753 +pandas/tests/io/test_orc.py,sha256=rYZCqSiNAPDQIK-2RczRh--E96NkLGV82tp42XR5n-A,13663 +pandas/tests/io/test_parquet.py,sha256=nvzpnsoTzULj1m0pzR53xRKdrIkl_gqNkt_7FqSfHRg,50215 +pandas/tests/io/test_pickle.py,sha256=eY7TI1oVlRXu9fAIJpiE0ig7H6bRC-L2YEOJNqIjh14,20755 +pandas/tests/io/test_s3.py,sha256=vLi6EkvAGMKudRcbxcosxHV7z_q6GbknZuYdEisHjy4,1181 +pandas/tests/io/test_spss.py,sha256=qp310khtYqh_uMjB_Y28MAJVuBUJEbSXwBtPCt2VoWg,6326 +pandas/tests/io/test_sql.py,sha256=wpkLb5iYSdRZCWnoYCHX2eFPB5Dayau2c3RH23kJDX4,145081 +pandas/tests/io/test_stata.py,sha256=ZMqmUmrVz1Jh5PF6-HSqHQth1T7z6tZiFYoFE4jrHJU,92292 +pandas/tests/io/xml/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/io/xml/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/io/xml/__pycache__/conftest.cpython-312.pyc,, +pandas/tests/io/xml/__pycache__/test_to_xml.cpython-312.pyc,, +pandas/tests/io/xml/__pycache__/test_xml.cpython-312.pyc,, +pandas/tests/io/xml/__pycache__/test_xml_dtypes.cpython-312.pyc,, +pandas/tests/io/xml/conftest.py,sha256=ex3IgyE-7MBC_y5T2gJphlfUex7nqRG5VfX62mTbe5E,850 +pandas/tests/io/xml/test_to_xml.py,sha256=IxG7rT8KV0BghiUMvVMyd5GkbDR9xqWSmSDqT3CUAKM,35612 +pandas/tests/io/xml/test_xml.py,sha256=vuVe3eJJ1_fBoJNgDoLdTYCiqXhMmSH5qHGsd3T7I2A,61257 +pandas/tests/io/xml/test_xml_dtypes.py,sha256=z8unMuhwvcrDUQ-7j4PBKBzr55QXNprA7qALGW7vYw0,13266 +pandas/tests/libs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/libs/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/libs/__pycache__/test_hashtable.cpython-312.pyc,, +pandas/tests/libs/__pycache__/test_join.cpython-312.pyc,, +pandas/tests/libs/__pycache__/test_lib.cpython-312.pyc,, +pandas/tests/libs/__pycache__/test_libalgos.cpython-312.pyc,, +pandas/tests/libs/test_hashtable.py,sha256=4rXFphd6C9bf5AVIqOohTwsJ7mA14SZmq3hcWtC7m-w,26091 +pandas/tests/libs/test_join.py,sha256=z5JeLRMmF_vu4wwOpi3cG6k-p6lkhjAKPad6ShMqS30,10811 +pandas/tests/libs/test_lib.py,sha256=iiYT79WGEiF-nHJuz7k-AoKwxd9x0BjcGry4j5SCFrc,10592 +pandas/tests/libs/test_libalgos.py,sha256=saDyCbchGU690HmrfZUJ6q1iCLNeW4x50Y-A2o1fgrg,5322 +pandas/tests/plotting/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/plotting/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/plotting/__pycache__/common.cpython-312.pyc,, +pandas/tests/plotting/__pycache__/conftest.cpython-312.pyc,, +pandas/tests/plotting/__pycache__/test_backend.cpython-312.pyc,, +pandas/tests/plotting/__pycache__/test_boxplot_method.cpython-312.pyc,, +pandas/tests/plotting/__pycache__/test_common.cpython-312.pyc,, +pandas/tests/plotting/__pycache__/test_converter.cpython-312.pyc,, +pandas/tests/plotting/__pycache__/test_datetimelike.cpython-312.pyc,, +pandas/tests/plotting/__pycache__/test_groupby.cpython-312.pyc,, +pandas/tests/plotting/__pycache__/test_hist_method.cpython-312.pyc,, +pandas/tests/plotting/__pycache__/test_misc.cpython-312.pyc,, +pandas/tests/plotting/__pycache__/test_series.cpython-312.pyc,, +pandas/tests/plotting/__pycache__/test_style.cpython-312.pyc,, +pandas/tests/plotting/common.py,sha256=6oADaI21vWLSPgHVqckoLiPFWsrGXw71fel7HHxJyZc,16871 +pandas/tests/plotting/conftest.py,sha256=WGxjahxQkw-Gk4DlnLW0rDsei0dmuoCuZusNMepwty0,1531 +pandas/tests/plotting/frame/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/plotting/frame/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/plotting/frame/__pycache__/test_frame.cpython-312.pyc,, +pandas/tests/plotting/frame/__pycache__/test_frame_color.cpython-312.pyc,, +pandas/tests/plotting/frame/__pycache__/test_frame_groupby.cpython-312.pyc,, +pandas/tests/plotting/frame/__pycache__/test_frame_legend.cpython-312.pyc,, +pandas/tests/plotting/frame/__pycache__/test_frame_subplots.cpython-312.pyc,, +pandas/tests/plotting/frame/__pycache__/test_hist_box_by.cpython-312.pyc,, +pandas/tests/plotting/frame/test_frame.py,sha256=q1Yqth7KkCdlm3UBASDvzVfm-fQMT9JPDp1INECJM4A,98409 +pandas/tests/plotting/frame/test_frame_color.py,sha256=gBkX_6DMH-joE-4GjwZpIYgWHJkrWPPDJ8R9gKuHqH8,28488 +pandas/tests/plotting/frame/test_frame_groupby.py,sha256=JNd4J9E4BEtcU5ed47_SZK5p77P6vthENn_shRPbAJQ,2547 +pandas/tests/plotting/frame/test_frame_legend.py,sha256=10NvOjyNdV703r-9mLhYXIxeyZJFq_-24N9XNkNReJw,10443 +pandas/tests/plotting/frame/test_frame_subplots.py,sha256=kRVFvweJSAwzh9gNIzoifuy6_U2d9mZ-K7zXR_K5otw,28986 +pandas/tests/plotting/frame/test_hist_box_by.py,sha256=8jqVQfLrE5AKvn7iKMX7L5Gbe7e4rv6Ic8MnNp7NALI,10969 +pandas/tests/plotting/test_backend.py,sha256=rE7SNyeJiSUOWwkvxndq3qtpUEOYkUetCwdO_ey-eWM,3382 +pandas/tests/plotting/test_boxplot_method.py,sha256=DZ7MuTRTuNzQfzbMpRerX8oMhgLwTokFNK6o_YdP6Ag,29319 +pandas/tests/plotting/test_common.py,sha256=if9WnxryRdUhub-3yjdTEKO2PME-Yhf5YIG8e2nvAXU,1869 +pandas/tests/plotting/test_converter.py,sha256=pC3IZ6pfKITbmzTZBwoPwG1abGtPT6Sp1YLMuKLDKG8,13251 +pandas/tests/plotting/test_datetimelike.py,sha256=Jvsqdvr_SKrdzgRYwoTlNJeS_NWMSTD183sQF-lQMAs,66544 +pandas/tests/plotting/test_groupby.py,sha256=mcM2bOmfvJteLz9H0qMawxN3Yef-Nj2zCa_MUUBWF_c,5735 +pandas/tests/plotting/test_hist_method.py,sha256=2Rkk6DlGz9I4rXDjs6qBrZiRvUNWiBDCIKk44m0mrxw,34972 +pandas/tests/plotting/test_misc.py,sha256=_IoHRNT_OSGTyFfIu5giv5BnaUFWENQH36VKN8q32tI,25201 +pandas/tests/plotting/test_series.py,sha256=73VoBpLMLjKHwIaZKM50rGpOSx1kBsCxlxxNSsPwh8k,35318 +pandas/tests/plotting/test_style.py,sha256=3YMcq45IgmIomuihBowBT-lyJfpJR_Q8fbMOEQXUkao,5172 +pandas/tests/reductions/__init__.py,sha256=vflo8yMcocx2X1Rdw9vt8NpiZ4ZFq9xZRC3PW6Gp-Cs,125 +pandas/tests/reductions/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/reductions/__pycache__/test_reductions.cpython-312.pyc,, +pandas/tests/reductions/__pycache__/test_stat_reductions.cpython-312.pyc,, +pandas/tests/reductions/test_reductions.py,sha256=KCjnnzvnla6IysKlbvUO8bXSZ3hxxqfskc9CweDd1iM,57446 +pandas/tests/reductions/test_stat_reductions.py,sha256=Q-sfitViCm3-oQQVHWDwjKKia1ZuUX6079cGmv3i3oU,9722 +pandas/tests/resample/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/resample/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/resample/__pycache__/conftest.cpython-312.pyc,, +pandas/tests/resample/__pycache__/test_base.cpython-312.pyc,, +pandas/tests/resample/__pycache__/test_datetime_index.cpython-312.pyc,, +pandas/tests/resample/__pycache__/test_period_index.cpython-312.pyc,, +pandas/tests/resample/__pycache__/test_resample_api.cpython-312.pyc,, +pandas/tests/resample/__pycache__/test_resampler_grouper.cpython-312.pyc,, +pandas/tests/resample/__pycache__/test_time_grouper.cpython-312.pyc,, +pandas/tests/resample/__pycache__/test_timedelta.cpython-312.pyc,, +pandas/tests/resample/conftest.py,sha256=XXj72zj-3AH2jPBUacVV6GSpY9Y4in_38g8cSf8UfYg,3355 +pandas/tests/resample/test_base.py,sha256=vKNx1a1KdSun8OIPEy62VUk390Eew6XyVgHQNbjPRFc,15475 +pandas/tests/resample/test_datetime_index.py,sha256=yQMqPpYkVcrH6MngHltPa9uVDd5n-Hv8jgy-jQVCIvs,74496 +pandas/tests/resample/test_period_index.py,sha256=zlaCtN0II7xAg9-sHDo6HdMNJhrmhCLVbSWe4QPZkR8,43093 +pandas/tests/resample/test_resample_api.py,sha256=QP9mj7ElUdWz7mMIfOJBLxYFsPhWugrzNZNGellLXTM,34082 +pandas/tests/resample/test_resampler_grouper.py,sha256=j2WlubBPgs6CJ8u1nJHhhLOi9LxFkhd6Si2fg2M7yGc,23266 +pandas/tests/resample/test_time_grouper.py,sha256=7VGDIWdewbXeWGH80i_w0s0ffBPke0r-nqmv9_PC52s,11837 +pandas/tests/resample/test_timedelta.py,sha256=H_ZjEJhXN6fhWbpwEwuPsxFDWQermDwUvsM7oaE2pG0,7469 +pandas/tests/reshape/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/reshape/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/reshape/__pycache__/test_crosstab.cpython-312.pyc,, +pandas/tests/reshape/__pycache__/test_cut.cpython-312.pyc,, +pandas/tests/reshape/__pycache__/test_from_dummies.cpython-312.pyc,, +pandas/tests/reshape/__pycache__/test_get_dummies.cpython-312.pyc,, +pandas/tests/reshape/__pycache__/test_melt.cpython-312.pyc,, +pandas/tests/reshape/__pycache__/test_pivot.cpython-312.pyc,, +pandas/tests/reshape/__pycache__/test_pivot_multilevel.cpython-312.pyc,, +pandas/tests/reshape/__pycache__/test_qcut.cpython-312.pyc,, +pandas/tests/reshape/__pycache__/test_union_categoricals.cpython-312.pyc,, +pandas/tests/reshape/__pycache__/test_util.cpython-312.pyc,, +pandas/tests/reshape/concat/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/reshape/concat/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/reshape/concat/__pycache__/conftest.cpython-312.pyc,, +pandas/tests/reshape/concat/__pycache__/test_append.cpython-312.pyc,, +pandas/tests/reshape/concat/__pycache__/test_append_common.cpython-312.pyc,, +pandas/tests/reshape/concat/__pycache__/test_categorical.cpython-312.pyc,, +pandas/tests/reshape/concat/__pycache__/test_concat.cpython-312.pyc,, +pandas/tests/reshape/concat/__pycache__/test_dataframe.cpython-312.pyc,, +pandas/tests/reshape/concat/__pycache__/test_datetimes.cpython-312.pyc,, +pandas/tests/reshape/concat/__pycache__/test_empty.cpython-312.pyc,, +pandas/tests/reshape/concat/__pycache__/test_index.cpython-312.pyc,, +pandas/tests/reshape/concat/__pycache__/test_invalid.cpython-312.pyc,, +pandas/tests/reshape/concat/__pycache__/test_series.cpython-312.pyc,, +pandas/tests/reshape/concat/__pycache__/test_sort.cpython-312.pyc,, +pandas/tests/reshape/concat/conftest.py,sha256=s94n_rOGHsQKdP2KbCAQEfZeQpesYmhH_d-RNNTkvYc,162 +pandas/tests/reshape/concat/test_append.py,sha256=mCBndbLvwmM8qTbwH7HoyZjFGLQWOsOMGjn1I1Mz8PA,14299 +pandas/tests/reshape/concat/test_append_common.py,sha256=Z2hBl4TyKpIJ-staPnWVmAbRMv9Wg0tQK_W8YpcIMXQ,27866 +pandas/tests/reshape/concat/test_categorical.py,sha256=37u7FkYgN0-HZX6z7_5MpAkgv4SCTX1xT4GfSgEfw5o,9531 +pandas/tests/reshape/concat/test_concat.py,sha256=tGbGgnotYE5XJLt0cG9D_FfziSflb9oNzlfqyeZbNL4,32440 +pandas/tests/reshape/concat/test_dataframe.py,sha256=-vObBDtkJ7N_eeIFgjpOVVrMJf_bB9KKknHZg1DbG7k,8864 +pandas/tests/reshape/concat/test_datetimes.py,sha256=dZc65JXlR1l5ulBaQrVzkLv0z8LgwXBlrBFxOxRSBZk,21584 +pandas/tests/reshape/concat/test_empty.py,sha256=wyQDnoujsaY-_dz5MlE-fpXqZyESi1mp0g8BFLQ3kyw,10242 +pandas/tests/reshape/concat/test_index.py,sha256=cauuUpDyWBOAmwmvaSnehWuve5XpFtGoNj2xuEFwdp4,17453 +pandas/tests/reshape/concat/test_invalid.py,sha256=E7InfrzodepcICRP_zFyg11CMs-2SmNrxFY3f8bhqjA,1608 +pandas/tests/reshape/concat/test_series.py,sha256=af0lLNaUEvGml86Ziy-VLJt-wQ-rwQZuQoFROulm9Z8,6061 +pandas/tests/reshape/concat/test_sort.py,sha256=RuXIJduLa56IJDmUQaCwyYOz_U0KXMDWf04WEzi8y7E,4350 +pandas/tests/reshape/merge/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/reshape/merge/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/reshape/merge/__pycache__/test_join.cpython-312.pyc,, +pandas/tests/reshape/merge/__pycache__/test_merge.cpython-312.pyc,, +pandas/tests/reshape/merge/__pycache__/test_merge_asof.cpython-312.pyc,, +pandas/tests/reshape/merge/__pycache__/test_merge_cross.cpython-312.pyc,, +pandas/tests/reshape/merge/__pycache__/test_merge_index_as_string.cpython-312.pyc,, +pandas/tests/reshape/merge/__pycache__/test_merge_ordered.cpython-312.pyc,, +pandas/tests/reshape/merge/__pycache__/test_multi.cpython-312.pyc,, +pandas/tests/reshape/merge/test_join.py,sha256=uCi2kLp2Liq430VMue_iNsG49vML1J5DtIFKxs_yRyc,37570 +pandas/tests/reshape/merge/test_merge.py,sha256=2R-652Kz2iamzZiT6o-kgsgHEUNg9i_iQmC7p5ZIdSU,106202 +pandas/tests/reshape/merge/test_merge_asof.py,sha256=Gib-41Z735fnPUh0Ipn6V_XyxKhPL3zm1iFFTNywclo,121614 +pandas/tests/reshape/merge/test_merge_cross.py,sha256=9BVH6HWJRh-dHKDTBy8Q2it97gjVW79FgPC99HNLIc4,3146 +pandas/tests/reshape/merge/test_merge_index_as_string.py,sha256=w_9BccpqfB7yPhy_TBlMGx2BPOBwPhfg-pYRKA4HEC8,5357 +pandas/tests/reshape/merge/test_merge_ordered.py,sha256=Y4GLA6hxUoUdo6XhJ5inFBf867JJ8XqiaMi7GY4tsNY,7731 +pandas/tests/reshape/merge/test_multi.py,sha256=kV5tUCNAljJ78IPNrhaeDX9AyKtN2KdF8ZpNMTeDyzY,31130 +pandas/tests/reshape/test_crosstab.py,sha256=fJTqrjVg45YUp8aPCcpgRzrNEoXibZIAz8Tmz2cTM7k,32578 +pandas/tests/reshape/test_cut.py,sha256=vr9TM1AwpJc1c_roHi43ydZ3cMDPBvNv29qqYiypbDk,24554 +pandas/tests/reshape/test_from_dummies.py,sha256=-EzZAKwOfAIdfmAf36a9yJoXb9EDee5s8b3Niz0QXSQ,13272 +pandas/tests/reshape/test_get_dummies.py,sha256=EwXZfFJkidYfSSfV3b22rjxl87oO4IzHlL7_anciR1g,27650 +pandas/tests/reshape/test_melt.py,sha256=myoJF1JEbXammo_jC8SoxfWBSMDUoybuAYyleerElJ0,42211 +pandas/tests/reshape/test_pivot.py,sha256=52TJ3gtJ4K79Q4_kc0GCCAKD3SD0j9niybWoxmI8Z_U,93311 +pandas/tests/reshape/test_pivot_multilevel.py,sha256=DYp3BZ0h80UEgqFs0sNVqnUWBWgYU4622wp62SdCDdI,7549 +pandas/tests/reshape/test_qcut.py,sha256=0XO-B9XmAGiWLhEFW8wujFo-VR1r62SZP7MT-DBz1VE,8477 +pandas/tests/reshape/test_union_categoricals.py,sha256=-5HAPWXufLo52xxRMFedZjSfadNv9GFy4c-OKvN8GBA,15207 +pandas/tests/reshape/test_util.py,sha256=mk60VTWL9YPWNPAmVBHwkOAOtrHIDU6L3EAnlasx6IQ,2897 +pandas/tests/scalar/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/scalar/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/scalar/__pycache__/test_na_scalar.cpython-312.pyc,, +pandas/tests/scalar/__pycache__/test_nat.cpython-312.pyc,, +pandas/tests/scalar/interval/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/scalar/interval/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/scalar/interval/__pycache__/test_arithmetic.cpython-312.pyc,, +pandas/tests/scalar/interval/__pycache__/test_constructors.cpython-312.pyc,, +pandas/tests/scalar/interval/__pycache__/test_contains.cpython-312.pyc,, +pandas/tests/scalar/interval/__pycache__/test_formats.cpython-312.pyc,, +pandas/tests/scalar/interval/__pycache__/test_interval.cpython-312.pyc,, +pandas/tests/scalar/interval/__pycache__/test_overlaps.cpython-312.pyc,, +pandas/tests/scalar/interval/test_arithmetic.py,sha256=qrUOEDp9dOkOoEfuuUHhmzKTZuPbj727p2PxO1kgxxM,5937 +pandas/tests/scalar/interval/test_constructors.py,sha256=DI5iRKoIg51lI_-FysKQyyaJnwrd8CqLjk7b7iqFIp0,1599 +pandas/tests/scalar/interval/test_contains.py,sha256=MSjo5U7KLuqugnEtURC8znpldI3-cLIfXQlIhNvQLI4,2354 +pandas/tests/scalar/interval/test_formats.py,sha256=Ep7692gGQMdrYiCxxudqXX-CA6S1sO3L2P2I4NHIreo,344 +pandas/tests/scalar/interval/test_interval.py,sha256=W54SKFbFSlsvFwoXkNhb6JK52klz8is2ww2ZQ7AIjUs,2656 +pandas/tests/scalar/interval/test_overlaps.py,sha256=2FHG23scoclsfZZAngK9sesna_3xgbjgSKoUzlMxHro,2274 +pandas/tests/scalar/period/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/scalar/period/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/scalar/period/__pycache__/test_arithmetic.cpython-312.pyc,, +pandas/tests/scalar/period/__pycache__/test_asfreq.cpython-312.pyc,, +pandas/tests/scalar/period/__pycache__/test_period.cpython-312.pyc,, +pandas/tests/scalar/period/test_arithmetic.py,sha256=YYt1270I1WxtnQqGck_49ECYtrpw__lX8qx8t-GuIZM,16775 +pandas/tests/scalar/period/test_asfreq.py,sha256=dbmg35zwFwPSiYR-5OuSA790slBEct8N6C1jkEXchBs,38445 +pandas/tests/scalar/period/test_period.py,sha256=zjHRVTyPeR7y2SgMn1UsUM1M37EfT1kypoPuqjxsFGI,40121 +pandas/tests/scalar/test_na_scalar.py,sha256=0t4r9nDTQtXUSeXRBxDfgWegznLM6TvMk2pK0gLScJc,7227 +pandas/tests/scalar/test_nat.py,sha256=pUhNNUxLBv4_D-l2tsHICFiT5ruDjvlj24oEkNZycxk,19972 +pandas/tests/scalar/timedelta/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/scalar/timedelta/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/scalar/timedelta/__pycache__/test_arithmetic.cpython-312.pyc,, +pandas/tests/scalar/timedelta/__pycache__/test_constructors.cpython-312.pyc,, +pandas/tests/scalar/timedelta/__pycache__/test_formats.cpython-312.pyc,, +pandas/tests/scalar/timedelta/__pycache__/test_timedelta.cpython-312.pyc,, +pandas/tests/scalar/timedelta/methods/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/scalar/timedelta/methods/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/scalar/timedelta/methods/__pycache__/test_as_unit.cpython-312.pyc,, +pandas/tests/scalar/timedelta/methods/__pycache__/test_round.cpython-312.pyc,, +pandas/tests/scalar/timedelta/methods/test_as_unit.py,sha256=Ut-_d5xcdAq9eD5_dknpSsnhjndzRyilGuT7PxOYl5s,2518 +pandas/tests/scalar/timedelta/methods/test_round.py,sha256=kAqNhW8GJMKvaACF1b6eKhO9DOvYUJuRrMyoxG2-nHM,6338 +pandas/tests/scalar/timedelta/test_arithmetic.py,sha256=mYTdK4okwMitWPPh335LY3wzy5hXncEXPnxLd1XrDXA,38156 +pandas/tests/scalar/timedelta/test_constructors.py,sha256=49f8ARiuEAbImuDasW9-NowtijVRPyoY6ARtX6iuNnM,22433 +pandas/tests/scalar/timedelta/test_formats.py,sha256=_5svunXjM1H4X5tMqgT7aO9CoDR96XgybUYHXNdcyDo,4161 +pandas/tests/scalar/timedelta/test_timedelta.py,sha256=VAEnw5O0egqtlazzAy6oJkgFGHCKDXp3NwRyBEQ19as,23413 +pandas/tests/scalar/timestamp/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/scalar/timestamp/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/scalar/timestamp/__pycache__/test_arithmetic.cpython-312.pyc,, +pandas/tests/scalar/timestamp/__pycache__/test_comparisons.cpython-312.pyc,, +pandas/tests/scalar/timestamp/__pycache__/test_constructors.cpython-312.pyc,, +pandas/tests/scalar/timestamp/__pycache__/test_formats.cpython-312.pyc,, +pandas/tests/scalar/timestamp/__pycache__/test_timestamp.cpython-312.pyc,, +pandas/tests/scalar/timestamp/__pycache__/test_timezones.cpython-312.pyc,, +pandas/tests/scalar/timestamp/methods/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/scalar/timestamp/methods/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/scalar/timestamp/methods/__pycache__/test_as_unit.cpython-312.pyc,, +pandas/tests/scalar/timestamp/methods/__pycache__/test_normalize.cpython-312.pyc,, +pandas/tests/scalar/timestamp/methods/__pycache__/test_replace.cpython-312.pyc,, +pandas/tests/scalar/timestamp/methods/__pycache__/test_round.cpython-312.pyc,, +pandas/tests/scalar/timestamp/methods/__pycache__/test_timestamp_method.cpython-312.pyc,, +pandas/tests/scalar/timestamp/methods/__pycache__/test_to_julian_date.cpython-312.pyc,, +pandas/tests/scalar/timestamp/methods/__pycache__/test_to_pydatetime.cpython-312.pyc,, +pandas/tests/scalar/timestamp/methods/__pycache__/test_tz_convert.cpython-312.pyc,, +pandas/tests/scalar/timestamp/methods/__pycache__/test_tz_localize.cpython-312.pyc,, +pandas/tests/scalar/timestamp/methods/test_as_unit.py,sha256=Od0YhrglrVPaad4kzpjPKoVf-pBz0_lTbdaj7cpD7eU,2706 +pandas/tests/scalar/timestamp/methods/test_normalize.py,sha256=NMQXgPRwSB8Z8YtQLrU4qNbxhaq1InqKqwS8veJ_Cts,831 +pandas/tests/scalar/timestamp/methods/test_replace.py,sha256=JT-qoGosdZa0tgjg2AtKrniJnT6-o1YIXQrq-pFDL5E,7055 +pandas/tests/scalar/timestamp/methods/test_round.py,sha256=mA1FyUI8-J14yZ1Vf5Se0OeW2u4nv9-1s0r9eOmOxnE,13027 +pandas/tests/scalar/timestamp/methods/test_timestamp_method.py,sha256=JlFBfEixuZiw96lRZc88wXR9-5uOt74gBCUql321H6w,1017 +pandas/tests/scalar/timestamp/methods/test_to_julian_date.py,sha256=izPqS1f7lJ3Tqkiz65t3NjZqtgxu1_jbSg-LmZheiD4,810 +pandas/tests/scalar/timestamp/methods/test_to_pydatetime.py,sha256=duSR43OjYJiMOHjt7lLVrSdBZa74GQRqwJz5RPdbQ5M,2871 +pandas/tests/scalar/timestamp/methods/test_tz_convert.py,sha256=yw1GiCOn7F8ZDof9d7IvG6T28e6nsB-_XswfO0HN-Dc,1710 +pandas/tests/scalar/timestamp/methods/test_tz_localize.py,sha256=drtq_N4h6E-25vsQuJJO4Sc5dUXyCwIWTHM0ozIc8gI,12774 +pandas/tests/scalar/timestamp/test_arithmetic.py,sha256=4exZrHW0m6i4mCzKVFhehECC232IJYyc3IW1f-YzPbM,10852 +pandas/tests/scalar/timestamp/test_comparisons.py,sha256=zxzSqDtYxP7Fc4vXcIqxYq0Yg7KeKEdAn3iwbgAv-ns,10059 +pandas/tests/scalar/timestamp/test_constructors.py,sha256=qC0ZLNT77BDnBQ1atxBN20AG06mi10ur8-4BP9zEKDg,39486 +pandas/tests/scalar/timestamp/test_formats.py,sha256=TKn4H02mIrLpoWm4YuDsA3gUy87bYVqNLu8SgnckZA0,6864 +pandas/tests/scalar/timestamp/test_timestamp.py,sha256=c0ZhIgkRq9JfpohnixtM-n2frtyF2fR2pnUFjFER8fY,31042 +pandas/tests/scalar/timestamp/test_timezones.py,sha256=dXCPtLiGfQ9B2pg_s_YK7fvWwUW-CbVOPYUn9paFosk,666 +pandas/tests/series/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/series/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/series/__pycache__/test_api.cpython-312.pyc,, +pandas/tests/series/__pycache__/test_arithmetic.cpython-312.pyc,, +pandas/tests/series/__pycache__/test_constructors.cpython-312.pyc,, +pandas/tests/series/__pycache__/test_cumulative.cpython-312.pyc,, +pandas/tests/series/__pycache__/test_formats.cpython-312.pyc,, +pandas/tests/series/__pycache__/test_iteration.cpython-312.pyc,, +pandas/tests/series/__pycache__/test_logical_ops.cpython-312.pyc,, +pandas/tests/series/__pycache__/test_missing.cpython-312.pyc,, +pandas/tests/series/__pycache__/test_npfuncs.cpython-312.pyc,, +pandas/tests/series/__pycache__/test_reductions.cpython-312.pyc,, +pandas/tests/series/__pycache__/test_subclass.cpython-312.pyc,, +pandas/tests/series/__pycache__/test_ufunc.cpython-312.pyc,, +pandas/tests/series/__pycache__/test_unary.cpython-312.pyc,, +pandas/tests/series/__pycache__/test_validate.cpython-312.pyc,, +pandas/tests/series/accessors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/series/accessors/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/series/accessors/__pycache__/test_cat_accessor.cpython-312.pyc,, +pandas/tests/series/accessors/__pycache__/test_dt_accessor.cpython-312.pyc,, +pandas/tests/series/accessors/__pycache__/test_list_accessor.cpython-312.pyc,, +pandas/tests/series/accessors/__pycache__/test_sparse_accessor.cpython-312.pyc,, +pandas/tests/series/accessors/__pycache__/test_str_accessor.cpython-312.pyc,, +pandas/tests/series/accessors/__pycache__/test_struct_accessor.cpython-312.pyc,, +pandas/tests/series/accessors/test_cat_accessor.py,sha256=1-ZRI4h_lsBclkXljCrYFwGIYXbhrpE1iET-MjNKngk,9611 +pandas/tests/series/accessors/test_dt_accessor.py,sha256=wL3MFsru8nLxaY2KLmCFfZvdpvtIYHJie44Ff6V7eSE,29886 +pandas/tests/series/accessors/test_list_accessor.py,sha256=7OsgwSCkXFDSRh81g5WKniPsv_zcTosuGicGPSemBqo,3425 +pandas/tests/series/accessors/test_sparse_accessor.py,sha256=yPxK1Re7RDPLi5v2r9etrgsUfSL9NN45CAvuR3tYVwA,296 +pandas/tests/series/accessors/test_str_accessor.py,sha256=M29X62c2ekvH1FTv56yye2TLcXyYUCM5AegAQVWLFc8,853 +pandas/tests/series/accessors/test_struct_accessor.py,sha256=Yg_Z1GjJf92XaXOnT0aUaeEtp7AOcQqWPT4guJKGfEg,5443 +pandas/tests/series/indexing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/series/indexing/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/series/indexing/__pycache__/test_datetime.cpython-312.pyc,, +pandas/tests/series/indexing/__pycache__/test_delitem.cpython-312.pyc,, +pandas/tests/series/indexing/__pycache__/test_get.cpython-312.pyc,, +pandas/tests/series/indexing/__pycache__/test_getitem.cpython-312.pyc,, +pandas/tests/series/indexing/__pycache__/test_indexing.cpython-312.pyc,, +pandas/tests/series/indexing/__pycache__/test_mask.cpython-312.pyc,, +pandas/tests/series/indexing/__pycache__/test_set_value.cpython-312.pyc,, +pandas/tests/series/indexing/__pycache__/test_setitem.cpython-312.pyc,, +pandas/tests/series/indexing/__pycache__/test_take.cpython-312.pyc,, +pandas/tests/series/indexing/__pycache__/test_where.cpython-312.pyc,, +pandas/tests/series/indexing/__pycache__/test_xs.cpython-312.pyc,, +pandas/tests/series/indexing/test_datetime.py,sha256=1_yUGMkSFYGh7TJOeDN_-5FvqsVyV-rGdgBzOnyqqNk,14752 +pandas/tests/series/indexing/test_delitem.py,sha256=bqam_JdFo9bWPIIglt0Rvms-KJxG1wZ5znTtrAn5eaI,2063 +pandas/tests/series/indexing/test_get.py,sha256=-FooS4ocg7uqbXYDNEZwMvRpTCar5LJCgCqi_CpDoo0,5758 +pandas/tests/series/indexing/test_getitem.py,sha256=TLizXWrxrsUj5KtXGVB2kIxHK3ayq5IsjnjTDqFiPzY,24431 +pandas/tests/series/indexing/test_indexing.py,sha256=UJrjrjD_5-nqaPVcjSz90dQQRdtXeCD5QzZDTCVGjTw,16679 +pandas/tests/series/indexing/test_mask.py,sha256=ecPdJ-CM8HbaaZoGUfwcoOuo0eIz7aEq-x8wL0PZWbE,1711 +pandas/tests/series/indexing/test_set_value.py,sha256=UwVNpW3Fh3PKhNiFzZiVK07W871CmFM2fGtC6CTW5z0,991 +pandas/tests/series/indexing/test_setitem.py,sha256=DdaSjqIX0sTc-zXFv3dbuq0KRcq4qcxJcZEblgdtIyg,60265 +pandas/tests/series/indexing/test_take.py,sha256=574cgL0w0fj-YnZma9b188Y0mTWs-Go6ZzB9zQSdpAk,1353 +pandas/tests/series/indexing/test_where.py,sha256=eAUIGPRMumG78t6CMCtoe50hJCKLUFCWSe8mjHyA5Bo,13441 +pandas/tests/series/indexing/test_xs.py,sha256=8EKGIgnK86_hsBjPIY5lednYnzatv14O6rq3LjR_KxI,2760 +pandas/tests/series/methods/__init__.py,sha256=zVXqGxDIQ-ebxxcetI9KcJ9ZEHeIC4086CoDvyc8CNM,225 +pandas/tests/series/methods/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/series/methods/__pycache__/test_add_prefix_suffix.cpython-312.pyc,, +pandas/tests/series/methods/__pycache__/test_align.cpython-312.pyc,, +pandas/tests/series/methods/__pycache__/test_argsort.cpython-312.pyc,, +pandas/tests/series/methods/__pycache__/test_asof.cpython-312.pyc,, +pandas/tests/series/methods/__pycache__/test_astype.cpython-312.pyc,, +pandas/tests/series/methods/__pycache__/test_autocorr.cpython-312.pyc,, +pandas/tests/series/methods/__pycache__/test_between.cpython-312.pyc,, +pandas/tests/series/methods/__pycache__/test_case_when.cpython-312.pyc,, +pandas/tests/series/methods/__pycache__/test_clip.cpython-312.pyc,, +pandas/tests/series/methods/__pycache__/test_combine.cpython-312.pyc,, +pandas/tests/series/methods/__pycache__/test_combine_first.cpython-312.pyc,, +pandas/tests/series/methods/__pycache__/test_compare.cpython-312.pyc,, +pandas/tests/series/methods/__pycache__/test_convert_dtypes.cpython-312.pyc,, +pandas/tests/series/methods/__pycache__/test_copy.cpython-312.pyc,, +pandas/tests/series/methods/__pycache__/test_count.cpython-312.pyc,, +pandas/tests/series/methods/__pycache__/test_cov_corr.cpython-312.pyc,, +pandas/tests/series/methods/__pycache__/test_describe.cpython-312.pyc,, +pandas/tests/series/methods/__pycache__/test_diff.cpython-312.pyc,, +pandas/tests/series/methods/__pycache__/test_drop.cpython-312.pyc,, +pandas/tests/series/methods/__pycache__/test_drop_duplicates.cpython-312.pyc,, +pandas/tests/series/methods/__pycache__/test_dropna.cpython-312.pyc,, +pandas/tests/series/methods/__pycache__/test_dtypes.cpython-312.pyc,, +pandas/tests/series/methods/__pycache__/test_duplicated.cpython-312.pyc,, +pandas/tests/series/methods/__pycache__/test_equals.cpython-312.pyc,, +pandas/tests/series/methods/__pycache__/test_explode.cpython-312.pyc,, +pandas/tests/series/methods/__pycache__/test_fillna.cpython-312.pyc,, +pandas/tests/series/methods/__pycache__/test_get_numeric_data.cpython-312.pyc,, +pandas/tests/series/methods/__pycache__/test_head_tail.cpython-312.pyc,, +pandas/tests/series/methods/__pycache__/test_infer_objects.cpython-312.pyc,, +pandas/tests/series/methods/__pycache__/test_info.cpython-312.pyc,, +pandas/tests/series/methods/__pycache__/test_interpolate.cpython-312.pyc,, +pandas/tests/series/methods/__pycache__/test_is_monotonic.cpython-312.pyc,, +pandas/tests/series/methods/__pycache__/test_is_unique.cpython-312.pyc,, +pandas/tests/series/methods/__pycache__/test_isin.cpython-312.pyc,, +pandas/tests/series/methods/__pycache__/test_isna.cpython-312.pyc,, +pandas/tests/series/methods/__pycache__/test_item.cpython-312.pyc,, +pandas/tests/series/methods/__pycache__/test_map.cpython-312.pyc,, +pandas/tests/series/methods/__pycache__/test_matmul.cpython-312.pyc,, +pandas/tests/series/methods/__pycache__/test_nlargest.cpython-312.pyc,, +pandas/tests/series/methods/__pycache__/test_nunique.cpython-312.pyc,, +pandas/tests/series/methods/__pycache__/test_pct_change.cpython-312.pyc,, +pandas/tests/series/methods/__pycache__/test_pop.cpython-312.pyc,, +pandas/tests/series/methods/__pycache__/test_quantile.cpython-312.pyc,, +pandas/tests/series/methods/__pycache__/test_rank.cpython-312.pyc,, +pandas/tests/series/methods/__pycache__/test_reindex.cpython-312.pyc,, +pandas/tests/series/methods/__pycache__/test_reindex_like.cpython-312.pyc,, +pandas/tests/series/methods/__pycache__/test_rename.cpython-312.pyc,, +pandas/tests/series/methods/__pycache__/test_rename_axis.cpython-312.pyc,, +pandas/tests/series/methods/__pycache__/test_repeat.cpython-312.pyc,, +pandas/tests/series/methods/__pycache__/test_replace.cpython-312.pyc,, +pandas/tests/series/methods/__pycache__/test_reset_index.cpython-312.pyc,, +pandas/tests/series/methods/__pycache__/test_round.cpython-312.pyc,, +pandas/tests/series/methods/__pycache__/test_searchsorted.cpython-312.pyc,, +pandas/tests/series/methods/__pycache__/test_set_name.cpython-312.pyc,, +pandas/tests/series/methods/__pycache__/test_size.cpython-312.pyc,, +pandas/tests/series/methods/__pycache__/test_sort_index.cpython-312.pyc,, +pandas/tests/series/methods/__pycache__/test_sort_values.cpython-312.pyc,, +pandas/tests/series/methods/__pycache__/test_to_csv.cpython-312.pyc,, +pandas/tests/series/methods/__pycache__/test_to_dict.cpython-312.pyc,, +pandas/tests/series/methods/__pycache__/test_to_frame.cpython-312.pyc,, +pandas/tests/series/methods/__pycache__/test_to_numpy.cpython-312.pyc,, +pandas/tests/series/methods/__pycache__/test_tolist.cpython-312.pyc,, +pandas/tests/series/methods/__pycache__/test_truncate.cpython-312.pyc,, +pandas/tests/series/methods/__pycache__/test_tz_localize.cpython-312.pyc,, +pandas/tests/series/methods/__pycache__/test_unique.cpython-312.pyc,, +pandas/tests/series/methods/__pycache__/test_unstack.cpython-312.pyc,, +pandas/tests/series/methods/__pycache__/test_update.cpython-312.pyc,, +pandas/tests/series/methods/__pycache__/test_value_counts.cpython-312.pyc,, +pandas/tests/series/methods/__pycache__/test_values.cpython-312.pyc,, +pandas/tests/series/methods/__pycache__/test_view.cpython-312.pyc,, +pandas/tests/series/methods/test_add_prefix_suffix.py,sha256=PeUIeDHa9rGggraEbVJRtLi2GcnNcXkrXb0otlthOC4,1556 +pandas/tests/series/methods/test_align.py,sha256=1qb66VDMKpMXYpivki_WWGR_aHtEishwVQlK1ZIJqHA,7700 +pandas/tests/series/methods/test_argsort.py,sha256=GSvtMvfeUktQkrOsl-bF4di5w8QPCo9GPza1OmeofeM,2871 +pandas/tests/series/methods/test_asof.py,sha256=CqRdyeXFhE7zVdkJB-TxVqK3XPyBNvtOAfb6_a0VGgM,6324 +pandas/tests/series/methods/test_astype.py,sha256=fPZRB30wa7fFvcY66odgPfA7f2_loj-nWthKLGkOHew,25472 +pandas/tests/series/methods/test_autocorr.py,sha256=SnxELB9bcE8H68tYUDN3UKMMPu-sEfbwTlLUn8WirV8,1015 +pandas/tests/series/methods/test_between.py,sha256=9w_8uWI5kcJOTfMwbEwmjGpU2j2cyuMtCYw4MrvgSM0,2584 +pandas/tests/series/methods/test_case_when.py,sha256=0YC-SaigIaoSO2l7h9sO4ebzCrxq0ma5FtiZKiwDMRs,4223 +pandas/tests/series/methods/test_clip.py,sha256=PuUarzkVXrwdYBF6pKqKbRw_GUuXdYsSPoNomgSDyzc,5220 +pandas/tests/series/methods/test_combine.py,sha256=ye8pwpjolpG_kUKSFTC8ZoRdj3ze8qtJXvDUZ5gpap4,627 +pandas/tests/series/methods/test_combine_first.py,sha256=84sHCHBNQIhQEtTRWNZQqAuq_3LuHiVbq7Xmp5pRjZo,5420 +pandas/tests/series/methods/test_compare.py,sha256=uRA4CKyOTPSzW3sihILLvxpxdSD1hb7mHrSydGFV2J4,4658 +pandas/tests/series/methods/test_convert_dtypes.py,sha256=OsSLvgRzG1MKutYTZXPqkHwiNy6-QzXqN-KtohZ7wYs,9724 +pandas/tests/series/methods/test_copy.py,sha256=im14SuY4pXfqYHvd4UamQSSTiXsK8GOP7Ga-5w-XRFs,3164 +pandas/tests/series/methods/test_count.py,sha256=mju3vjyHXg8qRH85cRLWvRL8lFnF7HGdETjt2e_pK7M,938 +pandas/tests/series/methods/test_cov_corr.py,sha256=NfmwlBV_Umm50xTwfuhJhKtNPmrUVEaJOt9GWTsb3DQ,5709 +pandas/tests/series/methods/test_describe.py,sha256=brDSZ2qicnLANI2ReYiYQiXzu6m9VxFr4DVULEyGgSA,6646 +pandas/tests/series/methods/test_diff.py,sha256=vEBvVglFS1cSDpllOLEZ9Dkdv1E02IYP9y6s6nsL6es,2538 +pandas/tests/series/methods/test_drop.py,sha256=nqTXYfvY76BZ2cl46kUb8mkkll5StdCzBaTn_YkGfIk,3394 +pandas/tests/series/methods/test_drop_duplicates.py,sha256=P6jHz77EAtuiI2IE25pNjBx3pXteUc0JUMoj2mWo8T4,9235 +pandas/tests/series/methods/test_dropna.py,sha256=fezc4siTNn-uOEQtOhaqNAOLYBoWN3Rh6STHAtOdk8U,3577 +pandas/tests/series/methods/test_dtypes.py,sha256=IkYkFl0o2LQ5qurobwoPgp4jqi2uKU7phoAk3oZtiYo,209 +pandas/tests/series/methods/test_duplicated.py,sha256=ACzVs9IJY4lC2SQb6frHVe4dGd6YLFID5UAw4BuZa7c,2059 +pandas/tests/series/methods/test_equals.py,sha256=qo8h305o5ktv9ooQ7pMbMUnQFjzOGLWc5TwxL9wD5zg,4182 +pandas/tests/series/methods/test_explode.py,sha256=Pw0yPOLX6iHzLDld7Bo1tC2CjnMYGIo9cEQs1Q6wmDg,5110 +pandas/tests/series/methods/test_fillna.py,sha256=tjuKAfrmByzwY1H_xez3xSwKkZUDac1aSt47ZHP7llI,39985 +pandas/tests/series/methods/test_get_numeric_data.py,sha256=UPWNlzpl2a9Zez1JSfFP2EwsYfs4U4_Re4yOkqGpsl8,1178 +pandas/tests/series/methods/test_head_tail.py,sha256=1EWojjTzcLvYH34VvyvEHxczDy7zL3dMTyayFHsVSzY,343 +pandas/tests/series/methods/test_infer_objects.py,sha256=w0UyAVk4bHlCBX8Ot8BiV6Y0flw-70XiENsh0jsgyhg,1903 +pandas/tests/series/methods/test_info.py,sha256=hff1IZ3mbfwsJzNvLcFyFlnk3aubm3gcxMhROr-F-aI,4907 +pandas/tests/series/methods/test_interpolate.py,sha256=Y0pZXAceQWfdEylQi0Q78g3LLSvwv9qTr0ur9z-SED8,34267 +pandas/tests/series/methods/test_is_monotonic.py,sha256=vvyWZFxiSybq88peF0zN5dM16rH2SgCEEA-gT2rRSSY,838 +pandas/tests/series/methods/test_is_unique.py,sha256=d3aLS5q491IVZkfKx8HTc4jkgTtuN0SOaUVfkyBTImE,953 +pandas/tests/series/methods/test_isin.py,sha256=iOwKDqYVh8mFnkwcdc9oRiJVlxfDF87AwL2i7kBugqQ,8343 +pandas/tests/series/methods/test_isna.py,sha256=TzNID2_dMG6ChWSwOMIqlF9AWcc1UjtjCHLNmT0vlBE,940 +pandas/tests/series/methods/test_item.py,sha256=z9gMBXHmc-Xhpyad9O0fT2RySMhlTa6MSrz2jPSUHxc,1627 +pandas/tests/series/methods/test_map.py,sha256=nVhgNZdZvBhJfLXOckrslAK5AINuZlwtfqpkeEZSuBc,18772 +pandas/tests/series/methods/test_matmul.py,sha256=cIj2nJctMnOvEDgTefpB3jypWJ6-RHasqtxywrxXw0g,2767 +pandas/tests/series/methods/test_nlargest.py,sha256=oIkyZ6Z2NiUL09sSTvAFK7IlcfQDiVgwssFe6NtsyIE,8442 +pandas/tests/series/methods/test_nunique.py,sha256=6B7fs9niuN2QYyxjVNX33WLBJvF2SJZRCn6SInTIz0g,481 +pandas/tests/series/methods/test_pct_change.py,sha256=C_WTtvjTsvfT94CUt22jYodJCHd18nUrkCLorQPf_d8,4523 +pandas/tests/series/methods/test_pop.py,sha256=xr9ZuFCI7O2gTW8a3WBr-ooQcOhBzoUK4N1x0K5G380,295 +pandas/tests/series/methods/test_quantile.py,sha256=DrjNLdKWpR-Sy8htHn2roHNI4roGKtR-ziZ77mPBVo8,8284 +pandas/tests/series/methods/test_rank.py,sha256=PokA09Wyiil9JGQ5CBNqEtRP_uvZlwTWPd-8TsGsrfw,18104 +pandas/tests/series/methods/test_reindex.py,sha256=-AIJ2FzgeE2-1z_WPfkFcktucr78afWJWx_TTU-J-jM,14576 +pandas/tests/series/methods/test_reindex_like.py,sha256=e_nuGo4QLgsdpnZrC49xDVfcz_prTGAOXGyjEEbkKM4,1245 +pandas/tests/series/methods/test_rename.py,sha256=XzMLQMJZ4bYYOnmck8NidsW5DSfw2YlbvQmZidXVoWk,6321 +pandas/tests/series/methods/test_rename_axis.py,sha256=TqGeZdhB3Ektvj48JfbX2Jr_qsCovtoWimpfX_ViJyg,1520 +pandas/tests/series/methods/test_repeat.py,sha256=WvER_QkoVNYU4bg5hQbLdCXIWxqVnSmJ6K3_3OLLLAI,1274 +pandas/tests/series/methods/test_replace.py,sha256=xyO2g1XBJhY9ApbZK4YPhj-3FwlH7vBwRzdJ8o1vEH8,31936 +pandas/tests/series/methods/test_reset_index.py,sha256=D7fTW37psSXR22ZQHrxID1NWgeFkCZU83QLBo1Cb7xI,7848 +pandas/tests/series/methods/test_round.py,sha256=eQ6kSu0XLBX9NSA1A8V4eXEHIgqoxKbSJ79dDGcYxi8,2651 +pandas/tests/series/methods/test_searchsorted.py,sha256=2nk-hXPbFjgZfKm4bO_TiKm2xjd4hj0L9hiqR4nZ2Ss,2493 +pandas/tests/series/methods/test_set_name.py,sha256=rt1BK8BnWMd8D8vrO7yQNN4o-Fnapq5bRmlHyrYpxk4,595 +pandas/tests/series/methods/test_size.py,sha256=3-LfpWtTLM_dPAHFG_mmCxAk3dJY9WIe13czw1d9Fn4,566 +pandas/tests/series/methods/test_sort_index.py,sha256=XIiu2aL5NayZoQDsBRdBbx6po5_pW4pq4us2utrSY2c,12634 +pandas/tests/series/methods/test_sort_values.py,sha256=jIvHYYMz-RySUtJnB9aFLR88s-M20-B5E5PwK9VQhns,9372 +pandas/tests/series/methods/test_to_csv.py,sha256=baTGH5GpQJOz4rpQmRMgClwBXxwVcTpEmM870qCZ2zs,6488 +pandas/tests/series/methods/test_to_dict.py,sha256=XGdcF1jD4R0a_vWAQXwal3IVJoNwEANa1tU7qHtpIGA,1178 +pandas/tests/series/methods/test_to_frame.py,sha256=nUkHQTpMTffkpDR7w3EcQvQAevEfflD6tHm3pTBxpTI,1992 +pandas/tests/series/methods/test_to_numpy.py,sha256=pEB2B08IdIPRYp5n7USYFX9HQbClJl4xOegjVd7mYLc,1321 +pandas/tests/series/methods/test_tolist.py,sha256=5F0VAYJTPDUTlqb5zDNEec-BeBY25ZjnjqYHFQq5GPU,1115 +pandas/tests/series/methods/test_truncate.py,sha256=suMKI1jMEVVSd_b5rlLM2iqsQ08c8a9CbN8mbNKdNEU,2307 +pandas/tests/series/methods/test_tz_localize.py,sha256=chP4Dnhzfg5zphKiHwZpN-43o_p6jf0wqgid3a-ZB-Y,4336 +pandas/tests/series/methods/test_unique.py,sha256=MQB5s4KVopor1V1CgvF6lZNUSX6ZcOS2_H5JRYf7emU,2219 +pandas/tests/series/methods/test_unstack.py,sha256=ahI7kSZhf9rX-TLfy7ymFLqM4m4t2niaD7p-9TV02XE,5116 +pandas/tests/series/methods/test_update.py,sha256=deGclG13lOOd_xEkKYEfFUDge0Iiudp9MJwuv7Yis-M,5339 +pandas/tests/series/methods/test_value_counts.py,sha256=LNmYx4OpzjjbLsjYHOrd4vxJZjKm9pEntq63I3mWttc,10109 +pandas/tests/series/methods/test_values.py,sha256=Q2jACWauws0GxIc_QzxbAOgMrJR6Qs7oyx_6LK7zVt8,747 +pandas/tests/series/methods/test_view.py,sha256=JipUTX6cC-NU4nVaDsyklmpRvfvf_HvUQE_fgYFqxPU,1851 +pandas/tests/series/test_api.py,sha256=_BaBWtliETwBqhysCrXr7x_1bc3r3SIAcIFOcjZ3F_A,10339 +pandas/tests/series/test_arithmetic.py,sha256=ifsEkN9NlowAT5PU5Foi0x68t1NkHkmVdMBQjtvsFl8,33812 +pandas/tests/series/test_constructors.py,sha256=ThOK2BIx6XPEhDiDFaB5qGJdPrurlrPzod80zN67PwQ,85250 +pandas/tests/series/test_cumulative.py,sha256=lYFRlmwTQBWBP-svJnt6e55b_wnCdDVZVhuvP0ezcR8,5034 +pandas/tests/series/test_formats.py,sha256=0zdlyYIuExdMVngGrYOatSq7M5ersVQe5rPZfG48KVs,17122 +pandas/tests/series/test_iteration.py,sha256=LKCUh0-OueVvxOr7uEG8U9cQxrAk7X-WDwfgEIKUekI,1408 +pandas/tests/series/test_logical_ops.py,sha256=4gXWtkqxNRA2ge7QjsomTkXVPnhd4u1t-50rH7L10KY,20199 +pandas/tests/series/test_missing.py,sha256=6TtIBFZgw-vrOYqRzSxhYCIBngoVX8r8-sT5jFgkWKM,3277 +pandas/tests/series/test_npfuncs.py,sha256=BxhxkI2uWC-ygB3DJK_-FX2TOxcuqDUHX4tRQqD9CfU,1093 +pandas/tests/series/test_reductions.py,sha256=hgPH62fS-Ha6Czk8WhKwJ8yy2KoSmG2Jx_ebpDysDxs,6519 +pandas/tests/series/test_subclass.py,sha256=aL5tgGGXZPPIXWIgpCPBrc7Q5KS8h1ipZNKCwciw-jY,2667 +pandas/tests/series/test_ufunc.py,sha256=uo0FJLsk2WFgOIMfKBlsuySEKzwkGYtcTPCRPmJt2qY,14758 +pandas/tests/series/test_unary.py,sha256=Xktw6w940LXm38OKLW-LRqpMZSA9EB5feCt9FMLh-E4,1620 +pandas/tests/series/test_validate.py,sha256=ziCmKi_jYuGyxcnsVaJpVgwSCjBgpHDJ0dbzWLa1-kA,668 +pandas/tests/strings/__init__.py,sha256=_uWelCEA7j9QwfQkgZomjbpFbuB_FlQO1sdMXak8Zn4,367 +pandas/tests/strings/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/strings/__pycache__/conftest.cpython-312.pyc,, +pandas/tests/strings/__pycache__/test_api.cpython-312.pyc,, +pandas/tests/strings/__pycache__/test_case_justify.cpython-312.pyc,, +pandas/tests/strings/__pycache__/test_cat.cpython-312.pyc,, +pandas/tests/strings/__pycache__/test_extract.cpython-312.pyc,, +pandas/tests/strings/__pycache__/test_find_replace.cpython-312.pyc,, +pandas/tests/strings/__pycache__/test_get_dummies.cpython-312.pyc,, +pandas/tests/strings/__pycache__/test_split_partition.cpython-312.pyc,, +pandas/tests/strings/__pycache__/test_string_array.cpython-312.pyc,, +pandas/tests/strings/__pycache__/test_strings.cpython-312.pyc,, +pandas/tests/strings/conftest.py,sha256=M-9nIdAAynMJ7FvFFTHXJEUZFT8uOTbizf5ZOnOJ-Tk,3960 +pandas/tests/strings/test_api.py,sha256=eW1Z06Ghgx6bRdL7Kmd-YOi9TGHJ7zAUOTFp8I8hOEM,6353 +pandas/tests/strings/test_case_justify.py,sha256=b7vTpbbvc3MSR5F-Bhge2NqUVGPkOlauOuaHwr09W28,13545 +pandas/tests/strings/test_cat.py,sha256=zCJBBRtmaOxMGwXeS4evfDtAVccO3EmloEUn-dMi0ho,13575 +pandas/tests/strings/test_extract.py,sha256=LuGkboI2Q6d60kQgwMDudy-5eEbixaaCGP78CwHli6c,26463 +pandas/tests/strings/test_find_replace.py,sha256=u_XxsoYO6onAYZWc52ATYjEp9RpRoAoInMvxshaPVTE,35016 +pandas/tests/strings/test_get_dummies.py,sha256=LyWHwMrb5pgX69t4b9ouHflXKp4gBXadTCkaZSk_HB4,1608 +pandas/tests/strings/test_split_partition.py,sha256=vi8PvUAnLQgWsWehN0VpR7zfIOShPce0svmGhRNht5U,23234 +pandas/tests/strings/test_string_array.py,sha256=I2Y1NMM_iOn9K6068sRNP_mcXHJYpxqIsDmhk1B2avQ,3558 +pandas/tests/strings/test_strings.py,sha256=rwLDRm3JWax5Nsbnl0v65e7RYSgK70lLWmDULUVrvx4,25502 +pandas/tests/test_aggregation.py,sha256=-9GlIUg7qPr3Ppj_TNbBF85oKjSIMAv056hfcYZvhWw,2779 +pandas/tests/test_algos.py,sha256=-OriW5Hjib-BwwHpO19liYsC-SpkYIhjnMJ3tHfz1Zs,77987 +pandas/tests/test_common.py,sha256=SHkM8XyjSNxUJquSiEDa3lqE0GJ7tLsfwdro0x2leAg,7695 +pandas/tests/test_downstream.py,sha256=--pbHUtoMwjpQB0_gsyOQpHkKAzKrZwOZlrHt-RsZW8,10501 +pandas/tests/test_errors.py,sha256=4WVxQSyv6okTRVQC9LC9thX5ZjXVMrX-3l93bEd9KZ8,2789 +pandas/tests/test_expressions.py,sha256=fyTafylKNf7Wb3qzwlvIGbM4MdlJB7V4yGJrgiMRE5w,14256 +pandas/tests/test_flags.py,sha256=Dsu6pvQ5A6Manyt1VlQLK8pRpZtr-S2T3ubJvRQaRlA,1550 +pandas/tests/test_multilevel.py,sha256=3-Gmz-7nEzWFDYT5k_nzRL17xLCj2ZF3q69dzHO5sL8,12206 +pandas/tests/test_nanops.py,sha256=NWzcF6_g_IT0HQRG9ETV3kimAAKVmoFohuGymqsDLPI,42042 +pandas/tests/test_optional_dependency.py,sha256=wnDdNm9tlr2MFSOwB9EWAPUf1_H3L0GUTbGeZyGUqL8,3159 +pandas/tests/test_register_accessor.py,sha256=L2cU-H7UU1M36_7DU7p69SvGEFWZXpMpUJ8NZS2yOTI,2671 +pandas/tests/test_sorting.py,sha256=0rqJWWFq1kVX8m-W0X7dXdl9XoaYxZKuGHtBiJIn3nQ,16595 +pandas/tests/test_take.py,sha256=YSMLvpggEaY_MOT3PkVtQYUw0MfwN4bVvI3EgmOgxfA,11539 +pandas/tests/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/tools/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/tools/__pycache__/test_to_datetime.cpython-312.pyc,, +pandas/tests/tools/__pycache__/test_to_numeric.cpython-312.pyc,, +pandas/tests/tools/__pycache__/test_to_time.cpython-312.pyc,, +pandas/tests/tools/__pycache__/test_to_timedelta.cpython-312.pyc,, +pandas/tests/tools/test_to_datetime.py,sha256=9-TkN1fCZiz4hBMUOvCo7YI7bQgHjXib2Nh2H51DmqA,147160 +pandas/tests/tools/test_to_numeric.py,sha256=R9fTxZIebRQp-yNh2oDsHYF8xgszrVLNqlVDYGwnajM,29480 +pandas/tests/tools/test_to_time.py,sha256=e-QmGu5nAe9clT8n9bda5aEwHBH4ZaXqBzs5-mKWMYQ,2417 +pandas/tests/tools/test_to_timedelta.py,sha256=sA-q01yavNfamRKB0JZ08ou3PN-G38PZ1Tuk5KOL8iI,12454 +pandas/tests/tseries/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/tseries/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/tseries/frequencies/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/tseries/frequencies/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/tseries/frequencies/__pycache__/test_freq_code.cpython-312.pyc,, +pandas/tests/tseries/frequencies/__pycache__/test_frequencies.cpython-312.pyc,, +pandas/tests/tseries/frequencies/__pycache__/test_inference.cpython-312.pyc,, +pandas/tests/tseries/frequencies/test_freq_code.py,sha256=hvQl37z3W6CwcLOAqrgc2acqtjOJIbqVbnXkEUBY4cM,1727 +pandas/tests/tseries/frequencies/test_frequencies.py,sha256=tyI9e6ve7sEXdALy9GYjMV3mAQHmQF2IqW-xFzPdgjY,821 +pandas/tests/tseries/frequencies/test_inference.py,sha256=o8bZEapedbcC1zoj_slbggdZkzxX9Z1oh6VuCly8PU4,15111 +pandas/tests/tseries/holiday/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/tseries/holiday/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/tseries/holiday/__pycache__/test_calendar.cpython-312.pyc,, +pandas/tests/tseries/holiday/__pycache__/test_federal.cpython-312.pyc,, +pandas/tests/tseries/holiday/__pycache__/test_holiday.cpython-312.pyc,, +pandas/tests/tseries/holiday/__pycache__/test_observance.cpython-312.pyc,, +pandas/tests/tseries/holiday/test_calendar.py,sha256=SdMzzgTizQ88wJBRVTmVIgxE8E20_sgLFunP3WHlkZU,3622 +pandas/tests/tseries/holiday/test_federal.py,sha256=ukOOSRoUdcfUOlAT10AWVj8uxiD-88_H8xd--WpOsG0,1948 +pandas/tests/tseries/holiday/test_holiday.py,sha256=0NsEkl5wr2ckwvGiXnrYhluZZRpCc_Ede6SqdrFGc7I,11173 +pandas/tests/tseries/holiday/test_observance.py,sha256=GJBqIF4W6QG4k3Yzz6_13WMOR4nHSVzPbixHxO8Tukw,2723 +pandas/tests/tseries/offsets/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/tseries/offsets/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/tseries/offsets/__pycache__/common.cpython-312.pyc,, +pandas/tests/tseries/offsets/__pycache__/test_business_day.cpython-312.pyc,, +pandas/tests/tseries/offsets/__pycache__/test_business_hour.cpython-312.pyc,, +pandas/tests/tseries/offsets/__pycache__/test_business_month.cpython-312.pyc,, +pandas/tests/tseries/offsets/__pycache__/test_business_quarter.cpython-312.pyc,, +pandas/tests/tseries/offsets/__pycache__/test_business_year.cpython-312.pyc,, +pandas/tests/tseries/offsets/__pycache__/test_common.cpython-312.pyc,, +pandas/tests/tseries/offsets/__pycache__/test_custom_business_day.cpython-312.pyc,, +pandas/tests/tseries/offsets/__pycache__/test_custom_business_hour.cpython-312.pyc,, +pandas/tests/tseries/offsets/__pycache__/test_custom_business_month.cpython-312.pyc,, +pandas/tests/tseries/offsets/__pycache__/test_dst.cpython-312.pyc,, +pandas/tests/tseries/offsets/__pycache__/test_easter.cpython-312.pyc,, +pandas/tests/tseries/offsets/__pycache__/test_fiscal.cpython-312.pyc,, +pandas/tests/tseries/offsets/__pycache__/test_index.cpython-312.pyc,, +pandas/tests/tseries/offsets/__pycache__/test_month.cpython-312.pyc,, +pandas/tests/tseries/offsets/__pycache__/test_offsets.cpython-312.pyc,, +pandas/tests/tseries/offsets/__pycache__/test_offsets_properties.cpython-312.pyc,, +pandas/tests/tseries/offsets/__pycache__/test_quarter.cpython-312.pyc,, +pandas/tests/tseries/offsets/__pycache__/test_ticks.cpython-312.pyc,, +pandas/tests/tseries/offsets/__pycache__/test_week.cpython-312.pyc,, +pandas/tests/tseries/offsets/__pycache__/test_year.cpython-312.pyc,, +pandas/tests/tseries/offsets/common.py,sha256=D3D8mcwwzW2kSEB8uX8gO6ARX4dB4PEu3_953APlRmk,900 +pandas/tests/tseries/offsets/test_business_day.py,sha256=dqOwIoAq3Mcxrc0EEeqJnnDvJYCFz5lA0JewVuODhBc,6808 +pandas/tests/tseries/offsets/test_business_hour.py,sha256=PV5Ddc4vEsQXrXhCKyDIcKptcNhXgIe-KiY14zsbVE0,58452 +pandas/tests/tseries/offsets/test_business_month.py,sha256=ZQlcBF15WTMq5w8uC7QeQ6QYVWN8hmfu1PtJvW-ebYU,6717 +pandas/tests/tseries/offsets/test_business_quarter.py,sha256=Tvp5J5r5uDBh8Y9yW65JItTp-B5fdJ4T9G0fxelHYaw,12591 +pandas/tests/tseries/offsets/test_business_year.py,sha256=OBs55t5gGKSPhTsnGafi5Uqsrjmq1cKpfuwWLUBR8Uo,6436 +pandas/tests/tseries/offsets/test_common.py,sha256=HpiuRR_ktnWLWSoFtMe87AVUCedpRcqxoTeVrfCg7is,7406 +pandas/tests/tseries/offsets/test_custom_business_day.py,sha256=YNN53-HvTW4JrbLYwyUiM10rQqIof1iA_W1uYkiHw7w,3180 +pandas/tests/tseries/offsets/test_custom_business_hour.py,sha256=UXa57Q-ZYPDMv307t7UKQGOIE32CH_FmCNY3hX8dcN4,12312 +pandas/tests/tseries/offsets/test_custom_business_month.py,sha256=WBgCVPO6PUa4oX0bDSDk_UE5hOeYbIo2sduIM9X3ASI,13362 +pandas/tests/tseries/offsets/test_dst.py,sha256=0s6bpzEFkVfUKN6lAkeFTiyzMwYRQwrZs49WAu-LK4o,9139 +pandas/tests/tseries/offsets/test_easter.py,sha256=oZlJ3lESuLTEv6A_chVDsD3Pa_cqgbVc4_zxrEE7cvc,1150 +pandas/tests/tseries/offsets/test_fiscal.py,sha256=p_rXA9wPnKZwDp40kaB8uGjq2fpHPCRU5PFF-1rClbA,26732 +pandas/tests/tseries/offsets/test_index.py,sha256=aeW6vyuME-22oikOhiE6q6nrLkIc22TjV3wPxpWXjIk,1147 +pandas/tests/tseries/offsets/test_month.py,sha256=EHsmRpEhG_CLSNEUOtA48auiJxFnr8sPsHQTyZeuu2g,23243 +pandas/tests/tseries/offsets/test_offsets.py,sha256=0yEFO27kh9uvdu4-MYW9bp5OX9Wb3lIKdiC4Jcna-2o,40623 +pandas/tests/tseries/offsets/test_offsets_properties.py,sha256=P_16zBX7ocaGN-br0pEQBGTlewfiDpJsnf5R1ei83JQ,1971 +pandas/tests/tseries/offsets/test_quarter.py,sha256=VBRsOqNS6xzYV63UVrPU3Z3_eAZQw4WefK2gPNfKork,11839 +pandas/tests/tseries/offsets/test_ticks.py,sha256=1n9PC1iEDQwnUKJivCaC6Wms3r8Je8ZKcGua_ySLLqE,11548 +pandas/tests/tseries/offsets/test_week.py,sha256=EUTDq6l4YT8xbBhQb0iHyNfJEme2jVZdjzaeg-Qj75g,12330 +pandas/tests/tseries/offsets/test_year.py,sha256=EM9DThnH2c6CMw518YpxkrpJixPmH3OVQ_Qp8iMIHPQ,10455 +pandas/tests/tslibs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/tslibs/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/tslibs/__pycache__/test_api.cpython-312.pyc,, +pandas/tests/tslibs/__pycache__/test_array_to_datetime.cpython-312.pyc,, +pandas/tests/tslibs/__pycache__/test_ccalendar.cpython-312.pyc,, +pandas/tests/tslibs/__pycache__/test_conversion.cpython-312.pyc,, +pandas/tests/tslibs/__pycache__/test_fields.cpython-312.pyc,, +pandas/tests/tslibs/__pycache__/test_libfrequencies.cpython-312.pyc,, +pandas/tests/tslibs/__pycache__/test_liboffsets.cpython-312.pyc,, +pandas/tests/tslibs/__pycache__/test_np_datetime.cpython-312.pyc,, +pandas/tests/tslibs/__pycache__/test_npy_units.cpython-312.pyc,, +pandas/tests/tslibs/__pycache__/test_parse_iso8601.cpython-312.pyc,, +pandas/tests/tslibs/__pycache__/test_parsing.cpython-312.pyc,, +pandas/tests/tslibs/__pycache__/test_period.cpython-312.pyc,, +pandas/tests/tslibs/__pycache__/test_resolution.cpython-312.pyc,, +pandas/tests/tslibs/__pycache__/test_strptime.cpython-312.pyc,, +pandas/tests/tslibs/__pycache__/test_timedeltas.cpython-312.pyc,, +pandas/tests/tslibs/__pycache__/test_timezones.cpython-312.pyc,, +pandas/tests/tslibs/__pycache__/test_to_offset.cpython-312.pyc,, +pandas/tests/tslibs/__pycache__/test_tzconversion.cpython-312.pyc,, +pandas/tests/tslibs/test_api.py,sha256=ooEY2RyO9oL8Wcbsc958sGrBjveqTQZPauLeBN3n9xc,1525 +pandas/tests/tslibs/test_array_to_datetime.py,sha256=uQOT4gOHQr35s3R6d8GxDdCH21db6rJZzXKQYrh89y0,11871 +pandas/tests/tslibs/test_ccalendar.py,sha256=Rl2OjoB8pHaOyXW5MmshsHmm8nNMuHQvS_Du1L6ODqw,1903 +pandas/tests/tslibs/test_conversion.py,sha256=rgtB7pIs6VvpkNakcew9PFQ8oVHtwCwwBtu2gCFqbh4,4555 +pandas/tests/tslibs/test_fields.py,sha256=BQKlBXOC4LsXe7eT2CK5mRGR_25g9qYykQZ6ojoGjbE,1352 +pandas/tests/tslibs/test_libfrequencies.py,sha256=Ai6deDiGlwUHR9mVvlkIbXYzWZADHuPLlaBjDK0R2wU,717 +pandas/tests/tslibs/test_liboffsets.py,sha256=958cVv4vva5nawrYcmSinfu62NIL7lYOXOHN7yU-gAE,5108 +pandas/tests/tslibs/test_np_datetime.py,sha256=n7MNYHw7i03w4ZcVTM6GkoRN7Y7UIGxnshjHph2eDPs,7889 +pandas/tests/tslibs/test_npy_units.py,sha256=d9NFsygcKGtp-pw-ZpOvIxMhpsRqd1uPBVlqejHkNmU,922 +pandas/tests/tslibs/test_parse_iso8601.py,sha256=XGQ_GBOCosTiOFFjK4rYoDDZcIBitnyIb_0SXxKF9yo,4535 +pandas/tests/tslibs/test_parsing.py,sha256=5b-ObA324ikkn2AjKTS3-666i8bKhiXtTICi3APdBGQ,13889 +pandas/tests/tslibs/test_period.py,sha256=l1xiNGDhMIJFG21BcAcE8Gkd6GODs-dPVOXcNuw6XTA,3424 +pandas/tests/tslibs/test_resolution.py,sha256=YC6IpOJsIHrsn7DUGi_LKdQrAuZgAqofNeW0DU2gays,1544 +pandas/tests/tslibs/test_strptime.py,sha256=DqjYyJ9t-cpSFDRyF3RepxMSZ4qvPllEjvarqvQKw1E,3896 +pandas/tests/tslibs/test_timedeltas.py,sha256=DaaxCrPg5Usv1UtpaVWpiYWixUtNT1FqjtS26MJq9PI,4662 +pandas/tests/tslibs/test_timezones.py,sha256=Hb56aLljCgRtBmXp7N_TaXM55ODLs6Mvl851dncnpsQ,4724 +pandas/tests/tslibs/test_to_offset.py,sha256=GaUG1VE0HhjMFjIj3aAP1LtzqFBCVx5_e0GUX1alIIU,5873 +pandas/tests/tslibs/test_tzconversion.py,sha256=6Ouplo1p8ArDrxCzPNyH9xpYkxERNPvbd4C_-WmTNd4,953 +pandas/tests/util/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/util/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/util/__pycache__/conftest.cpython-312.pyc,, +pandas/tests/util/__pycache__/test_assert_almost_equal.cpython-312.pyc,, +pandas/tests/util/__pycache__/test_assert_attr_equal.cpython-312.pyc,, +pandas/tests/util/__pycache__/test_assert_categorical_equal.cpython-312.pyc,, +pandas/tests/util/__pycache__/test_assert_extension_array_equal.cpython-312.pyc,, +pandas/tests/util/__pycache__/test_assert_frame_equal.cpython-312.pyc,, +pandas/tests/util/__pycache__/test_assert_index_equal.cpython-312.pyc,, +pandas/tests/util/__pycache__/test_assert_interval_array_equal.cpython-312.pyc,, +pandas/tests/util/__pycache__/test_assert_numpy_array_equal.cpython-312.pyc,, +pandas/tests/util/__pycache__/test_assert_produces_warning.cpython-312.pyc,, +pandas/tests/util/__pycache__/test_assert_series_equal.cpython-312.pyc,, +pandas/tests/util/__pycache__/test_deprecate.cpython-312.pyc,, +pandas/tests/util/__pycache__/test_deprecate_kwarg.cpython-312.pyc,, +pandas/tests/util/__pycache__/test_deprecate_nonkeyword_arguments.cpython-312.pyc,, +pandas/tests/util/__pycache__/test_doc.cpython-312.pyc,, +pandas/tests/util/__pycache__/test_hashing.cpython-312.pyc,, +pandas/tests/util/__pycache__/test_numba.cpython-312.pyc,, +pandas/tests/util/__pycache__/test_rewrite_warning.cpython-312.pyc,, +pandas/tests/util/__pycache__/test_shares_memory.cpython-312.pyc,, +pandas/tests/util/__pycache__/test_show_versions.cpython-312.pyc,, +pandas/tests/util/__pycache__/test_util.cpython-312.pyc,, +pandas/tests/util/__pycache__/test_validate_args.cpython-312.pyc,, +pandas/tests/util/__pycache__/test_validate_args_and_kwargs.cpython-312.pyc,, +pandas/tests/util/__pycache__/test_validate_inclusive.cpython-312.pyc,, +pandas/tests/util/__pycache__/test_validate_kwargs.cpython-312.pyc,, +pandas/tests/util/conftest.py,sha256=loEbQsEtHtv-T4Umeq_UeV6R7s8SO01GHbW6gn8lvlo,476 +pandas/tests/util/test_assert_almost_equal.py,sha256=K1-2c3XrbAb3jU23Dl9T79ueRfE32_Va7CNPfvopOYo,16803 +pandas/tests/util/test_assert_attr_equal.py,sha256=ZXTojP4V5Kle96QOFhxCZjq-dQf6gHvNOorYyOuFP1I,1045 +pandas/tests/util/test_assert_categorical_equal.py,sha256=yDmVzU22k5k5txSHixGfRJ4nKeP46FdNoh3CY1xEwEM,2728 +pandas/tests/util/test_assert_extension_array_equal.py,sha256=quw84fCgsrwtUMu-TcvHmrq5-08J7l1ZzS_3h1Eh3qw,3887 +pandas/tests/util/test_assert_frame_equal.py,sha256=ds6rGI2yrNUfU4tZfXT1KocldOkcOk0kRpBlLUk4S30,13382 +pandas/tests/util/test_assert_index_equal.py,sha256=V0rKjnd0r3Lpas1UF45kVaFxLvKDBVpQmkCg2nfvErU,10157 +pandas/tests/util/test_assert_interval_array_equal.py,sha256=ITqL0Z8AAy5D1knACPOHodI64AHxmNzxiG-i9FeU0b8,2158 +pandas/tests/util/test_assert_numpy_array_equal.py,sha256=fgb8GdUwX4EYiR3PWbjJULNfAJz4DfJ8RJXchssygO4,6624 +pandas/tests/util/test_assert_produces_warning.py,sha256=A-pN3V12hnIqlbFYArYbdU-992RgJ-fqsaKbM0yvYPw,8412 +pandas/tests/util/test_assert_series_equal.py,sha256=4_pRYe67lrrpjhcm5ceU4XBq9umgbczf7BnpvcuEQ8E,15081 +pandas/tests/util/test_deprecate.py,sha256=1hGoeUQTew5o0DnCjLV5-hOfEuSoIGOXGByq5KpAP7A,1617 +pandas/tests/util/test_deprecate_kwarg.py,sha256=7T2QkCxXUoJHhCxUjAH_5_hM-BHC6nPWG635LFY35lo,2043 +pandas/tests/util/test_deprecate_nonkeyword_arguments.py,sha256=0UkqIi4ehxD3aoA3z7y8-3dpOs6o30_Gp8rZvFX1W9Q,3623 +pandas/tests/util/test_doc.py,sha256=u0fxCg4zZWhB4SkJYc2huQ0xv7sKKAt0OlpWldmhh_M,1492 +pandas/tests/util/test_hashing.py,sha256=ZjoFCs6MoAhGV1j2WyjjEJkqyO9WQgRqwS6xx-3n0oE,13857 +pandas/tests/util/test_numba.py,sha256=6eOVcokESth7h6yyeehVizx61FtwDdVbF8wV8j3t-Ic,308 +pandas/tests/util/test_rewrite_warning.py,sha256=AUHz_OT0HS6kXs-9e59GflBCP3Tb5jy8jl9FxBg5rDs,1151 +pandas/tests/util/test_shares_memory.py,sha256=-ksI1I3vK3PR6jMqcQn_yFyJ5P0v3eLsiMI9vjZVMi4,789 +pandas/tests/util/test_show_versions.py,sha256=FjYUrUMAF7hOzphaXED__8yjeF0HTccZS6q05__rH44,2096 +pandas/tests/util/test_util.py,sha256=4UacWPLyjRQZU697jBxBWO6V1gUgkE4E-KKF6H6aXuE,1463 +pandas/tests/util/test_validate_args.py,sha256=9Z4zTqnKAWn1q9KZNvuO3DF6oszHjQrQgtOOimurWcs,1907 +pandas/tests/util/test_validate_args_and_kwargs.py,sha256=d_XcMRAQ9r--yIAAWSdJML6KeWgksy5qRNFXaY1BMQA,2456 +pandas/tests/util/test_validate_inclusive.py,sha256=w2twetJgIedm6KGQ4WmdmGC_6-RShFjXBMBVxR0gcME,896 +pandas/tests/util/test_validate_kwargs.py,sha256=NAZi-4Z0DrlQKZkkcKrWxoHxzWuKFxY8iphCBweA9jk,1808 +pandas/tests/window/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/window/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/window/__pycache__/conftest.cpython-312.pyc,, +pandas/tests/window/__pycache__/test_api.cpython-312.pyc,, +pandas/tests/window/__pycache__/test_apply.cpython-312.pyc,, +pandas/tests/window/__pycache__/test_base_indexer.cpython-312.pyc,, +pandas/tests/window/__pycache__/test_cython_aggregations.cpython-312.pyc,, +pandas/tests/window/__pycache__/test_dtypes.cpython-312.pyc,, +pandas/tests/window/__pycache__/test_ewm.cpython-312.pyc,, +pandas/tests/window/__pycache__/test_expanding.cpython-312.pyc,, +pandas/tests/window/__pycache__/test_groupby.cpython-312.pyc,, +pandas/tests/window/__pycache__/test_numba.cpython-312.pyc,, +pandas/tests/window/__pycache__/test_online.cpython-312.pyc,, +pandas/tests/window/__pycache__/test_pairwise.cpython-312.pyc,, +pandas/tests/window/__pycache__/test_rolling.cpython-312.pyc,, +pandas/tests/window/__pycache__/test_rolling_functions.cpython-312.pyc,, +pandas/tests/window/__pycache__/test_rolling_quantile.cpython-312.pyc,, +pandas/tests/window/__pycache__/test_rolling_skew_kurt.cpython-312.pyc,, +pandas/tests/window/__pycache__/test_timeseries_window.cpython-312.pyc,, +pandas/tests/window/__pycache__/test_win_type.cpython-312.pyc,, +pandas/tests/window/conftest.py,sha256=rlS3eILzfTByRmmm7HLjk-FHEIbdTVVE9c0Dq-nfxa4,3137 +pandas/tests/window/moments/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pandas/tests/window/moments/__pycache__/__init__.cpython-312.pyc,, +pandas/tests/window/moments/__pycache__/conftest.cpython-312.pyc,, +pandas/tests/window/moments/__pycache__/test_moments_consistency_ewm.cpython-312.pyc,, +pandas/tests/window/moments/__pycache__/test_moments_consistency_expanding.cpython-312.pyc,, +pandas/tests/window/moments/__pycache__/test_moments_consistency_rolling.cpython-312.pyc,, +pandas/tests/window/moments/conftest.py,sha256=xSkyyVltsAkJETLDHJSksjRkjcVHsnhfyCiNvhsQ3no,1595 +pandas/tests/window/moments/test_moments_consistency_ewm.py,sha256=4FPmIGVQuOUg13aT5c9l_DN7j7K3J9QEU0KXeO2Qrt0,8107 +pandas/tests/window/moments/test_moments_consistency_expanding.py,sha256=e4Vn3nE02q-UeRH2aWLOSMv0QN4nN04iePKst5N-Vbo,5537 +pandas/tests/window/moments/test_moments_consistency_rolling.py,sha256=UBQL1mWD1qIB3fNb4tizqv-q4xlAz4tGT1nC1G-9RWM,7821 +pandas/tests/window/test_api.py,sha256=QzFr1mgU99ETdYjqoucENyzJLmruPOO-dGR41MCARsY,13192 +pandas/tests/window/test_apply.py,sha256=v9YC4aORGX7yA50RFMjZqMx93SWp9o4Vpjo32xTROx0,9865 +pandas/tests/window/test_base_indexer.py,sha256=Fz81kU5x1g6OnNmRra6PRarPpq5HEYuA8XX0sR_y6LI,15954 +pandas/tests/window/test_cython_aggregations.py,sha256=wPAk76yfrG9D1-IzI0kDklpiTVqgp4xsEGjONe9lCY4,3967 +pandas/tests/window/test_dtypes.py,sha256=a3Xnqcq_jO0kczZmhmuBKkmCsKHOOufy9h6yNCPHlMk,5785 +pandas/tests/window/test_ewm.py,sha256=F1BB5E3_n5i5IzDNTMZeZzmG3aZqxC1jp_Pj-bWcozU,23020 +pandas/tests/window/test_expanding.py,sha256=Kz-2wSWxj4E31kd6y4jo7T7gE7aSe7yGHMYE7b4Bq18,24239 +pandas/tests/window/test_groupby.py,sha256=KXxA5gESxTSJEjmgnIz29Kz1jJRz1PYQsD64GSsoDz0,46719 +pandas/tests/window/test_numba.py,sha256=cd3uGoexUCkQ3BRNrGhfjzBb0DYGJbJ67_PJjnIxU8Q,16046 +pandas/tests/window/test_online.py,sha256=OuVpQr2NExZQ36Fl5RW4cm-2sDF3_CgEhbP-3W2xjUM,3346 +pandas/tests/window/test_pairwise.py,sha256=BXJLxRbolFs00FxTMp3uIFDNpZkciv8VGyAXFMw3zHI,16141 +pandas/tests/window/test_rolling.py,sha256=PzPkVsNDBUh6wgzFZvq_YNba2bdmwSO_H8BUK9ZxAys,61158 +pandas/tests/window/test_rolling_functions.py,sha256=xmaaXFaMq22o1s0Ba4NieIkTZtKWi9WOYae6z8i_rBo,17877 +pandas/tests/window/test_rolling_quantile.py,sha256=AvsqMR5YrVAlAFfhL0lHHAZIazXnzI1VkoVuPuiDEro,5516 +pandas/tests/window/test_rolling_skew_kurt.py,sha256=Emw9AJhTZyuVnxPg-nfYxpRNGJToWJ-he7obTSOy8iU,7807 +pandas/tests/window/test_timeseries_window.py,sha256=I0hk72tAFP4RJUaGesfUrjR5HC_bxBWwcXW7mxgslfg,24250 +pandas/tests/window/test_win_type.py,sha256=GRu_7tF1tQAEH8hcb6kZPSG2FJihUTE1_85tH1iYaN8,17522 +pandas/tseries/__init__.py,sha256=CM1Forog6FJC_5YY4IueiWfQ9cATlSDJ4hF23RTniBQ,293 +pandas/tseries/__pycache__/__init__.cpython-312.pyc,, +pandas/tseries/__pycache__/api.cpython-312.pyc,, +pandas/tseries/__pycache__/frequencies.cpython-312.pyc,, +pandas/tseries/__pycache__/holiday.cpython-312.pyc,, +pandas/tseries/__pycache__/offsets.cpython-312.pyc,, +pandas/tseries/api.py,sha256=0Tms-OsqaHcpWH7a2F4mqKqEV-G5btiZKte3cUnEWQM,234 +pandas/tseries/frequencies.py,sha256=HNmBHzxRPhtlnpZF6iBSvq6e2du9J1JZ9gQ2c48Bvv0,17686 +pandas/tseries/holiday.py,sha256=G9kQvaBMzdNUoCs4WApAcxzSkOozFEyfDYFFjL8ZlZc,18596 +pandas/tseries/offsets.py,sha256=wLWH1_fg7dYGDsHDRyBxc62788G9CDhLcpDeZHt5ixI,1531 +pandas/util/__init__.py,sha256=tXNVCMKcgkFf4GETkpUx_UYvN56-54tYCCM0-04OIn4,827 +pandas/util/__pycache__/__init__.cpython-312.pyc,, +pandas/util/__pycache__/_decorators.cpython-312.pyc,, +pandas/util/__pycache__/_doctools.cpython-312.pyc,, +pandas/util/__pycache__/_exceptions.cpython-312.pyc,, +pandas/util/__pycache__/_print_versions.cpython-312.pyc,, +pandas/util/__pycache__/_test_decorators.cpython-312.pyc,, +pandas/util/__pycache__/_tester.cpython-312.pyc,, +pandas/util/__pycache__/_validators.cpython-312.pyc,, +pandas/util/_decorators.py,sha256=n1OyKRRG-dcCRUSmyejpKTyfP_iu2kVF0TJ_9yIJkeo,17106 +pandas/util/_doctools.py,sha256=Es1FLqrmsOLpJ_7Y24q_vqdXGw5Vy6vcajcfbIi_FCo,6819 +pandas/util/_exceptions.py,sha256=H6Tz6X1PqPVp6wG_7OsjHEqTvTM9I3SebF5-WcTdZOc,2876 +pandas/util/_print_versions.py,sha256=eHw3wpaF-l66uzupWfl_x2jjXz8WTedHZdH4FFKtWo0,4636 +pandas/util/_test_decorators.py,sha256=KEhS1cMaBbf4U0R0KMRXZl-CcCkPfNqxpVz8BTtb0zY,5079 +pandas/util/_tester.py,sha256=Mluqpd_YwVdcdgZfSu-_oVdadk_JjX9FuPGFjn_S6ZA,1462 +pandas/util/_validators.py,sha256=VGKuOFzz0rY5g2dmbKpWV8vZb5Jb1RV5w-HTVi1GMY0,14300 +pandas/util/version/__init__.py,sha256=57SNOildSF8ehHn99uGwCZeAkTEuA6YMw6cYxjEyQ2I,16394 +pandas/util/version/__pycache__/__init__.cpython-312.pyc,, diff --git a/lib/python3.12/site-packages/pandas-2.2.3.dist-info/REQUESTED b/lib/python3.12/site-packages/pandas-2.2.3.dist-info/REQUESTED new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/lib/python3.12/site-packages/pandas-2.2.3.dist-info/WHEEL b/lib/python3.12/site-packages/pandas-2.2.3.dist-info/WHEEL new file mode 100644 index 0000000000000000000000000000000000000000..d98ef534f680b37433e9ab0f8470bdbe56c303d8 --- /dev/null +++ b/lib/python3.12/site-packages/pandas-2.2.3.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: meson +Root-Is-Purelib: false +Tag: cp312-cp312-manylinux_2_17_x86_64 +Tag: cp312-cp312-manylinux2014_x86_64 + diff --git a/lib/python3.12/site-packages/pandas-2.2.3.dist-info/entry_points.txt b/lib/python3.12/site-packages/pandas-2.2.3.dist-info/entry_points.txt new file mode 100644 index 0000000000000000000000000000000000000000..3c1b523d70758fbd0080e21ca4c7ce6d9c9d9bd5 --- /dev/null +++ b/lib/python3.12/site-packages/pandas-2.2.3.dist-info/entry_points.txt @@ -0,0 +1,3 @@ +[pandas_plotting_backends] +matplotlib = pandas:plotting._matplotlib + diff --git a/lib/python3.12/site-packages/parso/__init__.py b/lib/python3.12/site-packages/parso/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..a6b345f5ad917b96cf9d4092d725ee4c1e43a152 --- /dev/null +++ b/lib/python3.12/site-packages/parso/__init__.py @@ -0,0 +1,58 @@ +r""" +Parso is a Python parser that supports error recovery and round-trip parsing +for different Python versions (in multiple Python versions). Parso is also able +to list multiple syntax errors in your python file. + +Parso has been battle-tested by jedi_. It was pulled out of jedi to be useful +for other projects as well. + +Parso consists of a small API to parse Python and analyse the syntax tree. + +.. _jedi: https://github.com/davidhalter/jedi + +A simple example: + +>>> import parso +>>> module = parso.parse('hello + 1', version="3.9") +>>> expr = module.children[0] +>>> expr +PythonNode(arith_expr, [, , ]) +>>> print(expr.get_code()) +hello + 1 +>>> name = expr.children[0] +>>> name + +>>> name.end_pos +(1, 5) +>>> expr.end_pos +(1, 9) + +To list multiple issues: + +>>> grammar = parso.load_grammar() +>>> module = grammar.parse('foo +\nbar\ncontinue') +>>> error1, error2 = grammar.iter_errors(module) +>>> error1.message +'SyntaxError: invalid syntax' +>>> error2.message +"SyntaxError: 'continue' not properly in loop" +""" + +from parso.parser import ParserSyntaxError +from parso.grammar import Grammar, load_grammar +from parso.utils import split_lines, python_bytes_to_unicode + + +__version__ = '0.8.5' + + +def parse(code=None, **kwargs): + """ + A utility function to avoid loading grammars. + Params are documented in :py:meth:`parso.Grammar.parse`. + + :param str version: The version used by :py:func:`parso.load_grammar`. + """ + version = kwargs.pop('version', None) + grammar = load_grammar(version=version) + return grammar.parse(code, **kwargs) diff --git a/lib/python3.12/site-packages/parso/__pycache__/__init__.cpython-312.pyc b/lib/python3.12/site-packages/parso/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..14ccd8d594fd15fb386045b38ec00934beea055c Binary files /dev/null and b/lib/python3.12/site-packages/parso/__pycache__/__init__.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/parso/__pycache__/_compatibility.cpython-312.pyc b/lib/python3.12/site-packages/parso/__pycache__/_compatibility.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7132695241ed31b8c5b65b6a8efff739e9385b86 Binary files /dev/null and b/lib/python3.12/site-packages/parso/__pycache__/_compatibility.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/parso/__pycache__/cache.cpython-312.pyc b/lib/python3.12/site-packages/parso/__pycache__/cache.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b83484d9bb42236f42b51d71d06a7aca38c87185 Binary files /dev/null and b/lib/python3.12/site-packages/parso/__pycache__/cache.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/parso/__pycache__/file_io.cpython-312.pyc b/lib/python3.12/site-packages/parso/__pycache__/file_io.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3b685618c75891f6db4b16ce11f0cdfae52d18b1 Binary files /dev/null and b/lib/python3.12/site-packages/parso/__pycache__/file_io.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/parso/__pycache__/grammar.cpython-312.pyc b/lib/python3.12/site-packages/parso/__pycache__/grammar.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f5d9813837b030696f3fda1f2c1d51ddffae883c Binary files /dev/null and b/lib/python3.12/site-packages/parso/__pycache__/grammar.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/parso/__pycache__/normalizer.cpython-312.pyc b/lib/python3.12/site-packages/parso/__pycache__/normalizer.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1ebcda89ee3c9bb3f8d9229806b271a6c676a77c Binary files /dev/null and b/lib/python3.12/site-packages/parso/__pycache__/normalizer.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/parso/__pycache__/parser.cpython-312.pyc b/lib/python3.12/site-packages/parso/__pycache__/parser.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e948d15bf081543b8c88a6401f0d0acadc244d39 Binary files /dev/null and b/lib/python3.12/site-packages/parso/__pycache__/parser.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/parso/__pycache__/tree.cpython-312.pyc b/lib/python3.12/site-packages/parso/__pycache__/tree.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0b6efb65da15ea4e1e4fc30989b972c37507de74 Binary files /dev/null and b/lib/python3.12/site-packages/parso/__pycache__/tree.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/parso/__pycache__/utils.cpython-312.pyc b/lib/python3.12/site-packages/parso/__pycache__/utils.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c543ebe3b45ffc4037fe28ba364a2c608a4abadf Binary files /dev/null and b/lib/python3.12/site-packages/parso/__pycache__/utils.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/parso/_compatibility.py b/lib/python3.12/site-packages/parso/_compatibility.py new file mode 100644 index 0000000000000000000000000000000000000000..58b186fc43b38c92e65feeedb85061cf7fdc62b9 --- /dev/null +++ b/lib/python3.12/site-packages/parso/_compatibility.py @@ -0,0 +1,3 @@ +import platform + +is_pypy = platform.python_implementation() == 'PyPy' diff --git a/lib/python3.12/site-packages/parso/cache.py b/lib/python3.12/site-packages/parso/cache.py new file mode 100644 index 0000000000000000000000000000000000000000..5592a9fdda088c93c86cacb6d530019ffab69f4e --- /dev/null +++ b/lib/python3.12/site-packages/parso/cache.py @@ -0,0 +1,275 @@ +import time +import os +import sys +import hashlib +import gc +import shutil +import platform +import logging +import warnings +import pickle +from pathlib import Path +from typing import Dict, Any + +LOG = logging.getLogger(__name__) + +_CACHED_FILE_MINIMUM_SURVIVAL = 60 * 10 # 10 minutes +""" +Cached files should survive at least a few minutes. +""" + +_CACHED_FILE_MAXIMUM_SURVIVAL = 60 * 60 * 24 * 30 +""" +Maximum time for a cached file to survive if it is not +accessed within. +""" + +_CACHED_SIZE_TRIGGER = 600 +""" +This setting limits the amount of cached files. It's basically a way to start +garbage collection. + +The reasoning for this limit being as big as it is, is the following: + +Numpy, Pandas, Matplotlib and Tensorflow together use about 500 files. This +makes Jedi use ~500mb of memory. Since we might want a bit more than those few +libraries, we just increase it a bit. +""" + +_PICKLE_VERSION = 33 +""" +Version number (integer) for file system cache. + +Increment this number when there are any incompatible changes in +the parser tree classes. For example, the following changes +are regarded as incompatible. + +- A class name is changed. +- A class is moved to another module. +- A __slot__ of a class is changed. +""" + +_VERSION_TAG = '%s-%s%s-%s' % ( + platform.python_implementation(), + sys.version_info[0], + sys.version_info[1], + _PICKLE_VERSION +) +""" +Short name for distinguish Python implementations and versions. + +It's a bit similar to `sys.implementation.cache_tag`. +See: http://docs.python.org/3/library/sys.html#sys.implementation +""" + + +def _get_default_cache_path(): + if platform.system().lower() == 'windows': + dir_ = Path(os.getenv('LOCALAPPDATA') or '~', 'Parso', 'Parso') + elif platform.system().lower() == 'darwin': + dir_ = Path('~', 'Library', 'Caches', 'Parso') + else: + dir_ = Path(os.getenv('XDG_CACHE_HOME') or '~/.cache', 'parso') + return dir_.expanduser() + + +_default_cache_path = _get_default_cache_path() +""" +The path where the cache is stored. + +On Linux, this defaults to ``~/.cache/parso/``, on OS X to +``~/Library/Caches/Parso/`` and on Windows to ``%LOCALAPPDATA%\\Parso\\Parso\\``. +On Linux, if environment variable ``$XDG_CACHE_HOME`` is set, +``$XDG_CACHE_HOME/parso`` is used instead of the default one. +""" + +_CACHE_CLEAR_THRESHOLD = 60 * 60 * 24 + + +def _get_cache_clear_lock_path(cache_path=None): + """ + The path where the cache lock is stored. + + Cache lock will prevent continous cache clearing and only allow garbage + collection once a day (can be configured in _CACHE_CLEAR_THRESHOLD). + """ + cache_path = cache_path or _default_cache_path + return cache_path.joinpath("PARSO-CACHE-LOCK") + + +parser_cache: Dict[str, Any] = {} + + +class _NodeCacheItem: + def __init__(self, node, lines, change_time=None): + self.node = node + self.lines = lines + if change_time is None: + change_time = time.time() + self.change_time = change_time + self.last_used = change_time + + +def load_module(hashed_grammar, file_io, cache_path=None): + """ + Returns a module or None, if it fails. + """ + p_time = file_io.get_last_modified() + if p_time is None: + return None + + try: + module_cache_item = parser_cache[hashed_grammar][file_io.path] + if p_time <= module_cache_item.change_time: + module_cache_item.last_used = time.time() + return module_cache_item.node + except KeyError: + return _load_from_file_system( + hashed_grammar, + file_io.path, + p_time, + cache_path=cache_path + ) + + +def _load_from_file_system(hashed_grammar, path, p_time, cache_path=None): + cache_path = _get_hashed_path(hashed_grammar, path, cache_path=cache_path) + try: + if p_time > os.path.getmtime(cache_path): + # Cache is outdated + return None + + with open(cache_path, 'rb') as f: + gc.disable() + try: + module_cache_item = pickle.load(f) + finally: + gc.enable() + except FileNotFoundError: + return None + else: + _set_cache_item(hashed_grammar, path, module_cache_item) + LOG.debug('pickle loaded: %s', path) + return module_cache_item.node + + +def _set_cache_item(hashed_grammar, path, module_cache_item): + if sum(len(v) for v in parser_cache.values()) >= _CACHED_SIZE_TRIGGER: + # Garbage collection of old cache files. + # We are basically throwing everything away that hasn't been accessed + # in 10 minutes. + cutoff_time = time.time() - _CACHED_FILE_MINIMUM_SURVIVAL + for key, path_to_item_map in parser_cache.items(): + parser_cache[key] = { + path: node_item + for path, node_item in path_to_item_map.items() + if node_item.last_used > cutoff_time + } + + parser_cache.setdefault(hashed_grammar, {})[path] = module_cache_item + + +def try_to_save_module(hashed_grammar, file_io, module, lines, pickling=True, cache_path=None): + path = file_io.path + try: + p_time = None if path is None else file_io.get_last_modified() + except OSError: + p_time = None + pickling = False + + item = _NodeCacheItem(module, lines, p_time) + _set_cache_item(hashed_grammar, path, item) + if pickling and path is not None: + try: + _save_to_file_system(hashed_grammar, path, item, cache_path=cache_path) + except PermissionError: + # It's not really a big issue if the cache cannot be saved to the + # file system. It's still in RAM in that case. However we should + # still warn the user that this is happening. + warnings.warn( + 'Tried to save a file to %s, but got permission denied.' % path, + Warning + ) + else: + _remove_cache_and_update_lock(cache_path=cache_path) + + +def _save_to_file_system(hashed_grammar, path, item, cache_path=None): + with open(_get_hashed_path(hashed_grammar, path, cache_path=cache_path), 'wb') as f: + pickle.dump(item, f, pickle.HIGHEST_PROTOCOL) + + +def clear_cache(cache_path=None): + if cache_path is None: + cache_path = _default_cache_path + shutil.rmtree(cache_path) + parser_cache.clear() + + +def clear_inactive_cache( + cache_path=None, + inactivity_threshold=_CACHED_FILE_MAXIMUM_SURVIVAL, +): + if cache_path is None: + cache_path = _default_cache_path + if not cache_path.exists(): + return False + for dirname in os.listdir(cache_path): + version_path = cache_path.joinpath(dirname) + if not version_path.is_dir(): + continue + for file in os.scandir(version_path): + if file.stat().st_atime + _CACHED_FILE_MAXIMUM_SURVIVAL <= time.time(): + try: + os.remove(file.path) + except OSError: # silently ignore all failures + continue + else: + return True + + +def _touch(path): + try: + os.utime(path, None) + except FileNotFoundError: + try: + file = open(path, 'a') + file.close() + except (OSError, IOError): # TODO Maybe log this? + return False + return True + + +def _remove_cache_and_update_lock(cache_path=None): + lock_path = _get_cache_clear_lock_path(cache_path=cache_path) + try: + clear_lock_time = os.path.getmtime(lock_path) + except FileNotFoundError: + clear_lock_time = None + if ( + clear_lock_time is None # first time + or clear_lock_time + _CACHE_CLEAR_THRESHOLD <= time.time() + ): + if not _touch(lock_path): + # First make sure that as few as possible other cleanup jobs also + # get started. There is still a race condition but it's probably + # not a big problem. + return False + + clear_inactive_cache(cache_path=cache_path) + + +def _get_hashed_path(hashed_grammar, path, cache_path=None): + directory = _get_cache_directory_path(cache_path=cache_path) + + file_hash = hashlib.sha256(str(path).encode("utf-8")).hexdigest() + return os.path.join(directory, '%s-%s.pkl' % (hashed_grammar, file_hash)) + + +def _get_cache_directory_path(cache_path=None): + if cache_path is None: + cache_path = _default_cache_path + directory = cache_path.joinpath(_VERSION_TAG) + if not directory.exists(): + os.makedirs(directory) + return directory diff --git a/lib/python3.12/site-packages/parso/file_io.py b/lib/python3.12/site-packages/parso/file_io.py new file mode 100644 index 0000000000000000000000000000000000000000..568ce9d48c5e74be7ea36b200cb19bd1296dd524 --- /dev/null +++ b/lib/python3.12/site-packages/parso/file_io.py @@ -0,0 +1,38 @@ +import os +from pathlib import Path +from typing import Union + + +class FileIO: + def __init__(self, path: Union[os.PathLike, str]): + if isinstance(path, str): + path = Path(path) + self.path = path + + def read(self): # Returns bytes/str + # We would like to read unicode here, but we cannot, because we are not + # sure if it is a valid unicode file. Therefore just read whatever is + # here. + with open(self.path, 'rb') as f: + return f.read() + + def get_last_modified(self): + """ + Returns float - timestamp or None, if path doesn't exist. + """ + try: + return os.path.getmtime(self.path) + except FileNotFoundError: + return None + + def __repr__(self): + return '%s(%s)' % (self.__class__.__name__, self.path) + + +class KnownContentFileIO(FileIO): + def __init__(self, path, content): + super().__init__(path) + self._content = content + + def read(self): + return self._content diff --git a/lib/python3.12/site-packages/parso/grammar.py b/lib/python3.12/site-packages/parso/grammar.py new file mode 100644 index 0000000000000000000000000000000000000000..af4e3b670dc9a7fa4c53f42d2a3a4ab4ac3e29c0 --- /dev/null +++ b/lib/python3.12/site-packages/parso/grammar.py @@ -0,0 +1,273 @@ +import hashlib +import os +from typing import Generic, TypeVar, Union, Dict, Optional, Any +from pathlib import Path + +from parso._compatibility import is_pypy +from parso.pgen2 import generate_grammar +from parso.utils import split_lines, python_bytes_to_unicode, \ + PythonVersionInfo, parse_version_string +from parso.python.diff import DiffParser +from parso.python.tokenize import tokenize_lines, tokenize +from parso.python.token import PythonTokenTypes +from parso.cache import parser_cache, load_module, try_to_save_module +from parso.parser import BaseParser +from parso.python.parser import Parser as PythonParser +from parso.python.errors import ErrorFinderConfig +from parso.python import pep8 +from parso.file_io import FileIO, KnownContentFileIO +from parso.normalizer import RefactoringNormalizer, NormalizerConfig + +_loaded_grammars: Dict[str, 'Grammar'] = {} + +_NodeT = TypeVar("_NodeT") + + +class Grammar(Generic[_NodeT]): + """ + :py:func:`parso.load_grammar` returns instances of this class. + + Creating custom none-python grammars by calling this is not supported, yet. + + :param text: A BNF representation of your grammar. + """ + _start_nonterminal: str + _error_normalizer_config: Optional[ErrorFinderConfig] = None + _token_namespace: Any = None + _default_normalizer_config: NormalizerConfig = pep8.PEP8NormalizerConfig() + + def __init__(self, text: str, *, tokenizer, parser=BaseParser, diff_parser=None): + self._pgen_grammar = generate_grammar( + text, + token_namespace=self._get_token_namespace() + ) + self._parser = parser + self._tokenizer = tokenizer + self._diff_parser = diff_parser + self._hashed = hashlib.sha256(text.encode("utf-8")).hexdigest() + + def parse(self, + code: Union[str, bytes] = None, + *, + error_recovery=True, + path: Union[os.PathLike, str] = None, + start_symbol: str = None, + cache=False, + diff_cache=False, + cache_path: Union[os.PathLike, str] = None, + file_io: FileIO = None) -> _NodeT: + """ + If you want to parse a Python file you want to start here, most likely. + + If you need finer grained control over the parsed instance, there will be + other ways to access it. + + :param str code: A unicode or bytes string. When it's not possible to + decode bytes to a string, returns a + :py:class:`UnicodeDecodeError`. + :param bool error_recovery: If enabled, any code will be returned. If + it is invalid, it will be returned as an error node. If disabled, + you will get a ParseError when encountering syntax errors in your + code. + :param str start_symbol: The grammar rule (nonterminal) that you want + to parse. Only allowed to be used when error_recovery is False. + :param str path: The path to the file you want to open. Only needed for caching. + :param bool cache: Keeps a copy of the parser tree in RAM and on disk + if a path is given. Returns the cached trees if the corresponding + files on disk have not changed. Note that this stores pickle files + on your file system (e.g. for Linux in ``~/.cache/parso/``). + :param bool diff_cache: Diffs the cached python module against the new + code and tries to parse only the parts that have changed. Returns + the same (changed) module that is found in cache. Using this option + requires you to not do anything anymore with the cached modules + under that path, because the contents of it might change. This + option is still somewhat experimental. If you want stability, + please don't use it. + :param bool cache_path: If given saves the parso cache in this + directory. If not given, defaults to the default cache places on + each platform. + + :return: A subclass of :py:class:`parso.tree.NodeOrLeaf`. Typically a + :py:class:`parso.python.tree.Module`. + """ + if code is None and path is None and file_io is None: + raise TypeError("Please provide either code or a path.") + + if isinstance(path, str): + path = Path(path) + if isinstance(cache_path, str): + cache_path = Path(cache_path) + + if start_symbol is None: + start_symbol = self._start_nonterminal + + if error_recovery and start_symbol != 'file_input': + raise NotImplementedError("This is currently not implemented.") + + if file_io is None: + if code is None: + file_io = FileIO(path) # type: ignore[arg-type] + else: + file_io = KnownContentFileIO(path, code) + + if cache and file_io.path is not None: + module_node = load_module(self._hashed, file_io, cache_path=cache_path) + if module_node is not None: + return module_node # type: ignore[no-any-return] + + if code is None: + code = file_io.read() + code = python_bytes_to_unicode(code) + + lines = split_lines(code, keepends=True) + if diff_cache: + if self._diff_parser is None: + raise TypeError("You have to define a diff parser to be able " + "to use this option.") + try: + module_cache_item = parser_cache[self._hashed][file_io.path] + except KeyError: + pass + else: + module_node = module_cache_item.node + old_lines = module_cache_item.lines + if old_lines == lines: + return module_node # type: ignore[no-any-return] + + new_node = self._diff_parser( + self._pgen_grammar, self._tokenizer, module_node + ).update( + old_lines=old_lines, + new_lines=lines + ) + try_to_save_module(self._hashed, file_io, new_node, lines, + # Never pickle in pypy, it's slow as hell. + pickling=cache and not is_pypy, + cache_path=cache_path) + return new_node # type: ignore[no-any-return] + + tokens = self._tokenizer(lines) + + p = self._parser( + self._pgen_grammar, + error_recovery=error_recovery, + start_nonterminal=start_symbol + ) + root_node = p.parse(tokens=tokens) + + if cache or diff_cache: + try_to_save_module(self._hashed, file_io, root_node, lines, + # Never pickle in pypy, it's slow as hell. + pickling=cache and not is_pypy, + cache_path=cache_path) + return root_node # type: ignore[no-any-return] + + def _get_token_namespace(self): + ns = self._token_namespace + if ns is None: + raise ValueError("The token namespace should be set.") + return ns + + def iter_errors(self, node): + """ + Given a :py:class:`parso.tree.NodeOrLeaf` returns a generator of + :py:class:`parso.normalizer.Issue` objects. For Python this is + a list of syntax/indentation errors. + """ + if self._error_normalizer_config is None: + raise ValueError("No error normalizer specified for this grammar.") + + return self._get_normalizer_issues(node, self._error_normalizer_config) + + def refactor(self, base_node, node_to_str_map): + return RefactoringNormalizer(node_to_str_map).walk(base_node) + + def _get_normalizer(self, normalizer_config): + if normalizer_config is None: + normalizer_config = self._default_normalizer_config + if normalizer_config is None: + raise ValueError("You need to specify a normalizer, because " + "there's no default normalizer for this tree.") + return normalizer_config.create_normalizer(self) + + def _normalize(self, node, normalizer_config=None): + """ + TODO this is not public, yet. + The returned code will be normalized, e.g. PEP8 for Python. + """ + normalizer = self._get_normalizer(normalizer_config) + return normalizer.walk(node) + + def _get_normalizer_issues(self, node, normalizer_config=None): + normalizer = self._get_normalizer(normalizer_config) + normalizer.walk(node) + return normalizer.issues + + def __repr__(self): + nonterminals = self._pgen_grammar.nonterminal_to_dfas.keys() + txt = ' '.join(list(nonterminals)[:3]) + ' ...' + return '<%s:%s>' % (self.__class__.__name__, txt) + + +class PythonGrammar(Grammar): + _error_normalizer_config = ErrorFinderConfig() + _token_namespace = PythonTokenTypes + _start_nonterminal = 'file_input' + + def __init__(self, version_info: PythonVersionInfo, bnf_text: str): + super().__init__( + bnf_text, + tokenizer=self._tokenize_lines, + parser=PythonParser, + diff_parser=DiffParser + ) + self.version_info = version_info + + def _tokenize_lines(self, lines, **kwargs): + return tokenize_lines(lines, version_info=self.version_info, **kwargs) + + def _tokenize(self, code): + # Used by Jedi. + return tokenize(code, version_info=self.version_info) + + +def load_grammar(*, version: str = None, path: str = None): + """ + Loads a :py:class:`parso.Grammar`. The default version is the current Python + version. + + :param str version: A python version string, e.g. ``version='3.8'``. + :param str path: A path to a grammar file + """ + # NOTE: this (3, 14) should be updated to the latest version parso supports. + # (if this doesn't happen, users will get older syntaxes and spurious warnings) + passed_version_info = parse_version_string(version) + version_info = min(passed_version_info, PythonVersionInfo(3, 14)) + + # # NOTE: this is commented out until parso properly supports newer Python grammars. + # if passed_version_info != version_info: + # warnings.warn('parso does not support %s.%s yet.' % ( + # passed_version_info.major, passed_version_info.minor + # )) + + file = path or os.path.join( + 'python', + 'grammar%s%s.txt' % (version_info.major, version_info.minor) + ) + + global _loaded_grammars + path = os.path.join(os.path.dirname(__file__), file) + try: + return _loaded_grammars[path] + except KeyError: + try: + with open(path) as f: + bnf_text = f.read() + + grammar = PythonGrammar(version_info, bnf_text) + return _loaded_grammars.setdefault(path, grammar) + except FileNotFoundError: + message = "Python version %s.%s is currently not supported." % ( + version_info.major, version_info.minor + ) + raise NotImplementedError(message) diff --git a/lib/python3.12/site-packages/parso/normalizer.py b/lib/python3.12/site-packages/parso/normalizer.py new file mode 100644 index 0000000000000000000000000000000000000000..a95f029eb80c9acd937bdb39342c2ba19709e9a5 --- /dev/null +++ b/lib/python3.12/site-packages/parso/normalizer.py @@ -0,0 +1,198 @@ +from contextlib import contextmanager +from typing import Dict, List + + +class _NormalizerMeta(type): + def __new__(cls, name, bases, dct): + new_cls = type.__new__(cls, name, bases, dct) + new_cls.rule_value_classes = {} + new_cls.rule_type_classes = {} + return new_cls + + +class Normalizer(metaclass=_NormalizerMeta): + _rule_type_instances: Dict[str, List[type]] = {} + _rule_value_instances: Dict[str, List[type]] = {} + + def __init__(self, grammar, config): + self.grammar = grammar + self._config = config + self.issues = [] + + self._rule_type_instances = self._instantiate_rules('rule_type_classes') + self._rule_value_instances = self._instantiate_rules('rule_value_classes') + + def _instantiate_rules(self, attr): + dct = {} + for base in type(self).mro(): + rules_map = getattr(base, attr, {}) + for type_, rule_classes in rules_map.items(): + new = [rule_cls(self) for rule_cls in rule_classes] + dct.setdefault(type_, []).extend(new) + return dct + + def walk(self, node): + self.initialize(node) + value = self.visit(node) + self.finalize() + return value + + def visit(self, node): + try: + children = node.children + except AttributeError: + return self.visit_leaf(node) + else: + with self.visit_node(node): + return ''.join(self.visit(child) for child in children) + + @contextmanager + def visit_node(self, node): + self._check_type_rules(node) + yield + + def _check_type_rules(self, node): + for rule in self._rule_type_instances.get(node.type, []): + rule.feed_node(node) + + def visit_leaf(self, leaf): + self._check_type_rules(leaf) + + for rule in self._rule_value_instances.get(leaf.value, []): + rule.feed_node(leaf) + + return leaf.prefix + leaf.value + + def initialize(self, node): + pass + + def finalize(self): + pass + + def add_issue(self, node, code, message): + issue = Issue(node, code, message) + if issue not in self.issues: + self.issues.append(issue) + return True + + @classmethod + def register_rule(cls, *, value=None, values=(), type=None, types=()): + """ + Use it as a class decorator:: + + normalizer = Normalizer('grammar', 'config') + @normalizer.register_rule(value='foo') + class MyRule(Rule): + error_code = 42 + """ + values = list(values) + types = list(types) + if value is not None: + values.append(value) + if type is not None: + types.append(type) + + if not values and not types: + raise ValueError("You must register at least something.") + + def decorator(rule_cls): + for v in values: + cls.rule_value_classes.setdefault(v, []).append(rule_cls) + for t in types: + cls.rule_type_classes.setdefault(t, []).append(rule_cls) + return rule_cls + + return decorator + + +class NormalizerConfig: + normalizer_class = Normalizer + + def create_normalizer(self, grammar): + if self.normalizer_class is None: + return None + + return self.normalizer_class(grammar, self) + + +class Issue: + def __init__(self, node, code, message): + self.code = code + """ + An integer code that stands for the type of error. + """ + self.message = message + """ + A message (string) for the issue. + """ + self.start_pos = node.start_pos + """ + The start position position of the error as a tuple (line, column). As + always in |parso| the first line is 1 and the first column 0. + """ + self.end_pos = node.end_pos + + def __eq__(self, other): + return self.start_pos == other.start_pos and self.code == other.code + + def __ne__(self, other): + return not self.__eq__(other) + + def __hash__(self): + return hash((self.code, self.start_pos)) + + def __repr__(self): + return '<%s: %s>' % (self.__class__.__name__, self.code) + + +class Rule: + code: int + message: str + + def __init__(self, normalizer): + self._normalizer = normalizer + + def is_issue(self, node): + raise NotImplementedError() + + def get_node(self, node): + return node + + def _get_message(self, message, node): + if message is None: + message = self.message + if message is None: + raise ValueError("The message on the class is not set.") + return message + + def add_issue(self, node, code=None, message=None): + if code is None: + code = self.code + if code is None: + raise ValueError("The error code on the class is not set.") + + message = self._get_message(message, node) + + self._normalizer.add_issue(node, code, message) + + def feed_node(self, node): + if self.is_issue(node): + issue_node = self.get_node(node) + self.add_issue(issue_node) + + +class RefactoringNormalizer(Normalizer): + def __init__(self, node_to_str_map): + self._node_to_str_map = node_to_str_map + + def visit(self, node): + try: + return self._node_to_str_map[node] + except KeyError: + return super().visit(node) + + def visit_leaf(self, leaf): + try: + return self._node_to_str_map[leaf] + except KeyError: + return super().visit_leaf(leaf) diff --git a/lib/python3.12/site-packages/parso/parser.py b/lib/python3.12/site-packages/parso/parser.py new file mode 100644 index 0000000000000000000000000000000000000000..37466435dd6a1d61de07cfaa77e621f9ddcef3d4 --- /dev/null +++ b/lib/python3.12/site-packages/parso/parser.py @@ -0,0 +1,210 @@ +# Copyright 2004-2005 Elemental Security, Inc. All Rights Reserved. +# Licensed to PSF under a Contributor Agreement. + +# Modifications: +# Copyright David Halter and Contributors +# Modifications are dual-licensed: MIT and PSF. +# 99% of the code is different from pgen2, now. + +""" +The ``Parser`` tries to convert the available Python code in an easy to read +format, something like an abstract syntax tree. The classes who represent this +tree, are sitting in the :mod:`parso.tree` module. + +The Python module ``tokenize`` is a very important part in the ``Parser``, +because it splits the code into different words (tokens). Sometimes it looks a +bit messy. Sorry for that! You might ask now: "Why didn't you use the ``ast`` +module for this? Well, ``ast`` does a very good job understanding proper Python +code, but fails to work as soon as there's a single line of broken code. + +There's one important optimization that needs to be known: Statements are not +being parsed completely. ``Statement`` is just a representation of the tokens +within the statement. This lowers memory usage and cpu time and reduces the +complexity of the ``Parser`` (there's another parser sitting inside +``Statement``, which produces ``Array`` and ``Call``). +""" +from typing import Dict, Type + +from parso import tree +from parso.pgen2.generator import ReservedString + + +class ParserSyntaxError(Exception): + """ + Contains error information about the parser tree. + + May be raised as an exception. + """ + def __init__(self, message, error_leaf): + self.message = message + self.error_leaf = error_leaf + + +class InternalParseError(Exception): + """ + Exception to signal the parser is stuck and error recovery didn't help. + Basically this shouldn't happen. It's a sign that something is really + wrong. + """ + + def __init__(self, msg, type_, value, start_pos): + Exception.__init__(self, "%s: type=%r, value=%r, start_pos=%r" % + (msg, type_.name, value, start_pos)) + self.msg = msg + self.type = type + self.value = value + self.start_pos = start_pos + + +class Stack(list): + def _allowed_transition_names_and_token_types(self): + def iterate(): + # An API just for Jedi. + for stack_node in reversed(self): + for transition in stack_node.dfa.transitions: + if isinstance(transition, ReservedString): + yield transition.value + else: + yield transition # A token type + + if not stack_node.dfa.is_final: + break + + return list(iterate()) + + +class StackNode: + def __init__(self, dfa): + self.dfa = dfa + self.nodes = [] + + @property + def nonterminal(self): + return self.dfa.from_rule + + def __repr__(self): + return '%s(%s, %s)' % (self.__class__.__name__, self.dfa, self.nodes) + + +def _token_to_transition(grammar, type_, value): + # Map from token to label + if type_.value.contains_syntax: + # Check for reserved words (keywords) + try: + return grammar.reserved_syntax_strings[value] + except KeyError: + pass + + return type_ + + +class BaseParser: + """Parser engine. + + A Parser instance contains state pertaining to the current token + sequence, and should not be used concurrently by different threads + to parse separate token sequences. + + See python/tokenize.py for how to get input tokens by a string. + + When a syntax error occurs, error_recovery() is called. + """ + + node_map: Dict[str, Type[tree.BaseNode]] = {} + default_node = tree.Node + + leaf_map: Dict[str, Type[tree.Leaf]] = {} + default_leaf = tree.Leaf + + def __init__(self, pgen_grammar, start_nonterminal='file_input', error_recovery=False): + self._pgen_grammar = pgen_grammar + self._start_nonterminal = start_nonterminal + self._error_recovery = error_recovery + + def parse(self, tokens): + first_dfa = self._pgen_grammar.nonterminal_to_dfas[self._start_nonterminal][0] + self.stack = Stack([StackNode(first_dfa)]) + + for token in tokens: + self._add_token(token) + + while True: + tos = self.stack[-1] + if not tos.dfa.is_final: + # We never broke out -- EOF is too soon -- Unfinished statement. + # However, the error recovery might have added the token again, if + # the stack is empty, we're fine. + raise InternalParseError( + "incomplete input", token.type, token.string, token.start_pos + ) + + if len(self.stack) > 1: + self._pop() + else: + return self.convert_node(tos.nonterminal, tos.nodes) + + def error_recovery(self, token): + if self._error_recovery: + raise NotImplementedError("Error Recovery is not implemented") + else: + type_, value, start_pos, prefix = token + error_leaf = tree.ErrorLeaf(type_, value, start_pos, prefix) + raise ParserSyntaxError('SyntaxError: invalid syntax', error_leaf) + + def convert_node(self, nonterminal, children): + try: + node = self.node_map[nonterminal](children) + except KeyError: + node = self.default_node(nonterminal, children) + return node + + def convert_leaf(self, type_, value, prefix, start_pos): + try: + return self.leaf_map[type_](value, start_pos, prefix) + except KeyError: + return self.default_leaf(value, start_pos, prefix) + + def _add_token(self, token): + """ + This is the only core function for parsing. Here happens basically + everything. Everything is well prepared by the parser generator and we + only apply the necessary steps here. + """ + grammar = self._pgen_grammar + stack = self.stack + type_, value, start_pos, prefix = token + transition = _token_to_transition(grammar, type_, value) + + while True: + try: + plan = stack[-1].dfa.transitions[transition] + break + except KeyError: + if stack[-1].dfa.is_final: + self._pop() + else: + self.error_recovery(token) + return + except IndexError: + raise InternalParseError("too much input", type_, value, start_pos) + + stack[-1].dfa = plan.next_dfa + + for push in plan.dfa_pushes: + stack.append(StackNode(push)) + + leaf = self.convert_leaf(type_, value, prefix, start_pos) + stack[-1].nodes.append(leaf) + + def _pop(self): + tos = self.stack.pop() + # If there's exactly one child, return that child instead of + # creating a new node. We still create expr_stmt and + # file_input though, because a lot of Jedi depends on its + # logic. + if len(tos.nodes) == 1: + new_node = tos.nodes[0] + else: + new_node = self.convert_node(tos.dfa.from_rule, tos.nodes) + + self.stack[-1].nodes.append(new_node) diff --git a/lib/python3.12/site-packages/parso/pgen2/__init__.py b/lib/python3.12/site-packages/parso/pgen2/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..d4d9dcdc49983a06bc44c48fcbd6f9a097115b97 --- /dev/null +++ b/lib/python3.12/site-packages/parso/pgen2/__init__.py @@ -0,0 +1,10 @@ +# Copyright 2004-2005 Elemental Security, Inc. All Rights Reserved. +# Licensed to PSF under a Contributor Agreement. + +# Modifications: +# Copyright 2006 Google, Inc. All Rights Reserved. +# Licensed to PSF under a Contributor Agreement. +# Copyright 2014 David Halter and Contributors +# Modifications are dual-licensed: MIT and PSF. + +from parso.pgen2.generator import generate_grammar diff --git a/lib/python3.12/site-packages/parso/pgen2/__pycache__/__init__.cpython-312.pyc b/lib/python3.12/site-packages/parso/pgen2/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d44a515a50cab5178713648c4eb9f05eda9449ee Binary files /dev/null and b/lib/python3.12/site-packages/parso/pgen2/__pycache__/__init__.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/parso/pgen2/__pycache__/generator.cpython-312.pyc b/lib/python3.12/site-packages/parso/pgen2/__pycache__/generator.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c5a951df53cc5316e8bfa9a622b14f4722d79997 Binary files /dev/null and b/lib/python3.12/site-packages/parso/pgen2/__pycache__/generator.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/parso/pgen2/__pycache__/grammar_parser.cpython-312.pyc b/lib/python3.12/site-packages/parso/pgen2/__pycache__/grammar_parser.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e838c79e8b69a3044e90daf3c1eebf2ea00664b8 Binary files /dev/null and b/lib/python3.12/site-packages/parso/pgen2/__pycache__/grammar_parser.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/parso/pgen2/generator.py b/lib/python3.12/site-packages/parso/pgen2/generator.py new file mode 100644 index 0000000000000000000000000000000000000000..30f0b546b8ce9229817adbe1e2b862f521ec9128 --- /dev/null +++ b/lib/python3.12/site-packages/parso/pgen2/generator.py @@ -0,0 +1,382 @@ +# Copyright 2004-2005 Elemental Security, Inc. All Rights Reserved. +# Licensed to PSF under a Contributor Agreement. + +# Modifications: +# Copyright David Halter and Contributors +# Modifications are dual-licensed: MIT and PSF. + +""" +This module defines the data structures used to represent a grammar. + +Specifying grammars in pgen is possible with this grammar:: + + grammar: (NEWLINE | rule)* ENDMARKER + rule: NAME ':' rhs NEWLINE + rhs: items ('|' items)* + items: item+ + item: '[' rhs ']' | atom ['+' | '*'] + atom: '(' rhs ')' | NAME | STRING + +This grammar is self-referencing. + +This parser generator (pgen2) was created by Guido Rossum and used for lib2to3. +Most of the code has been refactored to make it more Pythonic. Since this was a +"copy" of the CPython Parser parser "pgen", there was some work needed to make +it more readable. It should also be slightly faster than the original pgen2, +because we made some optimizations. +""" + +from ast import literal_eval +from typing import TypeVar, Generic, Mapping, Sequence, Set, Union + +from parso.pgen2.grammar_parser import GrammarParser, NFAState + +_TokenTypeT = TypeVar("_TokenTypeT") + + +class Grammar(Generic[_TokenTypeT]): + """ + Once initialized, this class supplies the grammar tables for the + parsing engine implemented by parse.py. The parsing engine + accesses the instance variables directly. + + The only important part in this parsers are dfas and transitions between + dfas. + """ + + def __init__(self, + start_nonterminal: str, + rule_to_dfas: Mapping[str, Sequence['DFAState[_TokenTypeT]']], + reserved_syntax_strings: Mapping[str, 'ReservedString']): + self.nonterminal_to_dfas = rule_to_dfas + self.reserved_syntax_strings = reserved_syntax_strings + self.start_nonterminal = start_nonterminal + + +class DFAPlan: + """ + Plans are used for the parser to create stack nodes and do the proper + DFA state transitions. + """ + def __init__(self, next_dfa: 'DFAState', dfa_pushes: Sequence['DFAState'] = []): + self.next_dfa = next_dfa + self.dfa_pushes = dfa_pushes + + def __repr__(self): + return '%s(%s, %s)' % (self.__class__.__name__, self.next_dfa, self.dfa_pushes) + + +class DFAState(Generic[_TokenTypeT]): + """ + The DFAState object is the core class for pretty much anything. DFAState + are the vertices of an ordered graph while arcs and transitions are the + edges. + + Arcs are the initial edges, where most DFAStates are not connected and + transitions are then calculated to connect the DFA state machines that have + different nonterminals. + """ + def __init__(self, from_rule: str, nfa_set: Set[NFAState], final: NFAState): + assert isinstance(nfa_set, set) + assert isinstance(next(iter(nfa_set)), NFAState) + assert isinstance(final, NFAState) + self.from_rule = from_rule + self.nfa_set = nfa_set + # map from terminals/nonterminals to DFAState + self.arcs: Mapping[str, DFAState] = {} + # In an intermediary step we set these nonterminal arcs (which has the + # same structure as arcs). These don't contain terminals anymore. + self.nonterminal_arcs: Mapping[str, DFAState] = {} + + # Transitions are basically the only thing that the parser is using + # with is_final. Everyting else is purely here to create a parser. + self.transitions: Mapping[Union[_TokenTypeT, ReservedString], DFAPlan] = {} + self.is_final = final in nfa_set + + def add_arc(self, next_, label): + assert isinstance(label, str) + assert label not in self.arcs + assert isinstance(next_, DFAState) + self.arcs[label] = next_ + + def unifystate(self, old, new): + for label, next_ in self.arcs.items(): + if next_ is old: + self.arcs[label] = new + + def __eq__(self, other): + # Equality test -- ignore the nfa_set instance variable + assert isinstance(other, DFAState) + if self.is_final != other.is_final: + return False + # Can't just return self.arcs == other.arcs, because that + # would invoke this method recursively, with cycles... + if len(self.arcs) != len(other.arcs): + return False + for label, next_ in self.arcs.items(): + if next_ is not other.arcs.get(label): + return False + return True + + def __repr__(self): + return '<%s: %s is_final=%s>' % ( + self.__class__.__name__, self.from_rule, self.is_final + ) + + +class ReservedString: + """ + Most grammars will have certain keywords and operators that are mentioned + in the grammar as strings (e.g. "if") and not token types (e.g. NUMBER). + This class basically is the former. + """ + + def __init__(self, value: str): + self.value = value + + def __repr__(self): + return '%s(%s)' % (self.__class__.__name__, self.value) + + +def _simplify_dfas(dfas): + """ + This is not theoretically optimal, but works well enough. + Algorithm: repeatedly look for two states that have the same + set of arcs (same labels pointing to the same nodes) and + unify them, until things stop changing. + + dfas is a list of DFAState instances + """ + changes = True + while changes: + changes = False + for i, state_i in enumerate(dfas): + for j in range(i + 1, len(dfas)): + state_j = dfas[j] + if state_i == state_j: + del dfas[j] + for state in dfas: + state.unifystate(state_j, state_i) + changes = True + break + + +def _make_dfas(start, finish): + """ + Uses the powerset construction algorithm to create DFA states from sets of + NFA states. + + Also does state reduction if some states are not needed. + """ + # To turn an NFA into a DFA, we define the states of the DFA + # to correspond to *sets* of states of the NFA. Then do some + # state reduction. + assert isinstance(start, NFAState) + assert isinstance(finish, NFAState) + + def addclosure(nfa_state, base_nfa_set): + assert isinstance(nfa_state, NFAState) + if nfa_state in base_nfa_set: + return + base_nfa_set.add(nfa_state) + for nfa_arc in nfa_state.arcs: + if nfa_arc.nonterminal_or_string is None: + addclosure(nfa_arc.next, base_nfa_set) + + base_nfa_set = set() + addclosure(start, base_nfa_set) + states = [DFAState(start.from_rule, base_nfa_set, finish)] + for state in states: # NB states grows while we're iterating + arcs = {} + # Find state transitions and store them in arcs. + for nfa_state in state.nfa_set: + for nfa_arc in nfa_state.arcs: + if nfa_arc.nonterminal_or_string is not None: + nfa_set = arcs.setdefault(nfa_arc.nonterminal_or_string, set()) + addclosure(nfa_arc.next, nfa_set) + + # Now create the dfa's with no None's in arcs anymore. All Nones have + # been eliminated and state transitions (arcs) are properly defined, we + # just need to create the dfa's. + for nonterminal_or_string, nfa_set in arcs.items(): + for nested_state in states: + if nested_state.nfa_set == nfa_set: + # The DFA state already exists for this rule. + break + else: + nested_state = DFAState(start.from_rule, nfa_set, finish) + states.append(nested_state) + + state.add_arc(nested_state, nonterminal_or_string) + return states # List of DFAState instances; first one is start + + +def _dump_nfa(start, finish): + print("Dump of NFA for", start.from_rule) + todo = [start] + for i, state in enumerate(todo): + print(" State", i, state is finish and "(final)" or "") + for arc in state.arcs: + label, next_ = arc.nonterminal_or_string, arc.next + if next_ in todo: + j = todo.index(next_) + else: + j = len(todo) + todo.append(next_) + if label is None: + print(" -> %d" % j) + else: + print(" %s -> %d" % (label, j)) + + +def _dump_dfas(dfas): + print("Dump of DFA for", dfas[0].from_rule) + for i, state in enumerate(dfas): + print(" State", i, state.is_final and "(final)" or "") + for nonterminal, next_ in state.arcs.items(): + print(" %s -> %d" % (nonterminal, dfas.index(next_))) + + +def generate_grammar(bnf_grammar: str, token_namespace) -> Grammar: + """ + ``bnf_text`` is a grammar in extended BNF (using * for repetition, + for + at-least-once repetition, [] for optional parts, | for alternatives and () + for grouping). + + It's not EBNF according to ISO/IEC 14977. It's a dialect Python uses in its + own parser. + """ + rule_to_dfas = {} + start_nonterminal = None + for nfa_a, nfa_z in GrammarParser(bnf_grammar).parse(): + # _dump_nfa(nfa_a, nfa_z) + dfas = _make_dfas(nfa_a, nfa_z) + # _dump_dfas(dfas) + # oldlen = len(dfas) + _simplify_dfas(dfas) + # newlen = len(dfas) + rule_to_dfas[nfa_a.from_rule] = dfas + # print(nfa_a.from_rule, oldlen, newlen) + + if start_nonterminal is None: + start_nonterminal = nfa_a.from_rule + + reserved_strings: Mapping[str, ReservedString] = {} + for nonterminal, dfas in rule_to_dfas.items(): + for dfa_state in dfas: + for terminal_or_nonterminal, next_dfa in dfa_state.arcs.items(): + if terminal_or_nonterminal in rule_to_dfas: + dfa_state.nonterminal_arcs[terminal_or_nonterminal] = next_dfa + else: + transition = _make_transition( + token_namespace, + reserved_strings, + terminal_or_nonterminal + ) + dfa_state.transitions[transition] = DFAPlan(next_dfa) + + _calculate_tree_traversal(rule_to_dfas) + return Grammar(start_nonterminal, rule_to_dfas, reserved_strings) # type: ignore[arg-type] + + +def _make_transition(token_namespace, reserved_syntax_strings, label): + """ + Creates a reserved string ("if", "for", "*", ...) or returns the token type + (NUMBER, STRING, ...) for a given grammar terminal. + """ + if label[0].isalpha(): + # A named token (e.g. NAME, NUMBER, STRING) + return getattr(token_namespace, label) + else: + # Either a keyword or an operator + assert label[0] in ('"', "'"), label + assert not label.startswith('"""') and not label.startswith("'''") + value = literal_eval(label) + try: + return reserved_syntax_strings[value] + except KeyError: + r = reserved_syntax_strings[value] = ReservedString(value) + return r + + +def _calculate_tree_traversal(nonterminal_to_dfas): + """ + By this point we know how dfas can move around within a stack node, but we + don't know how we can add a new stack node (nonterminal transitions). + """ + # Map from grammar rule (nonterminal) name to a set of tokens. + first_plans = {} + + nonterminals = list(nonterminal_to_dfas.keys()) + nonterminals.sort() + for nonterminal in nonterminals: + if nonterminal not in first_plans: + _calculate_first_plans(nonterminal_to_dfas, first_plans, nonterminal) + + # Now that we have calculated the first terminals, we are sure that + # there is no left recursion. + + for dfas in nonterminal_to_dfas.values(): + for dfa_state in dfas: + transitions = dfa_state.transitions + for nonterminal, next_dfa in dfa_state.nonterminal_arcs.items(): + for transition, pushes in first_plans[nonterminal].items(): + if transition in transitions: + prev_plan = transitions[transition] + # Make sure these are sorted so that error messages are + # at least deterministic + choices = sorted([ + ( + prev_plan.dfa_pushes[0].from_rule + if prev_plan.dfa_pushes + else prev_plan.next_dfa.from_rule + ), + ( + pushes[0].from_rule + if pushes else next_dfa.from_rule + ), + ]) + raise ValueError( + "Rule %s is ambiguous; given a %s token, we " + "can't determine if we should evaluate %s or %s." + % ( + ( + dfa_state.from_rule, + transition, + ) + tuple(choices) + ) + ) + transitions[transition] = DFAPlan(next_dfa, pushes) + + +def _calculate_first_plans(nonterminal_to_dfas, first_plans, nonterminal): + """ + Calculates the first plan in the first_plans dictionary for every given + nonterminal. This is going to be used to know when to create stack nodes. + """ + dfas = nonterminal_to_dfas[nonterminal] + new_first_plans = {} + first_plans[nonterminal] = None # dummy to detect left recursion + # We only need to check the first dfa. All the following ones are not + # interesting to find first terminals. + state = dfas[0] + for transition, next_ in state.transitions.items(): + # It's a string. We have finally found a possible first token. + new_first_plans[transition] = [next_.next_dfa] + + for nonterminal2, next_ in state.nonterminal_arcs.items(): + # It's a nonterminal and we have either a left recursion issue + # in the grammar or we have to recurse. + try: + first_plans2 = first_plans[nonterminal2] + except KeyError: + first_plans2 = _calculate_first_plans(nonterminal_to_dfas, first_plans, nonterminal2) + else: + if first_plans2 is None: + raise ValueError("left recursion for rule %r" % nonterminal) + + for t, pushes in first_plans2.items(): + new_first_plans[t] = [next_] + pushes + + first_plans[nonterminal] = new_first_plans + return new_first_plans diff --git a/lib/python3.12/site-packages/parso/pgen2/grammar_parser.py b/lib/python3.12/site-packages/parso/pgen2/grammar_parser.py new file mode 100644 index 0000000000000000000000000000000000000000..582efb43acef0caa81e8bddea8c4fb4e2c03ed62 --- /dev/null +++ b/lib/python3.12/site-packages/parso/pgen2/grammar_parser.py @@ -0,0 +1,160 @@ +# Copyright 2004-2005 Elemental Security, Inc. All Rights Reserved. +# Licensed to PSF under a Contributor Agreement. + +# Modifications: +# Copyright David Halter and Contributors +# Modifications are dual-licensed: MIT and PSF. +from typing import Optional, Iterator, Tuple, List + +from parso.python.tokenize import tokenize +from parso.utils import parse_version_string +from parso.python.token import PythonTokenTypes + + +class NFAArc: + def __init__(self, next_: 'NFAState', nonterminal_or_string: Optional[str]): + self.next: NFAState = next_ + self.nonterminal_or_string: Optional[str] = nonterminal_or_string + + def __repr__(self): + return '<%s: %s>' % (self.__class__.__name__, self.nonterminal_or_string) + + +class NFAState: + def __init__(self, from_rule: str): + self.from_rule: str = from_rule + self.arcs: List[NFAArc] = [] + + def add_arc(self, next_, nonterminal_or_string=None): + assert nonterminal_or_string is None or isinstance(nonterminal_or_string, str) + assert isinstance(next_, NFAState) + self.arcs.append(NFAArc(next_, nonterminal_or_string)) + + def __repr__(self): + return '<%s: from %s>' % (self.__class__.__name__, self.from_rule) + + +class GrammarParser: + """ + The parser for Python grammar files. + """ + def __init__(self, bnf_grammar: str): + self._bnf_grammar = bnf_grammar + self.generator = tokenize( + bnf_grammar, + version_info=parse_version_string('3.9') + ) + self._gettoken() # Initialize lookahead + + def parse(self) -> Iterator[Tuple[NFAState, NFAState]]: + # grammar: (NEWLINE | rule)* ENDMARKER + while self.type != PythonTokenTypes.ENDMARKER: + while self.type == PythonTokenTypes.NEWLINE: + self._gettoken() + + # rule: NAME ':' rhs NEWLINE + self._current_rule_name = self._expect(PythonTokenTypes.NAME) + self._expect(PythonTokenTypes.OP, ':') + + a, z = self._parse_rhs() + self._expect(PythonTokenTypes.NEWLINE) + + yield a, z + + def _parse_rhs(self): + # rhs: items ('|' items)* + a, z = self._parse_items() + if self.value != "|": + return a, z + else: + aa = NFAState(self._current_rule_name) + zz = NFAState(self._current_rule_name) + while True: + # Add the possibility to go into the state of a and come back + # to finish. + aa.add_arc(a) + z.add_arc(zz) + if self.value != "|": + break + + self._gettoken() + a, z = self._parse_items() + return aa, zz + + def _parse_items(self): + # items: item+ + a, b = self._parse_item() + while self.type in (PythonTokenTypes.NAME, PythonTokenTypes.STRING) \ + or self.value in ('(', '['): + c, d = self._parse_item() + # Need to end on the next item. + b.add_arc(c) + b = d + return a, b + + def _parse_item(self): + # item: '[' rhs ']' | atom ['+' | '*'] + if self.value == "[": + self._gettoken() + a, z = self._parse_rhs() + self._expect(PythonTokenTypes.OP, ']') + # Make it also possible that there is no token and change the + # state. + a.add_arc(z) + return a, z + else: + a, z = self._parse_atom() + value = self.value + if value not in ("+", "*"): + return a, z + self._gettoken() + # Make it clear that we can go back to the old state and repeat. + z.add_arc(a) + if value == "+": + return a, z + else: + # The end state is the same as the beginning, nothing must + # change. + return a, a + + def _parse_atom(self): + # atom: '(' rhs ')' | NAME | STRING + if self.value == "(": + self._gettoken() + a, z = self._parse_rhs() + self._expect(PythonTokenTypes.OP, ')') + return a, z + elif self.type in (PythonTokenTypes.NAME, PythonTokenTypes.STRING): + a = NFAState(self._current_rule_name) + z = NFAState(self._current_rule_name) + # Make it clear that the state transition requires that value. + a.add_arc(z, self.value) + self._gettoken() + return a, z + else: + self._raise_error("expected (...) or NAME or STRING, got %s/%s", + self.type, self.value) + + def _expect(self, type_, value=None): + if self.type != type_: + self._raise_error("expected %s, got %s [%s]", + type_, self.type, self.value) + if value is not None and self.value != value: + self._raise_error("expected %s, got %s", value, self.value) + value = self.value + self._gettoken() + return value + + def _gettoken(self): + tup = next(self.generator) + self.type, self.value, self.begin, prefix = tup + + def _raise_error(self, msg, *args): + if args: + try: + msg = msg % args + except: + msg = " ".join([msg] + list(map(str, args))) + line = self._bnf_grammar.splitlines()[self.begin[0] - 1] + raise SyntaxError(msg, ('', self.begin[0], + self.begin[1], line)) diff --git a/lib/python3.12/site-packages/parso/py.typed b/lib/python3.12/site-packages/parso/py.typed new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/lib/python3.12/site-packages/parso/python/__init__.py b/lib/python3.12/site-packages/parso/python/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/lib/python3.12/site-packages/parso/python/__pycache__/__init__.cpython-312.pyc b/lib/python3.12/site-packages/parso/python/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2c72376ba0d3b9d65611bc18aaaa5df4dbbd5269 Binary files /dev/null and b/lib/python3.12/site-packages/parso/python/__pycache__/__init__.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/parso/python/__pycache__/diff.cpython-312.pyc b/lib/python3.12/site-packages/parso/python/__pycache__/diff.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c4d51ea816be0646366c6166dc0d556dbec42b8b Binary files /dev/null and b/lib/python3.12/site-packages/parso/python/__pycache__/diff.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/parso/python/__pycache__/errors.cpython-312.pyc b/lib/python3.12/site-packages/parso/python/__pycache__/errors.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..31274a68e1ca34f74b047762a61d4573f1787a0a Binary files /dev/null and b/lib/python3.12/site-packages/parso/python/__pycache__/errors.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/parso/python/__pycache__/parser.cpython-312.pyc b/lib/python3.12/site-packages/parso/python/__pycache__/parser.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ed0638e6eb79e9390490020f43505cd9c85ede3d Binary files /dev/null and b/lib/python3.12/site-packages/parso/python/__pycache__/parser.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/parso/python/__pycache__/pep8.cpython-312.pyc b/lib/python3.12/site-packages/parso/python/__pycache__/pep8.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..72603b5e99a8a4f35e12c9b769886bbb4ec96d30 Binary files /dev/null and b/lib/python3.12/site-packages/parso/python/__pycache__/pep8.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/parso/python/__pycache__/prefix.cpython-312.pyc b/lib/python3.12/site-packages/parso/python/__pycache__/prefix.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e6d68aaadd074df4470eb7d36b78ac611578cf72 Binary files /dev/null and b/lib/python3.12/site-packages/parso/python/__pycache__/prefix.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/parso/python/__pycache__/token.cpython-312.pyc b/lib/python3.12/site-packages/parso/python/__pycache__/token.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..de6002cf7b47f6121fce7788e9e51fe8ced118a6 Binary files /dev/null and b/lib/python3.12/site-packages/parso/python/__pycache__/token.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/parso/python/__pycache__/tokenize.cpython-312.pyc b/lib/python3.12/site-packages/parso/python/__pycache__/tokenize.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8bb9b57063671d30ba5b5526473db27e92d24dab Binary files /dev/null and b/lib/python3.12/site-packages/parso/python/__pycache__/tokenize.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/parso/python/__pycache__/tree.cpython-312.pyc b/lib/python3.12/site-packages/parso/python/__pycache__/tree.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f548084128966dd79b57ef8f447af429d02da04f Binary files /dev/null and b/lib/python3.12/site-packages/parso/python/__pycache__/tree.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/parso/python/diff.py b/lib/python3.12/site-packages/parso/python/diff.py new file mode 100644 index 0000000000000000000000000000000000000000..ba999fa4b54bf3b9fb6078bce5d19ca29e05cfd8 --- /dev/null +++ b/lib/python3.12/site-packages/parso/python/diff.py @@ -0,0 +1,886 @@ +""" +The diff parser is trying to be a faster version of the normal parser by trying +to reuse the nodes of a previous pass over the same file. This is also called +incremental parsing in parser literature. The difference is mostly that with +incremental parsing you get a range that needs to be reparsed. Here we +calculate that range ourselves by using difflib. After that it's essentially +incremental parsing. + +The biggest issue of this approach is that we reuse nodes in a mutable way. The +intial design and idea is quite problematic for this parser, but it is also +pretty fast. Measurements showed that just copying nodes in Python is simply +quite a bit slower (especially for big files >3 kLOC). Therefore we did not +want to get rid of the mutable nodes, since this is usually not an issue. + +This is by far the hardest software I ever wrote, exactly because the initial +design is crappy. When you have to account for a lot of mutable state, it +creates a ton of issues that you would otherwise not have. This file took +probably 3-6 months to write, which is insane for a parser. + +There is a fuzzer in that helps test this whole thing. Please use it if you +make changes here. If you run the fuzzer like:: + + test/fuzz_diff_parser.py random -n 100000 + +you can be pretty sure that everything is still fine. I sometimes run the +fuzzer up to 24h to make sure everything is still ok. +""" +import re +import difflib +from collections import namedtuple +import logging + +from parso.utils import split_lines +from parso.python.parser import Parser +from parso.python.tree import EndMarker +from parso.python.tokenize import PythonToken, BOM_UTF8_STRING +from parso.python.token import PythonTokenTypes + +LOG = logging.getLogger(__name__) +DEBUG_DIFF_PARSER = False + +_INDENTATION_TOKENS = 'INDENT', 'ERROR_DEDENT', 'DEDENT' + +NEWLINE = PythonTokenTypes.NEWLINE +DEDENT = PythonTokenTypes.DEDENT +NAME = PythonTokenTypes.NAME +ERROR_DEDENT = PythonTokenTypes.ERROR_DEDENT +ENDMARKER = PythonTokenTypes.ENDMARKER + + +def _is_indentation_error_leaf(node): + return node.type == 'error_leaf' and node.token_type in _INDENTATION_TOKENS + + +def _get_previous_leaf_if_indentation(leaf): + while leaf and _is_indentation_error_leaf(leaf): + leaf = leaf.get_previous_leaf() + return leaf + + +def _get_next_leaf_if_indentation(leaf): + while leaf and _is_indentation_error_leaf(leaf): + leaf = leaf.get_next_leaf() + return leaf + + +def _get_suite_indentation(tree_node): + return _get_indentation(tree_node.children[1]) + + +def _get_indentation(tree_node): + return tree_node.start_pos[1] + + +def _assert_valid_graph(node): + """ + Checks if the parent/children relationship is correct. + + This is a check that only runs during debugging/testing. + """ + try: + children = node.children + except AttributeError: + # Ignore INDENT is necessary, because indent/dedent tokens don't + # contain value/prefix and are just around, because of the tokenizer. + if node.type == 'error_leaf' and node.token_type in _INDENTATION_TOKENS: + assert not node.value + assert not node.prefix + return + + # Calculate the content between two start positions. + previous_leaf = _get_previous_leaf_if_indentation(node.get_previous_leaf()) + if previous_leaf is None: + content = node.prefix + previous_start_pos = 1, 0 + else: + assert previous_leaf.end_pos <= node.start_pos, \ + (previous_leaf, node) + + content = previous_leaf.value + node.prefix + previous_start_pos = previous_leaf.start_pos + + if '\n' in content or '\r' in content: + splitted = split_lines(content) + line = previous_start_pos[0] + len(splitted) - 1 + actual = line, len(splitted[-1]) + else: + actual = previous_start_pos[0], previous_start_pos[1] + len(content) + if content.startswith(BOM_UTF8_STRING) \ + and node.get_start_pos_of_prefix() == (1, 0): + # Remove the byte order mark + actual = actual[0], actual[1] - 1 + + assert node.start_pos == actual, (node.start_pos, actual) + else: + for child in children: + assert child.parent == node, (node, child) + _assert_valid_graph(child) + + +def _assert_nodes_are_equal(node1, node2): + try: + children1 = node1.children + except AttributeError: + assert not hasattr(node2, 'children'), (node1, node2) + assert node1.value == node2.value, (node1, node2) + assert node1.type == node2.type, (node1, node2) + assert node1.prefix == node2.prefix, (node1, node2) + assert node1.start_pos == node2.start_pos, (node1, node2) + return + else: + try: + children2 = node2.children + except AttributeError: + assert False, (node1, node2) + for n1, n2 in zip(children1, children2): + _assert_nodes_are_equal(n1, n2) + assert len(children1) == len(children2), '\n' + repr(children1) + '\n' + repr(children2) + + +def _get_debug_error_message(module, old_lines, new_lines): + current_lines = split_lines(module.get_code(), keepends=True) + current_diff = difflib.unified_diff(new_lines, current_lines) + old_new_diff = difflib.unified_diff(old_lines, new_lines) + import parso + return ( + "There's an issue with the diff parser. Please " + "report (parso v%s) - Old/New:\n%s\nActual Diff (May be empty):\n%s" + % (parso.__version__, ''.join(old_new_diff), ''.join(current_diff)) + ) + + +def _get_last_line(node_or_leaf): + last_leaf = node_or_leaf.get_last_leaf() + if _ends_with_newline(last_leaf): + return last_leaf.start_pos[0] + else: + n = last_leaf.get_next_leaf() + if n.type == 'endmarker' and '\n' in n.prefix: + # This is a very special case and has to do with error recovery in + # Parso. The problem is basically that there's no newline leaf at + # the end sometimes (it's required in the grammar, but not needed + # actually before endmarker, CPython just adds a newline to make + # source code pass the parser, to account for that Parso error + # recovery allows small_stmt instead of simple_stmt). + return last_leaf.end_pos[0] + 1 + return last_leaf.end_pos[0] + + +def _skip_dedent_error_leaves(leaf): + while leaf is not None and leaf.type == 'error_leaf' and leaf.token_type == 'DEDENT': + leaf = leaf.get_previous_leaf() + return leaf + + +def _ends_with_newline(leaf, suffix=''): + leaf = _skip_dedent_error_leaves(leaf) + + if leaf.type == 'error_leaf': + typ = leaf.token_type.lower() + else: + typ = leaf.type + + return typ == 'newline' or suffix.endswith('\n') or suffix.endswith('\r') + + +def _flows_finished(pgen_grammar, stack): + """ + if, while, for and try might not be finished, because another part might + still be parsed. + """ + for stack_node in stack: + if stack_node.nonterminal in ('if_stmt', 'while_stmt', 'for_stmt', 'try_stmt'): + return False + return True + + +def _func_or_class_has_suite(node): + if node.type == 'decorated': + node = node.children[-1] + if node.type in ('async_funcdef', 'async_stmt'): + node = node.children[-1] + return node.type in ('classdef', 'funcdef') and node.children[-1].type == 'suite' + + +def _suite_or_file_input_is_valid(pgen_grammar, stack): + if not _flows_finished(pgen_grammar, stack): + return False + + for stack_node in reversed(stack): + if stack_node.nonterminal == 'decorator': + # A decorator is only valid with the upcoming function. + return False + + if stack_node.nonterminal == 'suite': + # If only newline is in the suite, the suite is not valid, yet. + return len(stack_node.nodes) > 1 + # Not reaching a suite means that we're dealing with file_input levels + # where there's no need for a valid statement in it. It can also be empty. + return True + + +def _is_flow_node(node): + if node.type == 'async_stmt': + node = node.children[1] + try: + value = node.children[0].value + except AttributeError: + return False + return value in ('if', 'for', 'while', 'try', 'with') + + +class _PositionUpdatingFinished(Exception): + pass + + +def _update_positions(nodes, line_offset, last_leaf): + for node in nodes: + try: + children = node.children + except AttributeError: + # Is a leaf + node.line += line_offset + if node is last_leaf: + raise _PositionUpdatingFinished + else: + _update_positions(children, line_offset, last_leaf) + + +class DiffParser: + """ + An advanced form of parsing a file faster. Unfortunately comes with huge + side effects. It changes the given module. + """ + def __init__(self, pgen_grammar, tokenizer, module): + self._pgen_grammar = pgen_grammar + self._tokenizer = tokenizer + self._module = module + + def _reset(self): + self._copy_count = 0 + self._parser_count = 0 + + self._nodes_tree = _NodesTree(self._module) + + def update(self, old_lines, new_lines): + ''' + The algorithm works as follows: + + Equal: + - Assure that the start is a newline, otherwise parse until we get + one. + - Copy from parsed_until_line + 1 to max(i2 + 1) + - Make sure that the indentation is correct (e.g. add DEDENT) + - Add old and change positions + Insert: + - Parse from parsed_until_line + 1 to min(j2 + 1), hopefully not + much more. + + Returns the new module node. + ''' + LOG.debug('diff parser start') + # Reset the used names cache so they get regenerated. + self._module._used_names = None + + self._parser_lines_new = new_lines + + self._reset() + + line_length = len(new_lines) + sm = difflib.SequenceMatcher(None, old_lines, self._parser_lines_new) + opcodes = sm.get_opcodes() + LOG.debug('line_lengths old: %s; new: %s' % (len(old_lines), line_length)) + + for operation, i1, i2, j1, j2 in opcodes: + LOG.debug('-> code[%s] old[%s:%s] new[%s:%s]', + operation, i1 + 1, i2, j1 + 1, j2) + + if j2 == line_length and new_lines[-1] == '': + # The empty part after the last newline is not relevant. + j2 -= 1 + + if operation == 'equal': + line_offset = j1 - i1 + self._copy_from_old_parser(line_offset, i1 + 1, i2, j2) + elif operation == 'replace': + self._parse(until_line=j2) + elif operation == 'insert': + self._parse(until_line=j2) + else: + assert operation == 'delete' + + # With this action all change will finally be applied and we have a + # changed module. + self._nodes_tree.close() + + if DEBUG_DIFF_PARSER: + # If there is reasonable suspicion that the diff parser is not + # behaving well, this should be enabled. + try: + code = ''.join(new_lines) + assert self._module.get_code() == code + _assert_valid_graph(self._module) + without_diff_parser_module = Parser( + self._pgen_grammar, + error_recovery=True + ).parse(self._tokenizer(new_lines)) + _assert_nodes_are_equal(self._module, without_diff_parser_module) + except AssertionError: + print(_get_debug_error_message(self._module, old_lines, new_lines)) + raise + + last_pos = self._module.end_pos[0] + if last_pos != line_length: + raise Exception( + ('(%s != %s) ' % (last_pos, line_length)) + + _get_debug_error_message(self._module, old_lines, new_lines) + ) + LOG.debug('diff parser end') + return self._module + + def _enabled_debugging(self, old_lines, lines_new): + if self._module.get_code() != ''.join(lines_new): + LOG.warning('parser issue:\n%s\n%s', ''.join(old_lines), ''.join(lines_new)) + + def _copy_from_old_parser(self, line_offset, start_line_old, until_line_old, until_line_new): + last_until_line = -1 + while until_line_new > self._nodes_tree.parsed_until_line: + parsed_until_line_old = self._nodes_tree.parsed_until_line - line_offset + line_stmt = self._get_old_line_stmt(parsed_until_line_old + 1) + if line_stmt is None: + # Parse 1 line at least. We don't need more, because we just + # want to get into a state where the old parser has statements + # again that can be copied (e.g. not lines within parentheses). + self._parse(self._nodes_tree.parsed_until_line + 1) + else: + p_children = line_stmt.parent.children + index = p_children.index(line_stmt) + + if start_line_old == 1 \ + and p_children[0].get_first_leaf().prefix.startswith(BOM_UTF8_STRING): + # If there's a BOM in the beginning, just reparse. It's too + # complicated to account for it otherwise. + copied_nodes = [] + else: + from_ = self._nodes_tree.parsed_until_line + 1 + copied_nodes = self._nodes_tree.copy_nodes( + p_children[index:], + until_line_old, + line_offset + ) + # Match all the nodes that are in the wanted range. + if copied_nodes: + self._copy_count += 1 + + to = self._nodes_tree.parsed_until_line + + LOG.debug('copy old[%s:%s] new[%s:%s]', + copied_nodes[0].start_pos[0], + copied_nodes[-1].end_pos[0] - 1, from_, to) + else: + # We have copied as much as possible (but definitely not too + # much). Therefore we just parse a bit more. + self._parse(self._nodes_tree.parsed_until_line + 1) + # Since there are potential bugs that might loop here endlessly, we + # just stop here. + assert last_until_line != self._nodes_tree.parsed_until_line, last_until_line + last_until_line = self._nodes_tree.parsed_until_line + + def _get_old_line_stmt(self, old_line): + leaf = self._module.get_leaf_for_position((old_line, 0), include_prefixes=True) + + if _ends_with_newline(leaf): + leaf = leaf.get_next_leaf() + if leaf.get_start_pos_of_prefix()[0] == old_line: + node = leaf + while node.parent.type not in ('file_input', 'suite'): + node = node.parent + + # Make sure that if only the `else:` line of an if statement is + # copied that not the whole thing is going to be copied. + if node.start_pos[0] >= old_line: + return node + # Must be on the same line. Otherwise we need to parse that bit. + return None + + def _parse(self, until_line): + """ + Parses at least until the given line, but might just parse more until a + valid state is reached. + """ + last_until_line = 0 + while until_line > self._nodes_tree.parsed_until_line: + node = self._try_parse_part(until_line) + nodes = node.children + + self._nodes_tree.add_parsed_nodes(nodes, self._keyword_token_indents) + if self._replace_tos_indent is not None: + self._nodes_tree.indents[-1] = self._replace_tos_indent + + LOG.debug( + 'parse_part from %s to %s (to %s in part parser)', + nodes[0].get_start_pos_of_prefix()[0], + self._nodes_tree.parsed_until_line, + node.end_pos[0] - 1 + ) + # Since the tokenizer sometimes has bugs, we cannot be sure that + # this loop terminates. Therefore assert that there's always a + # change. + assert last_until_line != self._nodes_tree.parsed_until_line, last_until_line + last_until_line = self._nodes_tree.parsed_until_line + + def _try_parse_part(self, until_line): + """ + Sets up a normal parser that uses a spezialized tokenizer to only parse + until a certain position (or a bit longer if the statement hasn't + ended. + """ + self._parser_count += 1 + # TODO speed up, shouldn't copy the whole list all the time. + # memoryview? + parsed_until_line = self._nodes_tree.parsed_until_line + lines_after = self._parser_lines_new[parsed_until_line:] + tokens = self._diff_tokenize( + lines_after, + until_line, + line_offset=parsed_until_line + ) + self._active_parser = Parser( + self._pgen_grammar, + error_recovery=True + ) + return self._active_parser.parse(tokens=tokens) + + def _diff_tokenize(self, lines, until_line, line_offset=0): + was_newline = False + indents = self._nodes_tree.indents + initial_indentation_count = len(indents) + + tokens = self._tokenizer( + lines, + start_pos=(line_offset + 1, 0), + indents=indents, + is_first_token=line_offset == 0, + ) + stack = self._active_parser.stack + self._replace_tos_indent = None + self._keyword_token_indents = {} + # print('start', line_offset + 1, indents) + for token in tokens: + # print(token, indents) + typ = token.type + if typ == DEDENT: + if len(indents) < initial_indentation_count: + # We are done here, only thing that can come now is an + # endmarker or another dedented code block. + while True: + typ, string, start_pos, prefix = token = next(tokens) + if typ in (DEDENT, ERROR_DEDENT): + if typ == ERROR_DEDENT: + # We want to force an error dedent in the next + # parser/pass. To make this possible we just + # increase the location by one. + self._replace_tos_indent = start_pos[1] + 1 + pass + else: + break + + if '\n' in prefix or '\r' in prefix: + prefix = re.sub(r'[^\n\r]+\Z', '', prefix) + else: + assert start_pos[1] >= len(prefix), repr(prefix) + if start_pos[1] - len(prefix) == 0: + prefix = '' + yield PythonToken( + ENDMARKER, '', + start_pos, + prefix + ) + break + elif typ == NEWLINE and token.start_pos[0] >= until_line: + was_newline = True + elif was_newline: + was_newline = False + if len(indents) == initial_indentation_count: + # Check if the parser is actually in a valid suite state. + if _suite_or_file_input_is_valid(self._pgen_grammar, stack): + yield PythonToken(ENDMARKER, '', token.start_pos, '') + break + + if typ == NAME and token.string in ('class', 'def'): + self._keyword_token_indents[token.start_pos] = list(indents) + + yield token + + +class _NodesTreeNode: + _ChildrenGroup = namedtuple( + '_ChildrenGroup', + 'prefix children line_offset last_line_offset_leaf') + + def __init__(self, tree_node, parent=None, indentation=0): + self.tree_node = tree_node + self._children_groups = [] + self.parent = parent + self._node_children = [] + self.indentation = indentation + + def finish(self): + children = [] + for prefix, children_part, line_offset, last_line_offset_leaf in self._children_groups: + first_leaf = _get_next_leaf_if_indentation( + children_part[0].get_first_leaf() + ) + + first_leaf.prefix = prefix + first_leaf.prefix + if line_offset != 0: + try: + _update_positions( + children_part, line_offset, last_line_offset_leaf) + except _PositionUpdatingFinished: + pass + children += children_part + self.tree_node.children = children + # Reset the parents + for node in children: + node.parent = self.tree_node + + for node_child in self._node_children: + node_child.finish() + + def add_child_node(self, child_node): + self._node_children.append(child_node) + + def add_tree_nodes(self, prefix, children, line_offset=0, + last_line_offset_leaf=None): + if last_line_offset_leaf is None: + last_line_offset_leaf = children[-1].get_last_leaf() + group = self._ChildrenGroup( + prefix, children, line_offset, last_line_offset_leaf + ) + self._children_groups.append(group) + + def get_last_line(self, suffix): + line = 0 + if self._children_groups: + children_group = self._children_groups[-1] + last_leaf = _get_previous_leaf_if_indentation( + children_group.last_line_offset_leaf + ) + + line = last_leaf.end_pos[0] + children_group.line_offset + + # Newlines end on the next line, which means that they would cover + # the next line. That line is not fully parsed at this point. + if _ends_with_newline(last_leaf, suffix): + line -= 1 + line += len(split_lines(suffix)) - 1 + + if suffix and not suffix.endswith('\n') and not suffix.endswith('\r'): + # This is the end of a file (that doesn't end with a newline). + line += 1 + + if self._node_children: + return max(line, self._node_children[-1].get_last_line(suffix)) + return line + + def __repr__(self): + return '<%s: %s>' % (self.__class__.__name__, self.tree_node) + + +class _NodesTree: + def __init__(self, module): + self._base_node = _NodesTreeNode(module) + self._working_stack = [self._base_node] + self._module = module + self._prefix_remainder = '' + self.prefix = '' + self.indents = [0] + + @property + def parsed_until_line(self): + return self._working_stack[-1].get_last_line(self.prefix) + + def _update_insertion_node(self, indentation): + for node in reversed(list(self._working_stack)): + if node.indentation < indentation or node is self._working_stack[0]: + return node + self._working_stack.pop() + + def add_parsed_nodes(self, tree_nodes, keyword_token_indents): + old_prefix = self.prefix + tree_nodes = self._remove_endmarker(tree_nodes) + if not tree_nodes: + self.prefix = old_prefix + self.prefix + return + + assert tree_nodes[0].type != 'newline' + + node = self._update_insertion_node(tree_nodes[0].start_pos[1]) + assert node.tree_node.type in ('suite', 'file_input') + node.add_tree_nodes(old_prefix, tree_nodes) + # tos = Top of stack + self._update_parsed_node_tos(tree_nodes[-1], keyword_token_indents) + + def _update_parsed_node_tos(self, tree_node, keyword_token_indents): + if tree_node.type == 'suite': + def_leaf = tree_node.parent.children[0] + new_tos = _NodesTreeNode( + tree_node, + indentation=keyword_token_indents[def_leaf.start_pos][-1], + ) + new_tos.add_tree_nodes('', list(tree_node.children)) + + self._working_stack[-1].add_child_node(new_tos) + self._working_stack.append(new_tos) + + self._update_parsed_node_tos(tree_node.children[-1], keyword_token_indents) + elif _func_or_class_has_suite(tree_node): + self._update_parsed_node_tos(tree_node.children[-1], keyword_token_indents) + + def _remove_endmarker(self, tree_nodes): + """ + Helps cleaning up the tree nodes that get inserted. + """ + last_leaf = tree_nodes[-1].get_last_leaf() + is_endmarker = last_leaf.type == 'endmarker' + self._prefix_remainder = '' + if is_endmarker: + prefix = last_leaf.prefix + separation = max(prefix.rfind('\n'), prefix.rfind('\r')) + if separation > -1: + # Remove the whitespace part of the prefix after a newline. + # That is not relevant if parentheses were opened. Always parse + # until the end of a line. + last_leaf.prefix, self._prefix_remainder = \ + last_leaf.prefix[:separation + 1], last_leaf.prefix[separation + 1:] + + self.prefix = '' + + if is_endmarker: + self.prefix = last_leaf.prefix + + tree_nodes = tree_nodes[:-1] + return tree_nodes + + def _get_matching_indent_nodes(self, tree_nodes, is_new_suite): + # There might be a random dedent where we have to stop copying. + # Invalid indents are ok, because the parser handled that + # properly before. An invalid dedent can happen, because a few + # lines above there was an invalid indent. + node_iterator = iter(tree_nodes) + if is_new_suite: + yield next(node_iterator) + + first_node = next(node_iterator) + indent = _get_indentation(first_node) + if not is_new_suite and indent not in self.indents: + return + yield first_node + + for n in node_iterator: + if _get_indentation(n) != indent: + return + yield n + + def copy_nodes(self, tree_nodes, until_line, line_offset): + """ + Copies tree nodes from the old parser tree. + + Returns the number of tree nodes that were copied. + """ + if tree_nodes[0].type in ('error_leaf', 'error_node'): + # Avoid copying errors in the beginning. Can lead to a lot of + # issues. + return [] + + indentation = _get_indentation(tree_nodes[0]) + old_working_stack = list(self._working_stack) + old_prefix = self.prefix + old_indents = self.indents + self.indents = [i for i in self.indents if i <= indentation] + + self._update_insertion_node(indentation) + + new_nodes, self._working_stack, self.prefix, added_indents = self._copy_nodes( + list(self._working_stack), + tree_nodes, + until_line, + line_offset, + self.prefix, + ) + if new_nodes: + self.indents += added_indents + else: + self._working_stack = old_working_stack + self.prefix = old_prefix + self.indents = old_indents + return new_nodes + + def _copy_nodes(self, working_stack, nodes, until_line, line_offset, + prefix='', is_nested=False): + new_nodes = [] + added_indents = [] + + nodes = list(self._get_matching_indent_nodes( + nodes, + is_new_suite=is_nested, + )) + + new_prefix = '' + for node in nodes: + if node.start_pos[0] > until_line: + break + + if node.type == 'endmarker': + break + + if node.type == 'error_leaf' and node.token_type in ('DEDENT', 'ERROR_DEDENT'): + break + # TODO this check might take a bit of time for large files. We + # might want to change this to do more intelligent guessing or + # binary search. + if _get_last_line(node) > until_line: + # We can split up functions and classes later. + if _func_or_class_has_suite(node): + new_nodes.append(node) + break + try: + c = node.children + except AttributeError: + pass + else: + # This case basically appears with error recovery of one line + # suites like `def foo(): bar.-`. In this case we might not + # include a newline in the statement and we need to take care + # of that. + n = node + if n.type == 'decorated': + n = n.children[-1] + if n.type in ('async_funcdef', 'async_stmt'): + n = n.children[-1] + if n.type in ('classdef', 'funcdef'): + suite_node = n.children[-1] + else: + suite_node = c[-1] + + if suite_node.type in ('error_leaf', 'error_node'): + break + + new_nodes.append(node) + + # Pop error nodes at the end from the list + if new_nodes: + while new_nodes: + last_node = new_nodes[-1] + if (last_node.type in ('error_leaf', 'error_node') + or _is_flow_node(new_nodes[-1])): + # Error leafs/nodes don't have a defined start/end. Error + # nodes might not end with a newline (e.g. if there's an + # open `(`). Therefore ignore all of them unless they are + # succeeded with valid parser state. + # If we copy flows at the end, they might be continued + # after the copy limit (in the new parser). + # In this while loop we try to remove until we find a newline. + new_prefix = '' + new_nodes.pop() + while new_nodes: + last_node = new_nodes[-1] + if last_node.get_last_leaf().type == 'newline': + break + new_nodes.pop() + continue + if len(new_nodes) > 1 and new_nodes[-2].type == 'error_node': + # The problem here is that Parso error recovery sometimes + # influences nodes before this node. + # Since the new last node is an error node this will get + # cleaned up in the next while iteration. + new_nodes.pop() + continue + break + + if not new_nodes: + return [], working_stack, prefix, added_indents + + tos = working_stack[-1] + last_node = new_nodes[-1] + had_valid_suite_last = False + # Pop incomplete suites from the list + if _func_or_class_has_suite(last_node): + suite = last_node + while suite.type != 'suite': + suite = suite.children[-1] + + indent = _get_suite_indentation(suite) + added_indents.append(indent) + + suite_tos = _NodesTreeNode(suite, indentation=_get_indentation(last_node)) + # Don't need to pass line_offset here, it's already done by the + # parent. + suite_nodes, new_working_stack, new_prefix, ai = self._copy_nodes( + working_stack + [suite_tos], suite.children, until_line, line_offset, + is_nested=True, + ) + added_indents += ai + if len(suite_nodes) < 2: + # A suite only with newline is not valid. + new_nodes.pop() + new_prefix = '' + else: + assert new_nodes + tos.add_child_node(suite_tos) + working_stack = new_working_stack + had_valid_suite_last = True + + if new_nodes: + if not _ends_with_newline(new_nodes[-1].get_last_leaf()) and not had_valid_suite_last: + p = new_nodes[-1].get_next_leaf().prefix + # We are not allowed to remove the newline at the end of the + # line, otherwise it's going to be missing. This happens e.g. + # if a bracket is around before that moves newlines to + # prefixes. + new_prefix = split_lines(p, keepends=True)[0] + + if had_valid_suite_last: + last = new_nodes[-1] + if last.type == 'decorated': + last = last.children[-1] + if last.type in ('async_funcdef', 'async_stmt'): + last = last.children[-1] + last_line_offset_leaf = last.children[-2].get_last_leaf() + assert last_line_offset_leaf == ':' + else: + last_line_offset_leaf = new_nodes[-1].get_last_leaf() + tos.add_tree_nodes( + prefix, new_nodes, line_offset, last_line_offset_leaf, + ) + prefix = new_prefix + self._prefix_remainder = '' + + return new_nodes, working_stack, prefix, added_indents + + def close(self): + self._base_node.finish() + + # Add an endmarker. + try: + last_leaf = self._module.get_last_leaf() + except IndexError: + end_pos = [1, 0] + else: + last_leaf = _skip_dedent_error_leaves(last_leaf) + end_pos = list(last_leaf.end_pos) + lines = split_lines(self.prefix) + assert len(lines) > 0 + if len(lines) == 1: + if lines[0].startswith(BOM_UTF8_STRING) and end_pos == [1, 0]: + end_pos[1] -= 1 + end_pos[1] += len(lines[0]) + else: + end_pos[0] += len(lines) - 1 + end_pos[1] = len(lines[-1]) + + endmarker = EndMarker('', tuple(end_pos), self.prefix + self._prefix_remainder) + endmarker.parent = self._module + self._module.children.append(endmarker) diff --git a/lib/python3.12/site-packages/parso/python/errors.py b/lib/python3.12/site-packages/parso/python/errors.py new file mode 100644 index 0000000000000000000000000000000000000000..09c5047b612aa4e61fa8adfcbc314a610d96ae0c --- /dev/null +++ b/lib/python3.12/site-packages/parso/python/errors.py @@ -0,0 +1,1326 @@ +# -*- coding: utf-8 -*- +import codecs +import sys +import warnings +import re +from contextlib import contextmanager + +from parso.normalizer import Normalizer, NormalizerConfig, Issue, Rule +from parso.python.tokenize import _get_token_collection + +_BLOCK_STMTS = ('if_stmt', 'while_stmt', 'for_stmt', 'try_stmt', 'with_stmt') +_STAR_EXPR_PARENTS = ('testlist_star_expr', 'testlist_comp', 'exprlist') +# This is the maximal block size given by python. +_MAX_BLOCK_SIZE = 20 +_MAX_INDENT_COUNT = 100 +ALLOWED_FUTURES = ( + 'nested_scopes', 'generators', 'division', 'absolute_import', + 'with_statement', 'print_function', 'unicode_literals', 'generator_stop', +) +_COMP_FOR_TYPES = ('comp_for', 'sync_comp_for') + + +def _get_rhs_name(node, version): + type_ = node.type + if type_ == "lambdef": + return "lambda" + elif type_ == "atom": + comprehension = _get_comprehension_type(node) + first, second = node.children[:2] + if comprehension is not None: + return comprehension + elif second.type == "dictorsetmaker": + if version < (3, 8): + return "literal" + else: + if second.children[1] == ":" or second.children[0] == "**": + if version < (3, 10): + return "dict display" + else: + return "dict literal" + else: + return "set display" + elif ( + first == "(" + and (second == ")" + or (len(node.children) == 3 and node.children[1].type == "testlist_comp")) + ): + return "tuple" + elif first == "(": + return _get_rhs_name(_remove_parens(node), version=version) + elif first == "[": + return "list" + elif first == "{" and second == "}": + if version < (3, 10): + return "dict display" + else: + return "dict literal" + elif first == "{" and len(node.children) > 2: + return "set display" + elif type_ == "keyword": + if "yield" in node.value: + return "yield expression" + if version < (3, 8): + return "keyword" + else: + return str(node.value) + elif type_ == "operator" and node.value == "...": + if version < (3, 10): + return "Ellipsis" + else: + return "ellipsis" + elif type_ == "comparison": + return "comparison" + elif type_ in ("string", "number", "strings"): + return "literal" + elif type_ == "yield_expr": + return "yield expression" + elif type_ == "test": + return "conditional expression" + elif type_ in ("atom_expr", "power"): + if node.children[0] == "await": + return "await expression" + elif node.children[-1].type == "trailer": + trailer = node.children[-1] + if trailer.children[0] == "(": + return "function call" + elif trailer.children[0] == "[": + return "subscript" + elif trailer.children[0] == ".": + return "attribute" + elif ( + ("expr" in type_ and "star_expr" not in type_) # is a substring + or "_test" in type_ + or type_ in ("term", "factor") + ): + if version < (3, 10): + return "operator" + else: + return "expression" + elif type_ == "star_expr": + return "starred" + elif type_ == "testlist_star_expr": + return "tuple" + elif type_ == "fstring": + return "f-string expression" + return type_ # shouldn't reach here + + +def _iter_stmts(scope): + """ + Iterates over all statements and splits up simple_stmt. + """ + for child in scope.children: + if child.type == 'simple_stmt': + for child2 in child.children: + if child2.type == 'newline' or child2 == ';': + continue + yield child2 + else: + yield child + + +def _get_comprehension_type(atom): + first, second = atom.children[:2] + if second.type == 'testlist_comp' and second.children[1].type in _COMP_FOR_TYPES: + if first == '[': + return 'list comprehension' + else: + return 'generator expression' + elif second.type == 'dictorsetmaker' and second.children[-1].type in _COMP_FOR_TYPES: + if second.children[1] == ':': + return 'dict comprehension' + else: + return 'set comprehension' + return None + + +def _is_future_import(import_from): + # It looks like a __future__ import that is relative is still a future + # import. That feels kind of odd, but whatever. + # if import_from.level != 0: + # return False + from_names = import_from.get_from_names() + return [n.value for n in from_names] == ['__future__'] + + +def _remove_parens(atom): + """ + Returns the inner part of an expression like `(foo)`. Also removes nested + parens. + """ + try: + children = atom.children + except AttributeError: + pass + else: + if len(children) == 3 and children[0] == '(': + return _remove_parens(atom.children[1]) + return atom + + +def _skip_parens_bottom_up(node): + """ + Returns an ancestor node of an expression, skipping all levels of parens + bottom-up. + """ + while node.parent is not None: + node = node.parent + if node.type != 'atom' or node.children[0] != '(': + return node + return None + + +def _iter_params(parent_node): + return (n for n in parent_node.children if n.type == 'param' or n.type == 'operator') + + +def _is_future_import_first(import_from): + """ + Checks if the import is the first statement of a file. + """ + found_docstring = False + for stmt in _iter_stmts(import_from.get_root_node()): + if stmt.type == 'string' and not found_docstring: + continue + found_docstring = True + + if stmt == import_from: + return True + if stmt.type == 'import_from' and _is_future_import(stmt): + continue + return False + + +def _iter_definition_exprs_from_lists(exprlist): + def check_expr(child): + if child.type == 'atom': + if child.children[0] == '(': + testlist_comp = child.children[1] + if testlist_comp.type == 'testlist_comp': + yield from _iter_definition_exprs_from_lists(testlist_comp) + return + else: + # It's a paren that doesn't do anything, like 1 + (1) + yield from check_expr(testlist_comp) + return + elif child.children[0] == '[': + yield testlist_comp + return + yield child + + if exprlist.type in _STAR_EXPR_PARENTS: + for child in exprlist.children[::2]: + yield from check_expr(child) + else: + yield from check_expr(exprlist) + + +def _get_expr_stmt_definition_exprs(expr_stmt): + exprs = [] + for list_ in expr_stmt.children[:-2:2]: + if list_.type in ('testlist_star_expr', 'testlist'): + exprs += _iter_definition_exprs_from_lists(list_) + else: + exprs.append(list_) + return exprs + + +def _get_for_stmt_definition_exprs(for_stmt): + exprlist = for_stmt.children[1] + return list(_iter_definition_exprs_from_lists(exprlist)) + + +def _is_argument_comprehension(argument): + return argument.children[1].type in _COMP_FOR_TYPES + + +def _any_fstring_error(version, node): + if version < (3, 9) or node is None: + return False + if node.type == "error_node": + return any(child.type == "fstring_start" for child in node.children) + elif node.type == "fstring": + return True + else: + return node.search_ancestor("fstring") + + +class _Context: + def __init__(self, node, add_syntax_error, parent_context=None): + self.node = node + self.blocks = [] + self.parent_context = parent_context + self._used_name_dict = {} + self._global_names = [] + self._local_params_names = [] + self._nonlocal_names = [] + self._nonlocal_names_in_subscopes = [] + self._add_syntax_error = add_syntax_error + + def is_async_funcdef(self): + # Stupidly enough async funcdefs can have two different forms, + # depending if a decorator is used or not. + return self.is_function() \ + and self.node.parent.type in ('async_funcdef', 'async_stmt') + + def is_function(self): + return self.node.type == 'funcdef' + + def add_name(self, name): + parent_type = name.parent.type + if parent_type == 'trailer': + # We are only interested in first level names. + return + + if parent_type == 'global_stmt': + self._global_names.append(name) + elif parent_type == 'nonlocal_stmt': + self._nonlocal_names.append(name) + elif parent_type == 'funcdef': + self._local_params_names.extend( + [param.name.value for param in name.parent.get_params()] + ) + else: + self._used_name_dict.setdefault(name.value, []).append(name) + + def finalize(self): + """ + Returns a list of nonlocal names that need to be part of that scope. + """ + self._analyze_names(self._global_names, 'global') + self._analyze_names(self._nonlocal_names, 'nonlocal') + + global_name_strs = {n.value: n for n in self._global_names} + for nonlocal_name in self._nonlocal_names: + try: + global_name = global_name_strs[nonlocal_name.value] + except KeyError: + continue + + message = "name '%s' is nonlocal and global" % global_name.value + if global_name.start_pos < nonlocal_name.start_pos: + error_name = global_name + else: + error_name = nonlocal_name + self._add_syntax_error(error_name, message) + + nonlocals_not_handled = [] + for nonlocal_name in self._nonlocal_names_in_subscopes: + search = nonlocal_name.value + if search in self._local_params_names: + continue + if search in global_name_strs or self.parent_context is None: + message = "no binding for nonlocal '%s' found" % nonlocal_name.value + self._add_syntax_error(nonlocal_name, message) + elif not self.is_function() or \ + nonlocal_name.value not in self._used_name_dict: + nonlocals_not_handled.append(nonlocal_name) + return self._nonlocal_names + nonlocals_not_handled + + def _analyze_names(self, globals_or_nonlocals, type_): + def raise_(message): + self._add_syntax_error(base_name, message % (base_name.value, type_)) + + params = [] + if self.node.type == 'funcdef': + params = self.node.get_params() + + for base_name in globals_or_nonlocals: + found_global_or_nonlocal = False + # Somehow Python does it the reversed way. + for name in reversed(self._used_name_dict.get(base_name.value, [])): + if name.start_pos > base_name.start_pos: + # All following names don't have to be checked. + found_global_or_nonlocal = True + + parent = name.parent + if parent.type == 'param' and parent.name == name: + # Skip those here, these definitions belong to the next + # scope. + continue + + if name.is_definition(): + if parent.type == 'expr_stmt' \ + and parent.children[1].type == 'annassign': + if found_global_or_nonlocal: + # If it's after the global the error seems to be + # placed there. + base_name = name + raise_("annotated name '%s' can't be %s") + break + else: + message = "name '%s' is assigned to before %s declaration" + else: + message = "name '%s' is used prior to %s declaration" + + if not found_global_or_nonlocal: + raise_(message) + # Only add an error for the first occurence. + break + + for param in params: + if param.name.value == base_name.value: + raise_("name '%s' is parameter and %s"), + + @contextmanager + def add_block(self, node): + self.blocks.append(node) + yield + self.blocks.pop() + + def add_context(self, node): + return _Context(node, self._add_syntax_error, parent_context=self) + + def close_child_context(self, child_context): + self._nonlocal_names_in_subscopes += child_context.finalize() + + +class ErrorFinder(Normalizer): + """ + Searches for errors in the syntax tree. + """ + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self._error_dict = {} + self.version = self.grammar.version_info + + def initialize(self, node): + def create_context(node): + if node is None: + return None + + parent_context = create_context(node.parent) + if node.type in ('classdef', 'funcdef', 'file_input'): + return _Context(node, self._add_syntax_error, parent_context) + return parent_context + + self.context = create_context(node) or _Context(node, self._add_syntax_error) + self._indentation_count = 0 + + def visit(self, node): + if node.type == 'error_node': + with self.visit_node(node): + # Don't need to investigate the inners of an error node. We + # might find errors in there that should be ignored, because + # the error node itself already shows that there's an issue. + return '' + return super().visit(node) + + @contextmanager + def visit_node(self, node): + self._check_type_rules(node) + + if node.type in _BLOCK_STMTS: + with self.context.add_block(node): + if len(self.context.blocks) == _MAX_BLOCK_SIZE: + self._add_syntax_error(node, "too many statically nested blocks") + yield + return + elif node.type == 'suite': + self._indentation_count += 1 + if self._indentation_count == _MAX_INDENT_COUNT: + self._add_indentation_error(node.children[1], "too many levels of indentation") + + yield + + if node.type == 'suite': + self._indentation_count -= 1 + elif node.type in ('classdef', 'funcdef'): + context = self.context + self.context = context.parent_context + self.context.close_child_context(context) + + def visit_leaf(self, leaf): + if leaf.type == 'error_leaf': + if leaf.token_type in ('INDENT', 'ERROR_DEDENT'): + # Indents/Dedents itself never have a prefix. They are just + # "pseudo" tokens that get removed by the syntax tree later. + # Therefore in case of an error we also have to check for this. + spacing = list(leaf.get_next_leaf()._split_prefix())[-1] + if leaf.token_type == 'INDENT': + message = 'unexpected indent' + else: + message = 'unindent does not match any outer indentation level' + self._add_indentation_error(spacing, message) + else: + if leaf.value.startswith('\\'): + message = 'unexpected character after line continuation character' + else: + match = re.match('\\w{,2}("{1,3}|\'{1,3})', leaf.value) + if match is None: + message = 'invalid syntax' + if ( + self.version >= (3, 9) + and leaf.value in _get_token_collection( + self.version + ).always_break_tokens + ): + message = "f-string: " + message + else: + if len(match.group(1)) == 1: + message = 'EOL while scanning string literal' + else: + message = 'EOF while scanning triple-quoted string literal' + self._add_syntax_error(leaf, message) + return '' + elif leaf.value == ':': + parent = leaf.parent + if parent.type in ('classdef', 'funcdef'): + self.context = self.context.add_context(parent) + + # The rest is rule based. + return super().visit_leaf(leaf) + + def _add_indentation_error(self, spacing, message): + self.add_issue(spacing, 903, "IndentationError: " + message) + + def _add_syntax_error(self, node, message): + self.add_issue(node, 901, "SyntaxError: " + message) + + def add_issue(self, node, code, message): + # Overwrite the default behavior. + # Check if the issues are on the same line. + line = node.start_pos[0] + args = (code, message, node) + self._error_dict.setdefault(line, args) + + def finalize(self): + self.context.finalize() + + for code, message, node in self._error_dict.values(): + self.issues.append(Issue(node, code, message)) + + +class IndentationRule(Rule): + code = 903 + + def _get_message(self, message, node): + message = super()._get_message(message, node) + return "IndentationError: " + message + + +@ErrorFinder.register_rule(type='error_node') +class _ExpectIndentedBlock(IndentationRule): + message = 'expected an indented block' + + def get_node(self, node): + leaf = node.get_next_leaf() + return list(leaf._split_prefix())[-1] + + def is_issue(self, node): + # This is the beginning of a suite that is not indented. + return node.children[-1].type == 'newline' + + +class ErrorFinderConfig(NormalizerConfig): + normalizer_class = ErrorFinder + + +class SyntaxRule(Rule): + code = 901 + + def _get_message(self, message, node): + message = super()._get_message(message, node) + if ( + "f-string" not in message + and _any_fstring_error(self._normalizer.version, node) + ): + message = "f-string: " + message + return "SyntaxError: " + message + + +@ErrorFinder.register_rule(type='error_node') +class _InvalidSyntaxRule(SyntaxRule): + message = "invalid syntax" + fstring_message = "f-string: invalid syntax" + + def get_node(self, node): + return node.get_next_leaf() + + def is_issue(self, node): + error = node.get_next_leaf().type != 'error_leaf' + if ( + error + and _any_fstring_error(self._normalizer.version, node) + ): + self.add_issue(node, message=self.fstring_message) + else: + # Error leafs will be added later as an error. + return error + + +@ErrorFinder.register_rule(value='await') +class _AwaitOutsideAsync(SyntaxRule): + message = "'await' outside async function" + + def is_issue(self, leaf): + return not self._normalizer.context.is_async_funcdef() + + def get_error_node(self, node): + # Return the whole await statement. + return node.parent + + +@ErrorFinder.register_rule(value='break') +class _BreakOutsideLoop(SyntaxRule): + message = "'break' outside loop" + + def is_issue(self, leaf): + in_loop = False + for block in self._normalizer.context.blocks: + if block.type in ('for_stmt', 'while_stmt'): + in_loop = True + return not in_loop + + +@ErrorFinder.register_rule(value='continue') +class _ContinueChecks(SyntaxRule): + message = "'continue' not properly in loop" + message_in_finally = "'continue' not supported inside 'finally' clause" + + def is_issue(self, leaf): + in_loop = False + for block in self._normalizer.context.blocks: + if block.type in ('for_stmt', 'while_stmt'): + in_loop = True + if block.type == 'try_stmt': + last_block = block.children[-3] + if ( + last_block == "finally" + and leaf.start_pos > last_block.start_pos + and self._normalizer.version < (3, 8) + ): + self.add_issue(leaf, message=self.message_in_finally) + return False # Error already added + if not in_loop: + return True + + +@ErrorFinder.register_rule(value='from') +class _YieldFromCheck(SyntaxRule): + message = "'yield from' inside async function" + + def get_node(self, leaf): + return leaf.parent.parent # This is the actual yield statement. + + def is_issue(self, leaf): + return leaf.parent.type == 'yield_arg' \ + and self._normalizer.context.is_async_funcdef() + + +@ErrorFinder.register_rule(type='name') +class _NameChecks(SyntaxRule): + message = 'cannot assign to __debug__' + message_none = 'cannot assign to None' + + def is_issue(self, leaf): + self._normalizer.context.add_name(leaf) + + if leaf.value == '__debug__' and leaf.is_definition(): + return True + + +@ErrorFinder.register_rule(type='string') +class _StringChecks(SyntaxRule): + if sys.version_info < (3, 10): + message = "bytes can only contain ASCII literal characters." + else: + message = "bytes can only contain ASCII literal characters" + + def is_issue(self, leaf): + string_prefix = leaf.string_prefix.lower() + if 'b' in string_prefix \ + and any(c for c in leaf.value if ord(c) > 127): + # b'ä' + return True + + if 'r' not in string_prefix: + # Raw strings don't need to be checked if they have proper + # escaping. + + payload = leaf._get_payload() + if 'b' in string_prefix: + payload = payload.encode('utf-8') + func = codecs.escape_decode + else: + func = codecs.unicode_escape_decode + + try: + with warnings.catch_warnings(): + # The warnings from parsing strings are not relevant. + warnings.filterwarnings('ignore') + func(payload) + except UnicodeDecodeError as e: + self.add_issue(leaf, message='(unicode error) ' + str(e)) + except ValueError as e: + self.add_issue(leaf, message='(value error) ' + str(e)) + + +@ErrorFinder.register_rule(value='*') +class _StarCheck(SyntaxRule): + message = "named arguments must follow bare *" + + def is_issue(self, leaf): + params = leaf.parent + if params.type == 'parameters' and params: + after = params.children[params.children.index(leaf) + 1:] + after = [child for child in after + if child not in (',', ')') and not child.star_count] + return len(after) == 0 + + +@ErrorFinder.register_rule(value='**') +class _StarStarCheck(SyntaxRule): + # e.g. {**{} for a in [1]} + # TODO this should probably get a better end_pos including + # the next sibling of leaf. + message = "dict unpacking cannot be used in dict comprehension" + + def is_issue(self, leaf): + if leaf.parent.type == 'dictorsetmaker': + comp_for = leaf.get_next_sibling().get_next_sibling() + return comp_for is not None and comp_for.type in _COMP_FOR_TYPES + + +@ErrorFinder.register_rule(value='yield') +@ErrorFinder.register_rule(value='return') +class _ReturnAndYieldChecks(SyntaxRule): + message = "'return' with value in async generator" + message_async_yield = "'yield' inside async function" + + def get_node(self, leaf): + return leaf.parent + + def is_issue(self, leaf): + if self._normalizer.context.node.type != 'funcdef': + self.add_issue(self.get_node(leaf), message="'%s' outside function" % leaf.value) + elif self._normalizer.context.is_async_funcdef() \ + and any(self._normalizer.context.node.iter_yield_exprs()): + if leaf.value == 'return' and leaf.parent.type == 'return_stmt': + return True + + +@ErrorFinder.register_rule(type='strings') +class _BytesAndStringMix(SyntaxRule): + # e.g. 's' b'' + message = "cannot mix bytes and nonbytes literals" + + def _is_bytes_literal(self, string): + if string.type == 'fstring': + return False + return 'b' in string.string_prefix.lower() + + def is_issue(self, node): + first = node.children[0] + first_is_bytes = self._is_bytes_literal(first) + for string in node.children[1:]: + if first_is_bytes != self._is_bytes_literal(string): + return True + + +@ErrorFinder.register_rule(type='import_as_names') +class _TrailingImportComma(SyntaxRule): + # e.g. from foo import a, + message = "trailing comma not allowed without surrounding parentheses" + + def is_issue(self, node): + if node.children[-1] == ',' and node.parent.children[-1] != ')': + return True + + +@ErrorFinder.register_rule(type='import_from') +class _ImportStarInFunction(SyntaxRule): + message = "import * only allowed at module level" + + def is_issue(self, node): + return node.is_star_import() and self._normalizer.context.parent_context is not None + + +@ErrorFinder.register_rule(type='import_from') +class _FutureImportRule(SyntaxRule): + message = "from __future__ imports must occur at the beginning of the file" + + def is_issue(self, node): + if _is_future_import(node): + if not _is_future_import_first(node): + return True + + for from_name, future_name in node.get_paths(): + name = future_name.value + allowed_futures = list(ALLOWED_FUTURES) + if self._normalizer.version >= (3, 7): + allowed_futures.append('annotations') + if name == 'braces': + self.add_issue(node, message="not a chance") + elif name == 'barry_as_FLUFL': + m = "Seriously I'm not implementing this :) ~ Dave" + self.add_issue(node, message=m) + elif name not in allowed_futures: + message = "future feature %s is not defined" % name + self.add_issue(node, message=message) + + +@ErrorFinder.register_rule(type='star_expr') +class _StarExprRule(SyntaxRule): + message_iterable_unpacking = "iterable unpacking cannot be used in comprehension" + + def is_issue(self, node): + def check_delete_starred(node): + while node.parent is not None: + node = node.parent + if node.type == 'del_stmt': + return True + if node.type not in (*_STAR_EXPR_PARENTS, 'atom'): + return False + return False + + if self._normalizer.version >= (3, 9): + ancestor = node.parent + else: + ancestor = _skip_parens_bottom_up(node) + # starred expression not in tuple/list/set + if ancestor.type not in (*_STAR_EXPR_PARENTS, 'dictorsetmaker') \ + and not (ancestor.type == 'atom' and ancestor.children[0] != '('): + self.add_issue(node, message="can't use starred expression here") + return + + if check_delete_starred(node): + if self._normalizer.version >= (3, 9): + self.add_issue(node, message="cannot delete starred") + else: + self.add_issue(node, message="can't use starred expression here") + return + + if node.parent.type == 'testlist_comp': + # [*[] for a in [1]] + if node.parent.children[1].type in _COMP_FOR_TYPES: + self.add_issue(node, message=self.message_iterable_unpacking) + + +@ErrorFinder.register_rule(types=_STAR_EXPR_PARENTS) +class _StarExprParentRule(SyntaxRule): + def is_issue(self, node): + def is_definition(node, ancestor): + if ancestor is None: + return False + + type_ = ancestor.type + if type_ == 'trailer': + return False + + if type_ == 'expr_stmt': + return node.start_pos < ancestor.children[-1].start_pos + + return is_definition(node, ancestor.parent) + + if is_definition(node, node.parent): + args = [c for c in node.children if c != ','] + starred = [c for c in args if c.type == 'star_expr'] + if len(starred) > 1: + if self._normalizer.version < (3, 9): + message = "two starred expressions in assignment" + else: + message = "multiple starred expressions in assignment" + self.add_issue(starred[1], message=message) + elif starred: + count = args.index(starred[0]) + if count >= 256: + message = "too many expressions in star-unpacking assignment" + self.add_issue(starred[0], message=message) + + +@ErrorFinder.register_rule(type='annassign') +class _AnnotatorRule(SyntaxRule): + # True: int + # {}: float + message = "illegal target for annotation" + + def get_node(self, node): + return node.parent + + def is_issue(self, node): + type_ = None + lhs = node.parent.children[0] + lhs = _remove_parens(lhs) + try: + children = lhs.children + except AttributeError: + pass + else: + if ',' in children or lhs.type == 'atom' and children[0] == '(': + type_ = 'tuple' + elif lhs.type == 'atom' and children[0] == '[': + type_ = 'list' + trailer = children[-1] + + if type_ is None: + if not (lhs.type == 'name' + # subscript/attributes are allowed + or lhs.type in ('atom_expr', 'power') + and trailer.type == 'trailer' + and trailer.children[0] != '('): + return True + else: + # x, y: str + message = "only single target (not %s) can be annotated" + self.add_issue(lhs.parent, message=message % type_) + + +@ErrorFinder.register_rule(type='argument') +class _ArgumentRule(SyntaxRule): + def is_issue(self, node): + first = node.children[0] + if self._normalizer.version < (3, 8): + # a((b)=c) is valid in <3.8 + first = _remove_parens(first) + if node.children[1] == '=' and first.type != 'name': + if first.type == 'lambdef': + # f(lambda: 1=1) + if self._normalizer.version < (3, 8): + message = "lambda cannot contain assignment" + else: + message = 'expression cannot contain assignment, perhaps you meant "=="?' + else: + # f(+x=1) + if self._normalizer.version < (3, 8): + message = "keyword can't be an expression" + else: + message = 'expression cannot contain assignment, perhaps you meant "=="?' + self.add_issue(first, message=message) + + if _is_argument_comprehension(node) and node.parent.type == 'classdef': + self.add_issue(node, message='invalid syntax') + + +@ErrorFinder.register_rule(type='nonlocal_stmt') +class _NonlocalModuleLevelRule(SyntaxRule): + message = "nonlocal declaration not allowed at module level" + + def is_issue(self, node): + return self._normalizer.context.parent_context is None + + +@ErrorFinder.register_rule(type='arglist') +class _ArglistRule(SyntaxRule): + @property + def message(self): + if self._normalizer.version < (3, 7): + return "Generator expression must be parenthesized if not sole argument" + else: + return "Generator expression must be parenthesized" + + def is_issue(self, node): + arg_set = set() + kw_only = False + kw_unpacking_only = False + for argument in node.children: + if argument == ',': + continue + + if argument.type == 'argument': + first = argument.children[0] + if _is_argument_comprehension(argument) and len(node.children) >= 2: + # a(a, b for b in c) + return True + + if first in ('*', '**'): + if first == '*': + if kw_unpacking_only: + # foo(**kwargs, *args) + message = "iterable argument unpacking " \ + "follows keyword argument unpacking" + self.add_issue(argument, message=message) + else: + kw_unpacking_only = True + else: # Is a keyword argument. + kw_only = True + if first.type == 'name': + if first.value in arg_set: + # f(x=1, x=2) + message = "keyword argument repeated" + if self._normalizer.version >= (3, 9): + message += ": {}".format(first.value) + self.add_issue(first, message=message) + else: + arg_set.add(first.value) + else: + if kw_unpacking_only: + # f(**x, y) + message = "positional argument follows keyword argument unpacking" + self.add_issue(argument, message=message) + elif kw_only: + # f(x=2, y) + message = "positional argument follows keyword argument" + self.add_issue(argument, message=message) + + +@ErrorFinder.register_rule(type='parameters') +@ErrorFinder.register_rule(type='lambdef') +class _ParameterRule(SyntaxRule): + # def f(x=3, y): pass + message = "non-default argument follows default argument" + + def is_issue(self, node): + param_names = set() + default_only = False + star_seen = False + for p in _iter_params(node): + if p.type == 'operator': + if p.value == '*': + star_seen = True + default_only = False + continue + + if p.name.value in param_names: + message = "duplicate argument '%s' in function definition" + self.add_issue(p.name, message=message % p.name.value) + param_names.add(p.name.value) + + if not star_seen: + if p.default is None and not p.star_count: + if default_only: + return True + elif p.star_count: + star_seen = True + default_only = False + else: + default_only = True + + +@ErrorFinder.register_rule(type='try_stmt') +class _TryStmtRule(SyntaxRule): + message = "default 'except:' must be last" + + def is_issue(self, try_stmt): + default_except = None + for except_clause in try_stmt.children[3::3]: + if except_clause in ('else', 'finally'): + break + if except_clause == 'except': + default_except = except_clause + elif default_except is not None: + self.add_issue(default_except, message=self.message) + + +@ErrorFinder.register_rule(type='fstring') +class _FStringRule(SyntaxRule): + _fstring_grammar = None + message_expr = "f-string expression part cannot include a backslash" + message_nested = "f-string: expressions nested too deeply" + message_conversion = "f-string: invalid conversion character: expected 's', 'r', or 'a'" + + def _check_format_spec(self, format_spec, depth): + self._check_fstring_contents(format_spec.children[1:], depth) + + def _check_fstring_expr(self, fstring_expr, depth): + if depth >= 2: + self.add_issue(fstring_expr, message=self.message_nested) + + expr = fstring_expr.children[1] + if '\\' in expr.get_code(): + self.add_issue(expr, message=self.message_expr) + + children_2 = fstring_expr.children[2] + if children_2.type == 'operator' and children_2.value == '=': + conversion = fstring_expr.children[3] + else: + conversion = children_2 + if conversion.type == 'fstring_conversion': + name = conversion.children[1] + if name.value not in ('s', 'r', 'a'): + self.add_issue(name, message=self.message_conversion) + + format_spec = fstring_expr.children[-2] + if format_spec.type == 'fstring_format_spec': + self._check_format_spec(format_spec, depth + 1) + + def is_issue(self, fstring): + self._check_fstring_contents(fstring.children[1:-1]) + + def _check_fstring_contents(self, children, depth=0): + for fstring_content in children: + if fstring_content.type == 'fstring_expr': + self._check_fstring_expr(fstring_content, depth) + + +class _CheckAssignmentRule(SyntaxRule): + def _check_assignment(self, node, is_deletion=False, is_namedexpr=False, is_aug_assign=False): + error = None + type_ = node.type + if type_ == 'lambdef': + error = 'lambda' + elif type_ == 'atom': + first, second = node.children[:2] + error = _get_comprehension_type(node) + if error is None: + if second.type == 'dictorsetmaker': + if self._normalizer.version < (3, 8): + error = 'literal' + else: + if second.children[1] == ':': + if self._normalizer.version < (3, 10): + error = 'dict display' + else: + error = 'dict literal' + else: + error = 'set display' + elif first == "{" and second == "}": + if self._normalizer.version < (3, 8): + error = 'literal' + else: + if self._normalizer.version < (3, 10): + error = "dict display" + else: + error = "dict literal" + elif first == "{" and len(node.children) > 2: + if self._normalizer.version < (3, 8): + error = 'literal' + else: + error = "set display" + elif first in ('(', '['): + if second.type == 'yield_expr': + error = 'yield expression' + elif second.type == 'testlist_comp': + # ([a, b] := [1, 2]) + # ((a, b) := [1, 2]) + if is_namedexpr: + if first == '(': + error = 'tuple' + elif first == '[': + error = 'list' + + # This is not a comprehension, they were handled + # further above. + for child in second.children[::2]: + self._check_assignment(child, is_deletion, is_namedexpr, is_aug_assign) + else: # Everything handled, must be useless brackets. + self._check_assignment(second, is_deletion, is_namedexpr, is_aug_assign) + elif type_ == 'keyword': + if node.value == "yield": + error = "yield expression" + elif self._normalizer.version < (3, 8): + error = 'keyword' + else: + error = str(node.value) + elif type_ == 'operator': + if node.value == '...': + if self._normalizer.version < (3, 10): + error = 'Ellipsis' + else: + error = 'ellipsis' + elif type_ == 'comparison': + error = 'comparison' + elif type_ in ('string', 'number', 'strings'): + error = 'literal' + elif type_ == 'yield_expr': + # This one seems to be a slightly different warning in Python. + message = 'assignment to yield expression not possible' + self.add_issue(node, message=message) + elif type_ == 'test': + error = 'conditional expression' + elif type_ in ('atom_expr', 'power'): + if node.children[0] == 'await': + error = 'await expression' + elif node.children[-2] == '**': + if self._normalizer.version < (3, 10): + error = 'operator' + else: + error = 'expression' + else: + # Has a trailer + trailer = node.children[-1] + assert trailer.type == 'trailer' + if trailer.children[0] == '(': + error = 'function call' + elif is_namedexpr and trailer.children[0] == '[': + error = 'subscript' + elif is_namedexpr and trailer.children[0] == '.': + error = 'attribute' + elif type_ == "fstring": + if self._normalizer.version < (3, 8): + error = 'literal' + else: + error = "f-string expression" + elif type_ in ('testlist_star_expr', 'exprlist', 'testlist'): + for child in node.children[::2]: + self._check_assignment(child, is_deletion, is_namedexpr, is_aug_assign) + elif ('expr' in type_ and type_ != 'star_expr' # is a substring + or '_test' in type_ + or type_ in ('term', 'factor')): + if self._normalizer.version < (3, 10): + error = 'operator' + else: + error = 'expression' + elif type_ == "star_expr": + if is_deletion: + if self._normalizer.version >= (3, 9): + error = "starred" + else: + self.add_issue(node, message="can't use starred expression here") + else: + if self._normalizer.version >= (3, 9): + ancestor = node.parent + else: + ancestor = _skip_parens_bottom_up(node) + if ancestor.type not in _STAR_EXPR_PARENTS and not is_aug_assign \ + and not (ancestor.type == 'atom' and ancestor.children[0] == '['): + message = "starred assignment target must be in a list or tuple" + self.add_issue(node, message=message) + + self._check_assignment(node.children[1]) + + if error is not None: + if is_namedexpr: + message = 'cannot use assignment expressions with %s' % error + else: + cannot = "can't" if self._normalizer.version < (3, 8) else "cannot" + message = ' '.join([cannot, "delete" if is_deletion else "assign to", error]) + self.add_issue(node, message=message) + + +@ErrorFinder.register_rule(type='sync_comp_for') +class _CompForRule(_CheckAssignmentRule): + message = "asynchronous comprehension outside of an asynchronous function" + + def is_issue(self, node): + expr_list = node.children[1] + if expr_list.type != 'expr_list': # Already handled. + self._check_assignment(expr_list) + + return node.parent.children[0] == 'async' \ + and not self._normalizer.context.is_async_funcdef() + + +@ErrorFinder.register_rule(type='expr_stmt') +class _ExprStmtRule(_CheckAssignmentRule): + message = "illegal expression for augmented assignment" + extended_message = "'{target}' is an " + message + + def is_issue(self, node): + augassign = node.children[1] + is_aug_assign = augassign != '=' and augassign.type != 'annassign' + + if self._normalizer.version <= (3, 8) or not is_aug_assign: + for before_equal in node.children[:-2:2]: + self._check_assignment(before_equal, is_aug_assign=is_aug_assign) + + if is_aug_assign: + target = _remove_parens(node.children[0]) + # a, a[b], a.b + + if target.type == "name" or ( + target.type in ("atom_expr", "power") + and target.children[1].type == "trailer" + and target.children[-1].children[0] != "(" + ): + return False + + if self._normalizer.version <= (3, 8): + return True + else: + self.add_issue( + node, + message=self.extended_message.format( + target=_get_rhs_name(node.children[0], self._normalizer.version) + ), + ) + + +@ErrorFinder.register_rule(type='with_item') +class _WithItemRule(_CheckAssignmentRule): + def is_issue(self, with_item): + self._check_assignment(with_item.children[2]) + + +@ErrorFinder.register_rule(type='del_stmt') +class _DelStmtRule(_CheckAssignmentRule): + def is_issue(self, del_stmt): + child = del_stmt.children[1] + + if child.type != 'expr_list': # Already handled. + self._check_assignment(child, is_deletion=True) + + +@ErrorFinder.register_rule(type='expr_list') +class _ExprListRule(_CheckAssignmentRule): + def is_issue(self, expr_list): + for expr in expr_list.children[::2]: + self._check_assignment(expr) + + +@ErrorFinder.register_rule(type='for_stmt') +class _ForStmtRule(_CheckAssignmentRule): + def is_issue(self, for_stmt): + # Some of the nodes here are already used, so no else if + expr_list = for_stmt.children[1] + if expr_list.type != 'expr_list': # Already handled. + self._check_assignment(expr_list) + + +@ErrorFinder.register_rule(type='namedexpr_test') +class _NamedExprRule(_CheckAssignmentRule): + # namedexpr_test: test [':=' test] + + def is_issue(self, namedexpr_test): + # assigned name + first = namedexpr_test.children[0] + + def search_namedexpr_in_comp_for(node): + while True: + parent = node.parent + if parent is None: + return parent + if parent.type == 'sync_comp_for' and parent.children[3] == node: + return parent + node = parent + + if search_namedexpr_in_comp_for(namedexpr_test): + # [i+1 for i in (i := range(5))] + # [i+1 for i in (j := range(5))] + # [i+1 for i in (lambda: (j := range(5)))()] + message = 'assignment expression cannot be used in a comprehension iterable expression' + self.add_issue(namedexpr_test, message=message) + + # defined names + exprlist = list() + + def process_comp_for(comp_for): + if comp_for.type == 'sync_comp_for': + comp = comp_for + elif comp_for.type == 'comp_for': + comp = comp_for.children[1] + exprlist.extend(_get_for_stmt_definition_exprs(comp)) + + def search_all_comp_ancestors(node): + has_ancestors = False + while True: + node = node.search_ancestor('testlist_comp', 'dictorsetmaker') + if node is None: + break + for child in node.children: + if child.type in _COMP_FOR_TYPES: + process_comp_for(child) + has_ancestors = True + break + return has_ancestors + + # check assignment expressions in comprehensions + search_all = search_all_comp_ancestors(namedexpr_test) + if search_all: + if self._normalizer.context.node.type == 'classdef': + message = 'assignment expression within a comprehension ' \ + 'cannot be used in a class body' + self.add_issue(namedexpr_test, message=message) + + namelist = [expr.value for expr in exprlist if expr.type == 'name'] + if first.type == 'name' and first.value in namelist: + # [i := 0 for i, j in range(5)] + # [[(i := i) for j in range(5)] for i in range(5)] + # [i for i, j in range(5) if True or (i := 1)] + # [False and (i := 0) for i, j in range(5)] + message = 'assignment expression cannot rebind ' \ + 'comprehension iteration variable %r' % first.value + self.add_issue(namedexpr_test, message=message) + + self._check_assignment(first, is_namedexpr=True) diff --git a/lib/python3.12/site-packages/parso/python/grammar310.txt b/lib/python3.12/site-packages/parso/python/grammar310.txt new file mode 100644 index 0000000000000000000000000000000000000000..f092050d881db65ec25e1cebbc34921500b60eca --- /dev/null +++ b/lib/python3.12/site-packages/parso/python/grammar310.txt @@ -0,0 +1,169 @@ +# Grammar for Python + +# NOTE WELL: You should also follow all the steps listed at +# https://devguide.python.org/grammar/ + +# Start symbols for the grammar: +# single_input is a single interactive statement; +# file_input is a module or sequence of commands read from an input file; +# eval_input is the input for the eval() functions. +# NB: compound_stmt in single_input is followed by extra NEWLINE! +single_input: NEWLINE | simple_stmt | compound_stmt NEWLINE +file_input: stmt* ENDMARKER +eval_input: testlist NEWLINE* ENDMARKER + +decorator: '@' namedexpr_test NEWLINE +decorators: decorator+ +decorated: decorators (classdef | funcdef | async_funcdef) + +async_funcdef: 'async' funcdef +funcdef: 'def' NAME parameters ['->' test] ':' suite + +parameters: '(' [typedargslist] ')' +typedargslist: ( + (tfpdef ['=' test] (',' tfpdef ['=' test])* ',' '/' [',' [ tfpdef ['=' test] ( + ',' tfpdef ['=' test])* ([',' [ + '*' [tfpdef] (',' tfpdef ['=' test])* [',' ['**' tfpdef [',']]] + | '**' tfpdef [',']]]) + | '*' [tfpdef] (',' tfpdef ['=' test])* ([',' ['**' tfpdef [',']]]) + | '**' tfpdef [',']]] ) +| (tfpdef ['=' test] (',' tfpdef ['=' test])* [',' [ + '*' [tfpdef] (',' tfpdef ['=' test])* [',' ['**' tfpdef [',']]] + | '**' tfpdef [',']]] + | '*' [tfpdef] (',' tfpdef ['=' test])* [',' ['**' tfpdef [',']]] + | '**' tfpdef [',']) +) +tfpdef: NAME [':' test] +varargslist: vfpdef ['=' test ](',' vfpdef ['=' test])* ',' '/' [',' [ (vfpdef ['=' test] (',' vfpdef ['=' test])* [',' [ + '*' [vfpdef] (',' vfpdef ['=' test])* [',' ['**' vfpdef [',']]] + | '**' vfpdef [',']]] + | '*' [vfpdef] (',' vfpdef ['=' test])* [',' ['**' vfpdef [',']]] + | '**' vfpdef [',']) ]] | (vfpdef ['=' test] (',' vfpdef ['=' test])* [',' [ + '*' [vfpdef] (',' vfpdef ['=' test])* [',' ['**' vfpdef [',']]] + | '**' vfpdef [',']]] + | '*' [vfpdef] (',' vfpdef ['=' test])* [',' ['**' vfpdef [',']]] + | '**' vfpdef [','] +) +vfpdef: NAME + +stmt: simple_stmt | compound_stmt | NEWLINE +simple_stmt: small_stmt (';' small_stmt)* [';'] NEWLINE +small_stmt: (expr_stmt | del_stmt | pass_stmt | flow_stmt | + import_stmt | global_stmt | nonlocal_stmt | assert_stmt) +expr_stmt: testlist_star_expr (annassign | augassign (yield_expr|testlist) | + ('=' (yield_expr|testlist_star_expr))*) +annassign: ':' test ['=' (yield_expr|testlist_star_expr)] +testlist_star_expr: (test|star_expr) (',' (test|star_expr))* [','] +augassign: ('+=' | '-=' | '*=' | '@=' | '/=' | '%=' | '&=' | '|=' | '^=' | + '<<=' | '>>=' | '**=' | '//=') +# For normal and annotated assignments, additional restrictions enforced by the interpreter +del_stmt: 'del' exprlist +pass_stmt: 'pass' +flow_stmt: break_stmt | continue_stmt | return_stmt | raise_stmt | yield_stmt +break_stmt: 'break' +continue_stmt: 'continue' +return_stmt: 'return' [testlist_star_expr] +yield_stmt: yield_expr +raise_stmt: 'raise' [test ['from' test]] +import_stmt: import_name | import_from +import_name: 'import' dotted_as_names +# note below: the ('.' | '...') is necessary because '...' is tokenized as ELLIPSIS +import_from: ('from' (('.' | '...')* dotted_name | ('.' | '...')+) + 'import' ('*' | '(' import_as_names ')' | import_as_names)) +import_as_name: NAME ['as' NAME] +dotted_as_name: dotted_name ['as' NAME] +import_as_names: import_as_name (',' import_as_name)* [','] +dotted_as_names: dotted_as_name (',' dotted_as_name)* +dotted_name: NAME ('.' NAME)* +global_stmt: 'global' NAME (',' NAME)* +nonlocal_stmt: 'nonlocal' NAME (',' NAME)* +assert_stmt: 'assert' test [',' test] + +compound_stmt: if_stmt | while_stmt | for_stmt | try_stmt | with_stmt | funcdef | classdef | decorated | async_stmt +async_stmt: 'async' (funcdef | with_stmt | for_stmt) +if_stmt: 'if' namedexpr_test ':' suite ('elif' namedexpr_test ':' suite)* ['else' ':' suite] +while_stmt: 'while' namedexpr_test ':' suite ['else' ':' suite] +for_stmt: 'for' exprlist 'in' testlist ':' suite ['else' ':' suite] +try_stmt: ('try' ':' suite + ((except_clause ':' suite)+ + ['else' ':' suite] + ['finally' ':' suite] | + 'finally' ':' suite)) +with_stmt: 'with' with_item (',' with_item)* ':' suite +with_item: test ['as' expr] +# NB compile.c makes sure that the default except clause is last +except_clause: 'except' [test ['as' NAME]] +suite: simple_stmt | NEWLINE INDENT stmt+ DEDENT + +namedexpr_test: test [':=' test] +test: or_test ['if' or_test 'else' test] | lambdef +lambdef: 'lambda' [varargslist] ':' test +or_test: and_test ('or' and_test)* +and_test: not_test ('and' not_test)* +not_test: 'not' not_test | comparison +comparison: expr (comp_op expr)* +# <> isn't actually a valid comparison operator in Python. It's here for the +# sake of a __future__ import described in PEP 401 (which really works :-) +comp_op: '<'|'>'|'=='|'>='|'<='|'<>'|'!='|'in'|'not' 'in'|'is'|'is' 'not' +star_expr: '*' expr +expr: xor_expr ('|' xor_expr)* +xor_expr: and_expr ('^' and_expr)* +and_expr: shift_expr ('&' shift_expr)* +shift_expr: arith_expr (('<<'|'>>') arith_expr)* +arith_expr: term (('+'|'-') term)* +term: factor (('*'|'@'|'/'|'%'|'//') factor)* +factor: ('+'|'-'|'~') factor | power +power: atom_expr ['**' factor] +atom_expr: ['await'] atom trailer* +atom: ('(' [yield_expr|testlist_comp] ')' | + '[' [testlist_comp] ']' | + '{' [dictorsetmaker] '}' | + NAME | NUMBER | strings | '...' | 'None' | 'True' | 'False') +testlist_comp: (namedexpr_test|star_expr) ( comp_for | (',' (namedexpr_test|star_expr))* [','] ) +trailer: '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME +subscriptlist: subscript (',' subscript)* [','] +subscript: test [':=' test] | [test] ':' [test] [sliceop] +sliceop: ':' [test] +exprlist: (expr|star_expr) (',' (expr|star_expr))* [','] +testlist: test (',' test)* [','] +dictorsetmaker: ( ((test ':' test | '**' expr) + (comp_for | (',' (test ':' test | '**' expr))* [','])) | + ((test [':=' test] | star_expr) + (comp_for | (',' (test [':=' test] | star_expr))* [','])) ) + +classdef: 'class' NAME ['(' [arglist] ')'] ':' suite + +arglist: argument (',' argument)* [','] + +# The reason that keywords are test nodes instead of NAME is that using NAME +# results in an ambiguity. ast.c makes sure it's a NAME. +# "test '=' test" is really "keyword '=' test", but we have no such token. +# These need to be in a single rule to avoid grammar that is ambiguous +# to our LL(1) parser. Even though 'test' includes '*expr' in star_expr, +# we explicitly match '*' here, too, to give it proper precedence. +# Illegal combinations and orderings are blocked in ast.c: +# multiple (test comp_for) arguments are blocked; keyword unpackings +# that precede iterable unpackings are blocked; etc. +argument: ( test [comp_for] | + test ':=' test | + test '=' test | + '**' test | + '*' test ) + +comp_iter: comp_for | comp_if +sync_comp_for: 'for' exprlist 'in' or_test [comp_iter] +comp_for: ['async'] sync_comp_for +comp_if: 'if' or_test [comp_iter] + +# not used in grammar, but may appear in "node" passed from Parser to Compiler +encoding_decl: NAME + +yield_expr: 'yield' [yield_arg] +yield_arg: 'from' test | testlist_star_expr + +strings: (STRING | fstring)+ +fstring: FSTRING_START fstring_content* FSTRING_END +fstring_content: FSTRING_STRING | fstring_expr +fstring_conversion: '!' NAME +fstring_expr: '{' (testlist_comp | yield_expr) ['='] [ fstring_conversion ] [ fstring_format_spec ] '}' +fstring_format_spec: ':' fstring_content* diff --git a/lib/python3.12/site-packages/parso/python/grammar311.txt b/lib/python3.12/site-packages/parso/python/grammar311.txt new file mode 100644 index 0000000000000000000000000000000000000000..f092050d881db65ec25e1cebbc34921500b60eca --- /dev/null +++ b/lib/python3.12/site-packages/parso/python/grammar311.txt @@ -0,0 +1,169 @@ +# Grammar for Python + +# NOTE WELL: You should also follow all the steps listed at +# https://devguide.python.org/grammar/ + +# Start symbols for the grammar: +# single_input is a single interactive statement; +# file_input is a module or sequence of commands read from an input file; +# eval_input is the input for the eval() functions. +# NB: compound_stmt in single_input is followed by extra NEWLINE! +single_input: NEWLINE | simple_stmt | compound_stmt NEWLINE +file_input: stmt* ENDMARKER +eval_input: testlist NEWLINE* ENDMARKER + +decorator: '@' namedexpr_test NEWLINE +decorators: decorator+ +decorated: decorators (classdef | funcdef | async_funcdef) + +async_funcdef: 'async' funcdef +funcdef: 'def' NAME parameters ['->' test] ':' suite + +parameters: '(' [typedargslist] ')' +typedargslist: ( + (tfpdef ['=' test] (',' tfpdef ['=' test])* ',' '/' [',' [ tfpdef ['=' test] ( + ',' tfpdef ['=' test])* ([',' [ + '*' [tfpdef] (',' tfpdef ['=' test])* [',' ['**' tfpdef [',']]] + | '**' tfpdef [',']]]) + | '*' [tfpdef] (',' tfpdef ['=' test])* ([',' ['**' tfpdef [',']]]) + | '**' tfpdef [',']]] ) +| (tfpdef ['=' test] (',' tfpdef ['=' test])* [',' [ + '*' [tfpdef] (',' tfpdef ['=' test])* [',' ['**' tfpdef [',']]] + | '**' tfpdef [',']]] + | '*' [tfpdef] (',' tfpdef ['=' test])* [',' ['**' tfpdef [',']]] + | '**' tfpdef [',']) +) +tfpdef: NAME [':' test] +varargslist: vfpdef ['=' test ](',' vfpdef ['=' test])* ',' '/' [',' [ (vfpdef ['=' test] (',' vfpdef ['=' test])* [',' [ + '*' [vfpdef] (',' vfpdef ['=' test])* [',' ['**' vfpdef [',']]] + | '**' vfpdef [',']]] + | '*' [vfpdef] (',' vfpdef ['=' test])* [',' ['**' vfpdef [',']]] + | '**' vfpdef [',']) ]] | (vfpdef ['=' test] (',' vfpdef ['=' test])* [',' [ + '*' [vfpdef] (',' vfpdef ['=' test])* [',' ['**' vfpdef [',']]] + | '**' vfpdef [',']]] + | '*' [vfpdef] (',' vfpdef ['=' test])* [',' ['**' vfpdef [',']]] + | '**' vfpdef [','] +) +vfpdef: NAME + +stmt: simple_stmt | compound_stmt | NEWLINE +simple_stmt: small_stmt (';' small_stmt)* [';'] NEWLINE +small_stmt: (expr_stmt | del_stmt | pass_stmt | flow_stmt | + import_stmt | global_stmt | nonlocal_stmt | assert_stmt) +expr_stmt: testlist_star_expr (annassign | augassign (yield_expr|testlist) | + ('=' (yield_expr|testlist_star_expr))*) +annassign: ':' test ['=' (yield_expr|testlist_star_expr)] +testlist_star_expr: (test|star_expr) (',' (test|star_expr))* [','] +augassign: ('+=' | '-=' | '*=' | '@=' | '/=' | '%=' | '&=' | '|=' | '^=' | + '<<=' | '>>=' | '**=' | '//=') +# For normal and annotated assignments, additional restrictions enforced by the interpreter +del_stmt: 'del' exprlist +pass_stmt: 'pass' +flow_stmt: break_stmt | continue_stmt | return_stmt | raise_stmt | yield_stmt +break_stmt: 'break' +continue_stmt: 'continue' +return_stmt: 'return' [testlist_star_expr] +yield_stmt: yield_expr +raise_stmt: 'raise' [test ['from' test]] +import_stmt: import_name | import_from +import_name: 'import' dotted_as_names +# note below: the ('.' | '...') is necessary because '...' is tokenized as ELLIPSIS +import_from: ('from' (('.' | '...')* dotted_name | ('.' | '...')+) + 'import' ('*' | '(' import_as_names ')' | import_as_names)) +import_as_name: NAME ['as' NAME] +dotted_as_name: dotted_name ['as' NAME] +import_as_names: import_as_name (',' import_as_name)* [','] +dotted_as_names: dotted_as_name (',' dotted_as_name)* +dotted_name: NAME ('.' NAME)* +global_stmt: 'global' NAME (',' NAME)* +nonlocal_stmt: 'nonlocal' NAME (',' NAME)* +assert_stmt: 'assert' test [',' test] + +compound_stmt: if_stmt | while_stmt | for_stmt | try_stmt | with_stmt | funcdef | classdef | decorated | async_stmt +async_stmt: 'async' (funcdef | with_stmt | for_stmt) +if_stmt: 'if' namedexpr_test ':' suite ('elif' namedexpr_test ':' suite)* ['else' ':' suite] +while_stmt: 'while' namedexpr_test ':' suite ['else' ':' suite] +for_stmt: 'for' exprlist 'in' testlist ':' suite ['else' ':' suite] +try_stmt: ('try' ':' suite + ((except_clause ':' suite)+ + ['else' ':' suite] + ['finally' ':' suite] | + 'finally' ':' suite)) +with_stmt: 'with' with_item (',' with_item)* ':' suite +with_item: test ['as' expr] +# NB compile.c makes sure that the default except clause is last +except_clause: 'except' [test ['as' NAME]] +suite: simple_stmt | NEWLINE INDENT stmt+ DEDENT + +namedexpr_test: test [':=' test] +test: or_test ['if' or_test 'else' test] | lambdef +lambdef: 'lambda' [varargslist] ':' test +or_test: and_test ('or' and_test)* +and_test: not_test ('and' not_test)* +not_test: 'not' not_test | comparison +comparison: expr (comp_op expr)* +# <> isn't actually a valid comparison operator in Python. It's here for the +# sake of a __future__ import described in PEP 401 (which really works :-) +comp_op: '<'|'>'|'=='|'>='|'<='|'<>'|'!='|'in'|'not' 'in'|'is'|'is' 'not' +star_expr: '*' expr +expr: xor_expr ('|' xor_expr)* +xor_expr: and_expr ('^' and_expr)* +and_expr: shift_expr ('&' shift_expr)* +shift_expr: arith_expr (('<<'|'>>') arith_expr)* +arith_expr: term (('+'|'-') term)* +term: factor (('*'|'@'|'/'|'%'|'//') factor)* +factor: ('+'|'-'|'~') factor | power +power: atom_expr ['**' factor] +atom_expr: ['await'] atom trailer* +atom: ('(' [yield_expr|testlist_comp] ')' | + '[' [testlist_comp] ']' | + '{' [dictorsetmaker] '}' | + NAME | NUMBER | strings | '...' | 'None' | 'True' | 'False') +testlist_comp: (namedexpr_test|star_expr) ( comp_for | (',' (namedexpr_test|star_expr))* [','] ) +trailer: '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME +subscriptlist: subscript (',' subscript)* [','] +subscript: test [':=' test] | [test] ':' [test] [sliceop] +sliceop: ':' [test] +exprlist: (expr|star_expr) (',' (expr|star_expr))* [','] +testlist: test (',' test)* [','] +dictorsetmaker: ( ((test ':' test | '**' expr) + (comp_for | (',' (test ':' test | '**' expr))* [','])) | + ((test [':=' test] | star_expr) + (comp_for | (',' (test [':=' test] | star_expr))* [','])) ) + +classdef: 'class' NAME ['(' [arglist] ')'] ':' suite + +arglist: argument (',' argument)* [','] + +# The reason that keywords are test nodes instead of NAME is that using NAME +# results in an ambiguity. ast.c makes sure it's a NAME. +# "test '=' test" is really "keyword '=' test", but we have no such token. +# These need to be in a single rule to avoid grammar that is ambiguous +# to our LL(1) parser. Even though 'test' includes '*expr' in star_expr, +# we explicitly match '*' here, too, to give it proper precedence. +# Illegal combinations and orderings are blocked in ast.c: +# multiple (test comp_for) arguments are blocked; keyword unpackings +# that precede iterable unpackings are blocked; etc. +argument: ( test [comp_for] | + test ':=' test | + test '=' test | + '**' test | + '*' test ) + +comp_iter: comp_for | comp_if +sync_comp_for: 'for' exprlist 'in' or_test [comp_iter] +comp_for: ['async'] sync_comp_for +comp_if: 'if' or_test [comp_iter] + +# not used in grammar, but may appear in "node" passed from Parser to Compiler +encoding_decl: NAME + +yield_expr: 'yield' [yield_arg] +yield_arg: 'from' test | testlist_star_expr + +strings: (STRING | fstring)+ +fstring: FSTRING_START fstring_content* FSTRING_END +fstring_content: FSTRING_STRING | fstring_expr +fstring_conversion: '!' NAME +fstring_expr: '{' (testlist_comp | yield_expr) ['='] [ fstring_conversion ] [ fstring_format_spec ] '}' +fstring_format_spec: ':' fstring_content* diff --git a/lib/python3.12/site-packages/parso/python/grammar312.txt b/lib/python3.12/site-packages/parso/python/grammar312.txt new file mode 100644 index 0000000000000000000000000000000000000000..f092050d881db65ec25e1cebbc34921500b60eca --- /dev/null +++ b/lib/python3.12/site-packages/parso/python/grammar312.txt @@ -0,0 +1,169 @@ +# Grammar for Python + +# NOTE WELL: You should also follow all the steps listed at +# https://devguide.python.org/grammar/ + +# Start symbols for the grammar: +# single_input is a single interactive statement; +# file_input is a module or sequence of commands read from an input file; +# eval_input is the input for the eval() functions. +# NB: compound_stmt in single_input is followed by extra NEWLINE! +single_input: NEWLINE | simple_stmt | compound_stmt NEWLINE +file_input: stmt* ENDMARKER +eval_input: testlist NEWLINE* ENDMARKER + +decorator: '@' namedexpr_test NEWLINE +decorators: decorator+ +decorated: decorators (classdef | funcdef | async_funcdef) + +async_funcdef: 'async' funcdef +funcdef: 'def' NAME parameters ['->' test] ':' suite + +parameters: '(' [typedargslist] ')' +typedargslist: ( + (tfpdef ['=' test] (',' tfpdef ['=' test])* ',' '/' [',' [ tfpdef ['=' test] ( + ',' tfpdef ['=' test])* ([',' [ + '*' [tfpdef] (',' tfpdef ['=' test])* [',' ['**' tfpdef [',']]] + | '**' tfpdef [',']]]) + | '*' [tfpdef] (',' tfpdef ['=' test])* ([',' ['**' tfpdef [',']]]) + | '**' tfpdef [',']]] ) +| (tfpdef ['=' test] (',' tfpdef ['=' test])* [',' [ + '*' [tfpdef] (',' tfpdef ['=' test])* [',' ['**' tfpdef [',']]] + | '**' tfpdef [',']]] + | '*' [tfpdef] (',' tfpdef ['=' test])* [',' ['**' tfpdef [',']]] + | '**' tfpdef [',']) +) +tfpdef: NAME [':' test] +varargslist: vfpdef ['=' test ](',' vfpdef ['=' test])* ',' '/' [',' [ (vfpdef ['=' test] (',' vfpdef ['=' test])* [',' [ + '*' [vfpdef] (',' vfpdef ['=' test])* [',' ['**' vfpdef [',']]] + | '**' vfpdef [',']]] + | '*' [vfpdef] (',' vfpdef ['=' test])* [',' ['**' vfpdef [',']]] + | '**' vfpdef [',']) ]] | (vfpdef ['=' test] (',' vfpdef ['=' test])* [',' [ + '*' [vfpdef] (',' vfpdef ['=' test])* [',' ['**' vfpdef [',']]] + | '**' vfpdef [',']]] + | '*' [vfpdef] (',' vfpdef ['=' test])* [',' ['**' vfpdef [',']]] + | '**' vfpdef [','] +) +vfpdef: NAME + +stmt: simple_stmt | compound_stmt | NEWLINE +simple_stmt: small_stmt (';' small_stmt)* [';'] NEWLINE +small_stmt: (expr_stmt | del_stmt | pass_stmt | flow_stmt | + import_stmt | global_stmt | nonlocal_stmt | assert_stmt) +expr_stmt: testlist_star_expr (annassign | augassign (yield_expr|testlist) | + ('=' (yield_expr|testlist_star_expr))*) +annassign: ':' test ['=' (yield_expr|testlist_star_expr)] +testlist_star_expr: (test|star_expr) (',' (test|star_expr))* [','] +augassign: ('+=' | '-=' | '*=' | '@=' | '/=' | '%=' | '&=' | '|=' | '^=' | + '<<=' | '>>=' | '**=' | '//=') +# For normal and annotated assignments, additional restrictions enforced by the interpreter +del_stmt: 'del' exprlist +pass_stmt: 'pass' +flow_stmt: break_stmt | continue_stmt | return_stmt | raise_stmt | yield_stmt +break_stmt: 'break' +continue_stmt: 'continue' +return_stmt: 'return' [testlist_star_expr] +yield_stmt: yield_expr +raise_stmt: 'raise' [test ['from' test]] +import_stmt: import_name | import_from +import_name: 'import' dotted_as_names +# note below: the ('.' | '...') is necessary because '...' is tokenized as ELLIPSIS +import_from: ('from' (('.' | '...')* dotted_name | ('.' | '...')+) + 'import' ('*' | '(' import_as_names ')' | import_as_names)) +import_as_name: NAME ['as' NAME] +dotted_as_name: dotted_name ['as' NAME] +import_as_names: import_as_name (',' import_as_name)* [','] +dotted_as_names: dotted_as_name (',' dotted_as_name)* +dotted_name: NAME ('.' NAME)* +global_stmt: 'global' NAME (',' NAME)* +nonlocal_stmt: 'nonlocal' NAME (',' NAME)* +assert_stmt: 'assert' test [',' test] + +compound_stmt: if_stmt | while_stmt | for_stmt | try_stmt | with_stmt | funcdef | classdef | decorated | async_stmt +async_stmt: 'async' (funcdef | with_stmt | for_stmt) +if_stmt: 'if' namedexpr_test ':' suite ('elif' namedexpr_test ':' suite)* ['else' ':' suite] +while_stmt: 'while' namedexpr_test ':' suite ['else' ':' suite] +for_stmt: 'for' exprlist 'in' testlist ':' suite ['else' ':' suite] +try_stmt: ('try' ':' suite + ((except_clause ':' suite)+ + ['else' ':' suite] + ['finally' ':' suite] | + 'finally' ':' suite)) +with_stmt: 'with' with_item (',' with_item)* ':' suite +with_item: test ['as' expr] +# NB compile.c makes sure that the default except clause is last +except_clause: 'except' [test ['as' NAME]] +suite: simple_stmt | NEWLINE INDENT stmt+ DEDENT + +namedexpr_test: test [':=' test] +test: or_test ['if' or_test 'else' test] | lambdef +lambdef: 'lambda' [varargslist] ':' test +or_test: and_test ('or' and_test)* +and_test: not_test ('and' not_test)* +not_test: 'not' not_test | comparison +comparison: expr (comp_op expr)* +# <> isn't actually a valid comparison operator in Python. It's here for the +# sake of a __future__ import described in PEP 401 (which really works :-) +comp_op: '<'|'>'|'=='|'>='|'<='|'<>'|'!='|'in'|'not' 'in'|'is'|'is' 'not' +star_expr: '*' expr +expr: xor_expr ('|' xor_expr)* +xor_expr: and_expr ('^' and_expr)* +and_expr: shift_expr ('&' shift_expr)* +shift_expr: arith_expr (('<<'|'>>') arith_expr)* +arith_expr: term (('+'|'-') term)* +term: factor (('*'|'@'|'/'|'%'|'//') factor)* +factor: ('+'|'-'|'~') factor | power +power: atom_expr ['**' factor] +atom_expr: ['await'] atom trailer* +atom: ('(' [yield_expr|testlist_comp] ')' | + '[' [testlist_comp] ']' | + '{' [dictorsetmaker] '}' | + NAME | NUMBER | strings | '...' | 'None' | 'True' | 'False') +testlist_comp: (namedexpr_test|star_expr) ( comp_for | (',' (namedexpr_test|star_expr))* [','] ) +trailer: '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME +subscriptlist: subscript (',' subscript)* [','] +subscript: test [':=' test] | [test] ':' [test] [sliceop] +sliceop: ':' [test] +exprlist: (expr|star_expr) (',' (expr|star_expr))* [','] +testlist: test (',' test)* [','] +dictorsetmaker: ( ((test ':' test | '**' expr) + (comp_for | (',' (test ':' test | '**' expr))* [','])) | + ((test [':=' test] | star_expr) + (comp_for | (',' (test [':=' test] | star_expr))* [','])) ) + +classdef: 'class' NAME ['(' [arglist] ')'] ':' suite + +arglist: argument (',' argument)* [','] + +# The reason that keywords are test nodes instead of NAME is that using NAME +# results in an ambiguity. ast.c makes sure it's a NAME. +# "test '=' test" is really "keyword '=' test", but we have no such token. +# These need to be in a single rule to avoid grammar that is ambiguous +# to our LL(1) parser. Even though 'test' includes '*expr' in star_expr, +# we explicitly match '*' here, too, to give it proper precedence. +# Illegal combinations and orderings are blocked in ast.c: +# multiple (test comp_for) arguments are blocked; keyword unpackings +# that precede iterable unpackings are blocked; etc. +argument: ( test [comp_for] | + test ':=' test | + test '=' test | + '**' test | + '*' test ) + +comp_iter: comp_for | comp_if +sync_comp_for: 'for' exprlist 'in' or_test [comp_iter] +comp_for: ['async'] sync_comp_for +comp_if: 'if' or_test [comp_iter] + +# not used in grammar, but may appear in "node" passed from Parser to Compiler +encoding_decl: NAME + +yield_expr: 'yield' [yield_arg] +yield_arg: 'from' test | testlist_star_expr + +strings: (STRING | fstring)+ +fstring: FSTRING_START fstring_content* FSTRING_END +fstring_content: FSTRING_STRING | fstring_expr +fstring_conversion: '!' NAME +fstring_expr: '{' (testlist_comp | yield_expr) ['='] [ fstring_conversion ] [ fstring_format_spec ] '}' +fstring_format_spec: ':' fstring_content* diff --git a/lib/python3.12/site-packages/parso/python/grammar313.txt b/lib/python3.12/site-packages/parso/python/grammar313.txt new file mode 100644 index 0000000000000000000000000000000000000000..f092050d881db65ec25e1cebbc34921500b60eca --- /dev/null +++ b/lib/python3.12/site-packages/parso/python/grammar313.txt @@ -0,0 +1,169 @@ +# Grammar for Python + +# NOTE WELL: You should also follow all the steps listed at +# https://devguide.python.org/grammar/ + +# Start symbols for the grammar: +# single_input is a single interactive statement; +# file_input is a module or sequence of commands read from an input file; +# eval_input is the input for the eval() functions. +# NB: compound_stmt in single_input is followed by extra NEWLINE! +single_input: NEWLINE | simple_stmt | compound_stmt NEWLINE +file_input: stmt* ENDMARKER +eval_input: testlist NEWLINE* ENDMARKER + +decorator: '@' namedexpr_test NEWLINE +decorators: decorator+ +decorated: decorators (classdef | funcdef | async_funcdef) + +async_funcdef: 'async' funcdef +funcdef: 'def' NAME parameters ['->' test] ':' suite + +parameters: '(' [typedargslist] ')' +typedargslist: ( + (tfpdef ['=' test] (',' tfpdef ['=' test])* ',' '/' [',' [ tfpdef ['=' test] ( + ',' tfpdef ['=' test])* ([',' [ + '*' [tfpdef] (',' tfpdef ['=' test])* [',' ['**' tfpdef [',']]] + | '**' tfpdef [',']]]) + | '*' [tfpdef] (',' tfpdef ['=' test])* ([',' ['**' tfpdef [',']]]) + | '**' tfpdef [',']]] ) +| (tfpdef ['=' test] (',' tfpdef ['=' test])* [',' [ + '*' [tfpdef] (',' tfpdef ['=' test])* [',' ['**' tfpdef [',']]] + | '**' tfpdef [',']]] + | '*' [tfpdef] (',' tfpdef ['=' test])* [',' ['**' tfpdef [',']]] + | '**' tfpdef [',']) +) +tfpdef: NAME [':' test] +varargslist: vfpdef ['=' test ](',' vfpdef ['=' test])* ',' '/' [',' [ (vfpdef ['=' test] (',' vfpdef ['=' test])* [',' [ + '*' [vfpdef] (',' vfpdef ['=' test])* [',' ['**' vfpdef [',']]] + | '**' vfpdef [',']]] + | '*' [vfpdef] (',' vfpdef ['=' test])* [',' ['**' vfpdef [',']]] + | '**' vfpdef [',']) ]] | (vfpdef ['=' test] (',' vfpdef ['=' test])* [',' [ + '*' [vfpdef] (',' vfpdef ['=' test])* [',' ['**' vfpdef [',']]] + | '**' vfpdef [',']]] + | '*' [vfpdef] (',' vfpdef ['=' test])* [',' ['**' vfpdef [',']]] + | '**' vfpdef [','] +) +vfpdef: NAME + +stmt: simple_stmt | compound_stmt | NEWLINE +simple_stmt: small_stmt (';' small_stmt)* [';'] NEWLINE +small_stmt: (expr_stmt | del_stmt | pass_stmt | flow_stmt | + import_stmt | global_stmt | nonlocal_stmt | assert_stmt) +expr_stmt: testlist_star_expr (annassign | augassign (yield_expr|testlist) | + ('=' (yield_expr|testlist_star_expr))*) +annassign: ':' test ['=' (yield_expr|testlist_star_expr)] +testlist_star_expr: (test|star_expr) (',' (test|star_expr))* [','] +augassign: ('+=' | '-=' | '*=' | '@=' | '/=' | '%=' | '&=' | '|=' | '^=' | + '<<=' | '>>=' | '**=' | '//=') +# For normal and annotated assignments, additional restrictions enforced by the interpreter +del_stmt: 'del' exprlist +pass_stmt: 'pass' +flow_stmt: break_stmt | continue_stmt | return_stmt | raise_stmt | yield_stmt +break_stmt: 'break' +continue_stmt: 'continue' +return_stmt: 'return' [testlist_star_expr] +yield_stmt: yield_expr +raise_stmt: 'raise' [test ['from' test]] +import_stmt: import_name | import_from +import_name: 'import' dotted_as_names +# note below: the ('.' | '...') is necessary because '...' is tokenized as ELLIPSIS +import_from: ('from' (('.' | '...')* dotted_name | ('.' | '...')+) + 'import' ('*' | '(' import_as_names ')' | import_as_names)) +import_as_name: NAME ['as' NAME] +dotted_as_name: dotted_name ['as' NAME] +import_as_names: import_as_name (',' import_as_name)* [','] +dotted_as_names: dotted_as_name (',' dotted_as_name)* +dotted_name: NAME ('.' NAME)* +global_stmt: 'global' NAME (',' NAME)* +nonlocal_stmt: 'nonlocal' NAME (',' NAME)* +assert_stmt: 'assert' test [',' test] + +compound_stmt: if_stmt | while_stmt | for_stmt | try_stmt | with_stmt | funcdef | classdef | decorated | async_stmt +async_stmt: 'async' (funcdef | with_stmt | for_stmt) +if_stmt: 'if' namedexpr_test ':' suite ('elif' namedexpr_test ':' suite)* ['else' ':' suite] +while_stmt: 'while' namedexpr_test ':' suite ['else' ':' suite] +for_stmt: 'for' exprlist 'in' testlist ':' suite ['else' ':' suite] +try_stmt: ('try' ':' suite + ((except_clause ':' suite)+ + ['else' ':' suite] + ['finally' ':' suite] | + 'finally' ':' suite)) +with_stmt: 'with' with_item (',' with_item)* ':' suite +with_item: test ['as' expr] +# NB compile.c makes sure that the default except clause is last +except_clause: 'except' [test ['as' NAME]] +suite: simple_stmt | NEWLINE INDENT stmt+ DEDENT + +namedexpr_test: test [':=' test] +test: or_test ['if' or_test 'else' test] | lambdef +lambdef: 'lambda' [varargslist] ':' test +or_test: and_test ('or' and_test)* +and_test: not_test ('and' not_test)* +not_test: 'not' not_test | comparison +comparison: expr (comp_op expr)* +# <> isn't actually a valid comparison operator in Python. It's here for the +# sake of a __future__ import described in PEP 401 (which really works :-) +comp_op: '<'|'>'|'=='|'>='|'<='|'<>'|'!='|'in'|'not' 'in'|'is'|'is' 'not' +star_expr: '*' expr +expr: xor_expr ('|' xor_expr)* +xor_expr: and_expr ('^' and_expr)* +and_expr: shift_expr ('&' shift_expr)* +shift_expr: arith_expr (('<<'|'>>') arith_expr)* +arith_expr: term (('+'|'-') term)* +term: factor (('*'|'@'|'/'|'%'|'//') factor)* +factor: ('+'|'-'|'~') factor | power +power: atom_expr ['**' factor] +atom_expr: ['await'] atom trailer* +atom: ('(' [yield_expr|testlist_comp] ')' | + '[' [testlist_comp] ']' | + '{' [dictorsetmaker] '}' | + NAME | NUMBER | strings | '...' | 'None' | 'True' | 'False') +testlist_comp: (namedexpr_test|star_expr) ( comp_for | (',' (namedexpr_test|star_expr))* [','] ) +trailer: '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME +subscriptlist: subscript (',' subscript)* [','] +subscript: test [':=' test] | [test] ':' [test] [sliceop] +sliceop: ':' [test] +exprlist: (expr|star_expr) (',' (expr|star_expr))* [','] +testlist: test (',' test)* [','] +dictorsetmaker: ( ((test ':' test | '**' expr) + (comp_for | (',' (test ':' test | '**' expr))* [','])) | + ((test [':=' test] | star_expr) + (comp_for | (',' (test [':=' test] | star_expr))* [','])) ) + +classdef: 'class' NAME ['(' [arglist] ')'] ':' suite + +arglist: argument (',' argument)* [','] + +# The reason that keywords are test nodes instead of NAME is that using NAME +# results in an ambiguity. ast.c makes sure it's a NAME. +# "test '=' test" is really "keyword '=' test", but we have no such token. +# These need to be in a single rule to avoid grammar that is ambiguous +# to our LL(1) parser. Even though 'test' includes '*expr' in star_expr, +# we explicitly match '*' here, too, to give it proper precedence. +# Illegal combinations and orderings are blocked in ast.c: +# multiple (test comp_for) arguments are blocked; keyword unpackings +# that precede iterable unpackings are blocked; etc. +argument: ( test [comp_for] | + test ':=' test | + test '=' test | + '**' test | + '*' test ) + +comp_iter: comp_for | comp_if +sync_comp_for: 'for' exprlist 'in' or_test [comp_iter] +comp_for: ['async'] sync_comp_for +comp_if: 'if' or_test [comp_iter] + +# not used in grammar, but may appear in "node" passed from Parser to Compiler +encoding_decl: NAME + +yield_expr: 'yield' [yield_arg] +yield_arg: 'from' test | testlist_star_expr + +strings: (STRING | fstring)+ +fstring: FSTRING_START fstring_content* FSTRING_END +fstring_content: FSTRING_STRING | fstring_expr +fstring_conversion: '!' NAME +fstring_expr: '{' (testlist_comp | yield_expr) ['='] [ fstring_conversion ] [ fstring_format_spec ] '}' +fstring_format_spec: ':' fstring_content* diff --git a/lib/python3.12/site-packages/parso/python/grammar314.txt b/lib/python3.12/site-packages/parso/python/grammar314.txt new file mode 100644 index 0000000000000000000000000000000000000000..f092050d881db65ec25e1cebbc34921500b60eca --- /dev/null +++ b/lib/python3.12/site-packages/parso/python/grammar314.txt @@ -0,0 +1,169 @@ +# Grammar for Python + +# NOTE WELL: You should also follow all the steps listed at +# https://devguide.python.org/grammar/ + +# Start symbols for the grammar: +# single_input is a single interactive statement; +# file_input is a module or sequence of commands read from an input file; +# eval_input is the input for the eval() functions. +# NB: compound_stmt in single_input is followed by extra NEWLINE! +single_input: NEWLINE | simple_stmt | compound_stmt NEWLINE +file_input: stmt* ENDMARKER +eval_input: testlist NEWLINE* ENDMARKER + +decorator: '@' namedexpr_test NEWLINE +decorators: decorator+ +decorated: decorators (classdef | funcdef | async_funcdef) + +async_funcdef: 'async' funcdef +funcdef: 'def' NAME parameters ['->' test] ':' suite + +parameters: '(' [typedargslist] ')' +typedargslist: ( + (tfpdef ['=' test] (',' tfpdef ['=' test])* ',' '/' [',' [ tfpdef ['=' test] ( + ',' tfpdef ['=' test])* ([',' [ + '*' [tfpdef] (',' tfpdef ['=' test])* [',' ['**' tfpdef [',']]] + | '**' tfpdef [',']]]) + | '*' [tfpdef] (',' tfpdef ['=' test])* ([',' ['**' tfpdef [',']]]) + | '**' tfpdef [',']]] ) +| (tfpdef ['=' test] (',' tfpdef ['=' test])* [',' [ + '*' [tfpdef] (',' tfpdef ['=' test])* [',' ['**' tfpdef [',']]] + | '**' tfpdef [',']]] + | '*' [tfpdef] (',' tfpdef ['=' test])* [',' ['**' tfpdef [',']]] + | '**' tfpdef [',']) +) +tfpdef: NAME [':' test] +varargslist: vfpdef ['=' test ](',' vfpdef ['=' test])* ',' '/' [',' [ (vfpdef ['=' test] (',' vfpdef ['=' test])* [',' [ + '*' [vfpdef] (',' vfpdef ['=' test])* [',' ['**' vfpdef [',']]] + | '**' vfpdef [',']]] + | '*' [vfpdef] (',' vfpdef ['=' test])* [',' ['**' vfpdef [',']]] + | '**' vfpdef [',']) ]] | (vfpdef ['=' test] (',' vfpdef ['=' test])* [',' [ + '*' [vfpdef] (',' vfpdef ['=' test])* [',' ['**' vfpdef [',']]] + | '**' vfpdef [',']]] + | '*' [vfpdef] (',' vfpdef ['=' test])* [',' ['**' vfpdef [',']]] + | '**' vfpdef [','] +) +vfpdef: NAME + +stmt: simple_stmt | compound_stmt | NEWLINE +simple_stmt: small_stmt (';' small_stmt)* [';'] NEWLINE +small_stmt: (expr_stmt | del_stmt | pass_stmt | flow_stmt | + import_stmt | global_stmt | nonlocal_stmt | assert_stmt) +expr_stmt: testlist_star_expr (annassign | augassign (yield_expr|testlist) | + ('=' (yield_expr|testlist_star_expr))*) +annassign: ':' test ['=' (yield_expr|testlist_star_expr)] +testlist_star_expr: (test|star_expr) (',' (test|star_expr))* [','] +augassign: ('+=' | '-=' | '*=' | '@=' | '/=' | '%=' | '&=' | '|=' | '^=' | + '<<=' | '>>=' | '**=' | '//=') +# For normal and annotated assignments, additional restrictions enforced by the interpreter +del_stmt: 'del' exprlist +pass_stmt: 'pass' +flow_stmt: break_stmt | continue_stmt | return_stmt | raise_stmt | yield_stmt +break_stmt: 'break' +continue_stmt: 'continue' +return_stmt: 'return' [testlist_star_expr] +yield_stmt: yield_expr +raise_stmt: 'raise' [test ['from' test]] +import_stmt: import_name | import_from +import_name: 'import' dotted_as_names +# note below: the ('.' | '...') is necessary because '...' is tokenized as ELLIPSIS +import_from: ('from' (('.' | '...')* dotted_name | ('.' | '...')+) + 'import' ('*' | '(' import_as_names ')' | import_as_names)) +import_as_name: NAME ['as' NAME] +dotted_as_name: dotted_name ['as' NAME] +import_as_names: import_as_name (',' import_as_name)* [','] +dotted_as_names: dotted_as_name (',' dotted_as_name)* +dotted_name: NAME ('.' NAME)* +global_stmt: 'global' NAME (',' NAME)* +nonlocal_stmt: 'nonlocal' NAME (',' NAME)* +assert_stmt: 'assert' test [',' test] + +compound_stmt: if_stmt | while_stmt | for_stmt | try_stmt | with_stmt | funcdef | classdef | decorated | async_stmt +async_stmt: 'async' (funcdef | with_stmt | for_stmt) +if_stmt: 'if' namedexpr_test ':' suite ('elif' namedexpr_test ':' suite)* ['else' ':' suite] +while_stmt: 'while' namedexpr_test ':' suite ['else' ':' suite] +for_stmt: 'for' exprlist 'in' testlist ':' suite ['else' ':' suite] +try_stmt: ('try' ':' suite + ((except_clause ':' suite)+ + ['else' ':' suite] + ['finally' ':' suite] | + 'finally' ':' suite)) +with_stmt: 'with' with_item (',' with_item)* ':' suite +with_item: test ['as' expr] +# NB compile.c makes sure that the default except clause is last +except_clause: 'except' [test ['as' NAME]] +suite: simple_stmt | NEWLINE INDENT stmt+ DEDENT + +namedexpr_test: test [':=' test] +test: or_test ['if' or_test 'else' test] | lambdef +lambdef: 'lambda' [varargslist] ':' test +or_test: and_test ('or' and_test)* +and_test: not_test ('and' not_test)* +not_test: 'not' not_test | comparison +comparison: expr (comp_op expr)* +# <> isn't actually a valid comparison operator in Python. It's here for the +# sake of a __future__ import described in PEP 401 (which really works :-) +comp_op: '<'|'>'|'=='|'>='|'<='|'<>'|'!='|'in'|'not' 'in'|'is'|'is' 'not' +star_expr: '*' expr +expr: xor_expr ('|' xor_expr)* +xor_expr: and_expr ('^' and_expr)* +and_expr: shift_expr ('&' shift_expr)* +shift_expr: arith_expr (('<<'|'>>') arith_expr)* +arith_expr: term (('+'|'-') term)* +term: factor (('*'|'@'|'/'|'%'|'//') factor)* +factor: ('+'|'-'|'~') factor | power +power: atom_expr ['**' factor] +atom_expr: ['await'] atom trailer* +atom: ('(' [yield_expr|testlist_comp] ')' | + '[' [testlist_comp] ']' | + '{' [dictorsetmaker] '}' | + NAME | NUMBER | strings | '...' | 'None' | 'True' | 'False') +testlist_comp: (namedexpr_test|star_expr) ( comp_for | (',' (namedexpr_test|star_expr))* [','] ) +trailer: '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME +subscriptlist: subscript (',' subscript)* [','] +subscript: test [':=' test] | [test] ':' [test] [sliceop] +sliceop: ':' [test] +exprlist: (expr|star_expr) (',' (expr|star_expr))* [','] +testlist: test (',' test)* [','] +dictorsetmaker: ( ((test ':' test | '**' expr) + (comp_for | (',' (test ':' test | '**' expr))* [','])) | + ((test [':=' test] | star_expr) + (comp_for | (',' (test [':=' test] | star_expr))* [','])) ) + +classdef: 'class' NAME ['(' [arglist] ')'] ':' suite + +arglist: argument (',' argument)* [','] + +# The reason that keywords are test nodes instead of NAME is that using NAME +# results in an ambiguity. ast.c makes sure it's a NAME. +# "test '=' test" is really "keyword '=' test", but we have no such token. +# These need to be in a single rule to avoid grammar that is ambiguous +# to our LL(1) parser. Even though 'test' includes '*expr' in star_expr, +# we explicitly match '*' here, too, to give it proper precedence. +# Illegal combinations and orderings are blocked in ast.c: +# multiple (test comp_for) arguments are blocked; keyword unpackings +# that precede iterable unpackings are blocked; etc. +argument: ( test [comp_for] | + test ':=' test | + test '=' test | + '**' test | + '*' test ) + +comp_iter: comp_for | comp_if +sync_comp_for: 'for' exprlist 'in' or_test [comp_iter] +comp_for: ['async'] sync_comp_for +comp_if: 'if' or_test [comp_iter] + +# not used in grammar, but may appear in "node" passed from Parser to Compiler +encoding_decl: NAME + +yield_expr: 'yield' [yield_arg] +yield_arg: 'from' test | testlist_star_expr + +strings: (STRING | fstring)+ +fstring: FSTRING_START fstring_content* FSTRING_END +fstring_content: FSTRING_STRING | fstring_expr +fstring_conversion: '!' NAME +fstring_expr: '{' (testlist_comp | yield_expr) ['='] [ fstring_conversion ] [ fstring_format_spec ] '}' +fstring_format_spec: ':' fstring_content* diff --git a/lib/python3.12/site-packages/parso/python/grammar36.txt b/lib/python3.12/site-packages/parso/python/grammar36.txt new file mode 100644 index 0000000000000000000000000000000000000000..e79620668d7283770a9604647ba5d2ee78d98a01 --- /dev/null +++ b/lib/python3.12/site-packages/parso/python/grammar36.txt @@ -0,0 +1,158 @@ +# Grammar for Python + +# NOTE WELL: You should also follow all the steps listed at +# https://docs.python.org/devguide/grammar.html + +# Start symbols for the grammar: +# single_input is a single interactive statement; +# file_input is a module or sequence of commands read from an input file; +# eval_input is the input for the eval() functions. +# NB: compound_stmt in single_input is followed by extra NEWLINE! +single_input: NEWLINE | simple_stmt | compound_stmt NEWLINE +file_input: stmt* ENDMARKER +eval_input: testlist NEWLINE* ENDMARKER +decorator: '@' dotted_name [ '(' [arglist] ')' ] NEWLINE +decorators: decorator+ +decorated: decorators (classdef | funcdef | async_funcdef) + +# NOTE: Francisco Souza/Reinoud Elhorst, using ASYNC/'await' keywords instead of +# skipping python3.5+ compatibility, in favour of 3.7 solution +async_funcdef: 'async' funcdef +funcdef: 'def' NAME parameters ['->' test] ':' suite + +parameters: '(' [typedargslist] ')' +typedargslist: (tfpdef ['=' test] (',' tfpdef ['=' test])* [',' [ + '*' [tfpdef] (',' tfpdef ['=' test])* [',' ['**' tfpdef [',']]] + | '**' tfpdef [',']]] + | '*' [tfpdef] (',' tfpdef ['=' test])* [',' ['**' tfpdef [',']]] + | '**' tfpdef [',']) +tfpdef: NAME [':' test] +varargslist: (vfpdef ['=' test] (',' vfpdef ['=' test])* [',' [ + '*' [vfpdef] (',' vfpdef ['=' test])* [',' ['**' vfpdef [',']]] + | '**' vfpdef [',']]] + | '*' [vfpdef] (',' vfpdef ['=' test])* [',' ['**' vfpdef [',']]] + | '**' vfpdef [','] +) +vfpdef: NAME + +stmt: simple_stmt | compound_stmt | NEWLINE +simple_stmt: small_stmt (';' small_stmt)* [';'] NEWLINE +small_stmt: (expr_stmt | del_stmt | pass_stmt | flow_stmt | + import_stmt | global_stmt | nonlocal_stmt | assert_stmt) +expr_stmt: testlist_star_expr (annassign | augassign (yield_expr|testlist) | + ('=' (yield_expr|testlist_star_expr))*) +annassign: ':' test ['=' test] +testlist_star_expr: (test|star_expr) (',' (test|star_expr))* [','] +augassign: ('+=' | '-=' | '*=' | '@=' | '/=' | '%=' | '&=' | '|=' | '^=' | + '<<=' | '>>=' | '**=' | '//=') +# For normal and annotated assignments, additional restrictions enforced by the interpreter +del_stmt: 'del' exprlist +pass_stmt: 'pass' +flow_stmt: break_stmt | continue_stmt | return_stmt | raise_stmt | yield_stmt +break_stmt: 'break' +continue_stmt: 'continue' +return_stmt: 'return' [testlist] +yield_stmt: yield_expr +raise_stmt: 'raise' [test ['from' test]] +import_stmt: import_name | import_from +import_name: 'import' dotted_as_names +# note below: the ('.' | '...') is necessary because '...' is tokenized as ELLIPSIS +import_from: ('from' (('.' | '...')* dotted_name | ('.' | '...')+) + 'import' ('*' | '(' import_as_names ')' | import_as_names)) +import_as_name: NAME ['as' NAME] +dotted_as_name: dotted_name ['as' NAME] +import_as_names: import_as_name (',' import_as_name)* [','] +dotted_as_names: dotted_as_name (',' dotted_as_name)* +dotted_name: NAME ('.' NAME)* +global_stmt: 'global' NAME (',' NAME)* +nonlocal_stmt: 'nonlocal' NAME (',' NAME)* +assert_stmt: 'assert' test [',' test] + +compound_stmt: if_stmt | while_stmt | for_stmt | try_stmt | with_stmt | funcdef | classdef | decorated | async_stmt +async_stmt: 'async' (funcdef | with_stmt | for_stmt) +if_stmt: 'if' test ':' suite ('elif' test ':' suite)* ['else' ':' suite] +while_stmt: 'while' test ':' suite ['else' ':' suite] +for_stmt: 'for' exprlist 'in' testlist ':' suite ['else' ':' suite] +try_stmt: ('try' ':' suite + ((except_clause ':' suite)+ + ['else' ':' suite] + ['finally' ':' suite] | + 'finally' ':' suite)) +with_stmt: 'with' with_item (',' with_item)* ':' suite +with_item: test ['as' expr] +# NB compile.c makes sure that the default except clause is last +except_clause: 'except' [test ['as' NAME]] +suite: simple_stmt | NEWLINE INDENT stmt+ DEDENT + +test: or_test ['if' or_test 'else' test] | lambdef +test_nocond: or_test | lambdef_nocond +lambdef: 'lambda' [varargslist] ':' test +lambdef_nocond: 'lambda' [varargslist] ':' test_nocond +or_test: and_test ('or' and_test)* +and_test: not_test ('and' not_test)* +not_test: 'not' not_test | comparison +comparison: expr (comp_op expr)* +# <> isn't actually a valid comparison operator in Python. It's here for the +# sake of a __future__ import described in PEP 401 (which really works :-) +comp_op: '<'|'>'|'=='|'>='|'<='|'<>'|'!='|'in'|'not' 'in'|'is'|'is' 'not' +star_expr: '*' expr +expr: xor_expr ('|' xor_expr)* +xor_expr: and_expr ('^' and_expr)* +and_expr: shift_expr ('&' shift_expr)* +shift_expr: arith_expr (('<<'|'>>') arith_expr)* +arith_expr: term (('+'|'-') term)* +term: factor (('*'|'@'|'/'|'%'|'//') factor)* +factor: ('+'|'-'|'~') factor | power +power: atom_expr ['**' factor] +atom_expr: ['await'] atom trailer* +atom: ('(' [yield_expr|testlist_comp] ')' | + '[' [testlist_comp] ']' | + '{' [dictorsetmaker] '}' | + NAME | NUMBER | strings | '...' | 'None' | 'True' | 'False') +testlist_comp: (test|star_expr) ( comp_for | (',' (test|star_expr))* [','] ) +trailer: '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME +subscriptlist: subscript (',' subscript)* [','] +subscript: test | [test] ':' [test] [sliceop] +sliceop: ':' [test] +exprlist: (expr|star_expr) (',' (expr|star_expr))* [','] +testlist: test (',' test)* [','] +dictorsetmaker: ( ((test ':' test | '**' expr) + (comp_for | (',' (test ':' test | '**' expr))* [','])) | + ((test | star_expr) + (comp_for | (',' (test | star_expr))* [','])) ) + +classdef: 'class' NAME ['(' [arglist] ')'] ':' suite + +arglist: argument (',' argument)* [','] + +# The reason that keywords are test nodes instead of NAME is that using NAME +# results in an ambiguity. ast.c makes sure it's a NAME. +# "test '=' test" is really "keyword '=' test", but we have no such token. +# These need to be in a single rule to avoid grammar that is ambiguous +# to our LL(1) parser. Even though 'test' includes '*expr' in star_expr, +# we explicitly match '*' here, too, to give it proper precedence. +# Illegal combinations and orderings are blocked in ast.c: +# multiple (test comp_for) arguments are blocked; keyword unpackings +# that precede iterable unpackings are blocked; etc. +argument: ( test [comp_for] | + test '=' test | + '**' test | + '*' test ) + +comp_iter: comp_for | comp_if +sync_comp_for: 'for' exprlist 'in' or_test [comp_iter] +comp_for: ['async'] sync_comp_for +comp_if: 'if' test_nocond [comp_iter] + +# not used in grammar, but may appear in "node" passed from Parser to Compiler +encoding_decl: NAME + +yield_expr: 'yield' [yield_arg] +yield_arg: 'from' test | testlist + +strings: (STRING | fstring)+ +fstring: FSTRING_START fstring_content* FSTRING_END +fstring_content: FSTRING_STRING | fstring_expr +fstring_conversion: '!' NAME +fstring_expr: '{' (testlist_comp | yield_expr) [ fstring_conversion ] [ fstring_format_spec ] '}' +fstring_format_spec: ':' fstring_content* diff --git a/lib/python3.12/site-packages/parso/python/grammar37.txt b/lib/python3.12/site-packages/parso/python/grammar37.txt new file mode 100644 index 0000000000000000000000000000000000000000..f4a929fe98fc36239a6c03cdf6a49715c1e9112f --- /dev/null +++ b/lib/python3.12/site-packages/parso/python/grammar37.txt @@ -0,0 +1,156 @@ +# Grammar for Python + +# NOTE WELL: You should also follow all the steps listed at +# https://docs.python.org/devguide/grammar.html + +# Start symbols for the grammar: +# single_input is a single interactive statement; +# file_input is a module or sequence of commands read from an input file; +# eval_input is the input for the eval() functions. +# NB: compound_stmt in single_input is followed by extra NEWLINE! +single_input: NEWLINE | simple_stmt | compound_stmt NEWLINE +file_input: stmt* ENDMARKER +eval_input: testlist NEWLINE* ENDMARKER +decorator: '@' dotted_name [ '(' [arglist] ')' ] NEWLINE +decorators: decorator+ +decorated: decorators (classdef | funcdef | async_funcdef) + +async_funcdef: 'async' funcdef +funcdef: 'def' NAME parameters ['->' test] ':' suite + +parameters: '(' [typedargslist] ')' +typedargslist: (tfpdef ['=' test] (',' tfpdef ['=' test])* [',' [ + '*' [tfpdef] (',' tfpdef ['=' test])* [',' ['**' tfpdef [',']]] + | '**' tfpdef [',']]] + | '*' [tfpdef] (',' tfpdef ['=' test])* [',' ['**' tfpdef [',']]] + | '**' tfpdef [',']) +tfpdef: NAME [':' test] +varargslist: (vfpdef ['=' test] (',' vfpdef ['=' test])* [',' [ + '*' [vfpdef] (',' vfpdef ['=' test])* [',' ['**' vfpdef [',']]] + | '**' vfpdef [',']]] + | '*' [vfpdef] (',' vfpdef ['=' test])* [',' ['**' vfpdef [',']]] + | '**' vfpdef [','] +) +vfpdef: NAME + +stmt: simple_stmt | compound_stmt | NEWLINE +simple_stmt: small_stmt (';' small_stmt)* [';'] NEWLINE +small_stmt: (expr_stmt | del_stmt | pass_stmt | flow_stmt | + import_stmt | global_stmt | nonlocal_stmt | assert_stmt) +expr_stmt: testlist_star_expr (annassign | augassign (yield_expr|testlist) | + ('=' (yield_expr|testlist_star_expr))*) +annassign: ':' test ['=' test] +testlist_star_expr: (test|star_expr) (',' (test|star_expr))* [','] +augassign: ('+=' | '-=' | '*=' | '@=' | '/=' | '%=' | '&=' | '|=' | '^=' | + '<<=' | '>>=' | '**=' | '//=') +# For normal and annotated assignments, additional restrictions enforced by the interpreter +del_stmt: 'del' exprlist +pass_stmt: 'pass' +flow_stmt: break_stmt | continue_stmt | return_stmt | raise_stmt | yield_stmt +break_stmt: 'break' +continue_stmt: 'continue' +return_stmt: 'return' [testlist] +yield_stmt: yield_expr +raise_stmt: 'raise' [test ['from' test]] +import_stmt: import_name | import_from +import_name: 'import' dotted_as_names +# note below: the ('.' | '...') is necessary because '...' is tokenized as ELLIPSIS +import_from: ('from' (('.' | '...')* dotted_name | ('.' | '...')+) + 'import' ('*' | '(' import_as_names ')' | import_as_names)) +import_as_name: NAME ['as' NAME] +dotted_as_name: dotted_name ['as' NAME] +import_as_names: import_as_name (',' import_as_name)* [','] +dotted_as_names: dotted_as_name (',' dotted_as_name)* +dotted_name: NAME ('.' NAME)* +global_stmt: 'global' NAME (',' NAME)* +nonlocal_stmt: 'nonlocal' NAME (',' NAME)* +assert_stmt: 'assert' test [',' test] + +compound_stmt: if_stmt | while_stmt | for_stmt | try_stmt | with_stmt | funcdef | classdef | decorated | async_stmt +async_stmt: 'async' (funcdef | with_stmt | for_stmt) +if_stmt: 'if' test ':' suite ('elif' test ':' suite)* ['else' ':' suite] +while_stmt: 'while' test ':' suite ['else' ':' suite] +for_stmt: 'for' exprlist 'in' testlist ':' suite ['else' ':' suite] +try_stmt: ('try' ':' suite + ((except_clause ':' suite)+ + ['else' ':' suite] + ['finally' ':' suite] | + 'finally' ':' suite)) +with_stmt: 'with' with_item (',' with_item)* ':' suite +with_item: test ['as' expr] +# NB compile.c makes sure that the default except clause is last +except_clause: 'except' [test ['as' NAME]] +suite: simple_stmt | NEWLINE INDENT stmt+ DEDENT + +test: or_test ['if' or_test 'else' test] | lambdef +test_nocond: or_test | lambdef_nocond +lambdef: 'lambda' [varargslist] ':' test +lambdef_nocond: 'lambda' [varargslist] ':' test_nocond +or_test: and_test ('or' and_test)* +and_test: not_test ('and' not_test)* +not_test: 'not' not_test | comparison +comparison: expr (comp_op expr)* +# <> isn't actually a valid comparison operator in Python. It's here for the +# sake of a __future__ import described in PEP 401 (which really works :-) +comp_op: '<'|'>'|'=='|'>='|'<='|'<>'|'!='|'in'|'not' 'in'|'is'|'is' 'not' +star_expr: '*' expr +expr: xor_expr ('|' xor_expr)* +xor_expr: and_expr ('^' and_expr)* +and_expr: shift_expr ('&' shift_expr)* +shift_expr: arith_expr (('<<'|'>>') arith_expr)* +arith_expr: term (('+'|'-') term)* +term: factor (('*'|'@'|'/'|'%'|'//') factor)* +factor: ('+'|'-'|'~') factor | power +power: atom_expr ['**' factor] +atom_expr: ['await'] atom trailer* +atom: ('(' [yield_expr|testlist_comp] ')' | + '[' [testlist_comp] ']' | + '{' [dictorsetmaker] '}' | + NAME | NUMBER | strings | '...' | 'None' | 'True' | 'False') +testlist_comp: (test|star_expr) ( comp_for | (',' (test|star_expr))* [','] ) +trailer: '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME +subscriptlist: subscript (',' subscript)* [','] +subscript: test | [test] ':' [test] [sliceop] +sliceop: ':' [test] +exprlist: (expr|star_expr) (',' (expr|star_expr))* [','] +testlist: test (',' test)* [','] +dictorsetmaker: ( ((test ':' test | '**' expr) + (comp_for | (',' (test ':' test | '**' expr))* [','])) | + ((test | star_expr) + (comp_for | (',' (test | star_expr))* [','])) ) + +classdef: 'class' NAME ['(' [arglist] ')'] ':' suite + +arglist: argument (',' argument)* [','] + +# The reason that keywords are test nodes instead of NAME is that using NAME +# results in an ambiguity. ast.c makes sure it's a NAME. +# "test '=' test" is really "keyword '=' test", but we have no such token. +# These need to be in a single rule to avoid grammar that is ambiguous +# to our LL(1) parser. Even though 'test' includes '*expr' in star_expr, +# we explicitly match '*' here, too, to give it proper precedence. +# Illegal combinations and orderings are blocked in ast.c: +# multiple (test comp_for) arguments are blocked; keyword unpackings +# that precede iterable unpackings are blocked; etc. +argument: ( test [comp_for] | + test '=' test | + '**' test | + '*' test ) + +comp_iter: comp_for | comp_if +sync_comp_for: 'for' exprlist 'in' or_test [comp_iter] +comp_for: ['async'] sync_comp_for +comp_if: 'if' test_nocond [comp_iter] + +# not used in grammar, but may appear in "node" passed from Parser to Compiler +encoding_decl: NAME + +yield_expr: 'yield' [yield_arg] +yield_arg: 'from' test | testlist + +strings: (STRING | fstring)+ +fstring: FSTRING_START fstring_content* FSTRING_END +fstring_content: FSTRING_STRING | fstring_expr +fstring_conversion: '!' NAME +fstring_expr: '{' (testlist_comp | yield_expr) [ fstring_conversion ] [ fstring_format_spec ] '}' +fstring_format_spec: ':' fstring_content* diff --git a/lib/python3.12/site-packages/parso/python/grammar38.txt b/lib/python3.12/site-packages/parso/python/grammar38.txt new file mode 100644 index 0000000000000000000000000000000000000000..7288d556f9b4b09154e1e002630162a0e7d0cbe2 --- /dev/null +++ b/lib/python3.12/site-packages/parso/python/grammar38.txt @@ -0,0 +1,171 @@ +# Grammar for Python + +# NOTE WELL: You should also follow all the steps listed at +# https://devguide.python.org/grammar/ + +# Start symbols for the grammar: +# single_input is a single interactive statement; +# file_input is a module or sequence of commands read from an input file; +# eval_input is the input for the eval() functions. +# NB: compound_stmt in single_input is followed by extra NEWLINE! +single_input: NEWLINE | simple_stmt | compound_stmt NEWLINE +file_input: stmt* ENDMARKER +eval_input: testlist NEWLINE* ENDMARKER + +decorator: '@' dotted_name [ '(' [arglist] ')' ] NEWLINE +decorators: decorator+ +decorated: decorators (classdef | funcdef | async_funcdef) + +async_funcdef: 'async' funcdef +funcdef: 'def' NAME parameters ['->' test] ':' suite + +parameters: '(' [typedargslist] ')' +typedargslist: ( + (tfpdef ['=' test] (',' tfpdef ['=' test])* ',' '/' [',' [ tfpdef ['=' test] ( + ',' tfpdef ['=' test])* ([',' [ + '*' [tfpdef] (',' tfpdef ['=' test])* [',' ['**' tfpdef [',']]] + | '**' tfpdef [',']]]) + | '*' [tfpdef] (',' tfpdef ['=' test])* ([',' ['**' tfpdef [',']]]) + | '**' tfpdef [',']]] ) +| (tfpdef ['=' test] (',' tfpdef ['=' test])* [',' [ + '*' [tfpdef] (',' tfpdef ['=' test])* [',' ['**' tfpdef [',']]] + | '**' tfpdef [',']]] + | '*' [tfpdef] (',' tfpdef ['=' test])* [',' ['**' tfpdef [',']]] + | '**' tfpdef [',']) +) +tfpdef: NAME [':' test] +varargslist: vfpdef ['=' test ](',' vfpdef ['=' test])* ',' '/' [',' [ (vfpdef ['=' test] (',' vfpdef ['=' test])* [',' [ + '*' [vfpdef] (',' vfpdef ['=' test])* [',' ['**' vfpdef [',']]] + | '**' vfpdef [',']]] + | '*' [vfpdef] (',' vfpdef ['=' test])* [',' ['**' vfpdef [',']]] + | '**' vfpdef [',']) ]] | (vfpdef ['=' test] (',' vfpdef ['=' test])* [',' [ + '*' [vfpdef] (',' vfpdef ['=' test])* [',' ['**' vfpdef [',']]] + | '**' vfpdef [',']]] + | '*' [vfpdef] (',' vfpdef ['=' test])* [',' ['**' vfpdef [',']]] + | '**' vfpdef [','] +) +vfpdef: NAME + +stmt: simple_stmt | compound_stmt | NEWLINE +simple_stmt: small_stmt (';' small_stmt)* [';'] NEWLINE +small_stmt: (expr_stmt | del_stmt | pass_stmt | flow_stmt | + import_stmt | global_stmt | nonlocal_stmt | assert_stmt) +expr_stmt: testlist_star_expr (annassign | augassign (yield_expr|testlist) | + ('=' (yield_expr|testlist_star_expr))*) +annassign: ':' test ['=' (yield_expr|testlist_star_expr)] +testlist_star_expr: (test|star_expr) (',' (test|star_expr))* [','] +augassign: ('+=' | '-=' | '*=' | '@=' | '/=' | '%=' | '&=' | '|=' | '^=' | + '<<=' | '>>=' | '**=' | '//=') +# For normal and annotated assignments, additional restrictions enforced by the interpreter +del_stmt: 'del' exprlist +pass_stmt: 'pass' +flow_stmt: break_stmt | continue_stmt | return_stmt | raise_stmt | yield_stmt +break_stmt: 'break' +continue_stmt: 'continue' +return_stmt: 'return' [testlist_star_expr] +yield_stmt: yield_expr +raise_stmt: 'raise' [test ['from' test]] +import_stmt: import_name | import_from +import_name: 'import' dotted_as_names +# note below: the ('.' | '...') is necessary because '...' is tokenized as ELLIPSIS +import_from: ('from' (('.' | '...')* dotted_name | ('.' | '...')+) + 'import' ('*' | '(' import_as_names ')' | import_as_names)) +import_as_name: NAME ['as' NAME] +dotted_as_name: dotted_name ['as' NAME] +import_as_names: import_as_name (',' import_as_name)* [','] +dotted_as_names: dotted_as_name (',' dotted_as_name)* +dotted_name: NAME ('.' NAME)* +global_stmt: 'global' NAME (',' NAME)* +nonlocal_stmt: 'nonlocal' NAME (',' NAME)* +assert_stmt: 'assert' test [',' test] + +compound_stmt: if_stmt | while_stmt | for_stmt | try_stmt | with_stmt | funcdef | classdef | decorated | async_stmt +async_stmt: 'async' (funcdef | with_stmt | for_stmt) +if_stmt: 'if' namedexpr_test ':' suite ('elif' namedexpr_test ':' suite)* ['else' ':' suite] +while_stmt: 'while' namedexpr_test ':' suite ['else' ':' suite] +for_stmt: 'for' exprlist 'in' testlist ':' suite ['else' ':' suite] +try_stmt: ('try' ':' suite + ((except_clause ':' suite)+ + ['else' ':' suite] + ['finally' ':' suite] | + 'finally' ':' suite)) +with_stmt: 'with' with_item (',' with_item)* ':' suite +with_item: test ['as' expr] +# NB compile.c makes sure that the default except clause is last +except_clause: 'except' [test ['as' NAME]] +suite: simple_stmt | NEWLINE INDENT stmt+ DEDENT + +namedexpr_test: test [':=' test] +test: or_test ['if' or_test 'else' test] | lambdef +test_nocond: or_test | lambdef_nocond +lambdef: 'lambda' [varargslist] ':' test +lambdef_nocond: 'lambda' [varargslist] ':' test_nocond +or_test: and_test ('or' and_test)* +and_test: not_test ('and' not_test)* +not_test: 'not' not_test | comparison +comparison: expr (comp_op expr)* +# <> isn't actually a valid comparison operator in Python. It's here for the +# sake of a __future__ import described in PEP 401 (which really works :-) +comp_op: '<'|'>'|'=='|'>='|'<='|'<>'|'!='|'in'|'not' 'in'|'is'|'is' 'not' +star_expr: '*' expr +expr: xor_expr ('|' xor_expr)* +xor_expr: and_expr ('^' and_expr)* +and_expr: shift_expr ('&' shift_expr)* +shift_expr: arith_expr (('<<'|'>>') arith_expr)* +arith_expr: term (('+'|'-') term)* +term: factor (('*'|'@'|'/'|'%'|'//') factor)* +factor: ('+'|'-'|'~') factor | power +power: atom_expr ['**' factor] +atom_expr: ['await'] atom trailer* +atom: ('(' [yield_expr|testlist_comp] ')' | + '[' [testlist_comp] ']' | + '{' [dictorsetmaker] '}' | + NAME | NUMBER | strings | '...' | 'None' | 'True' | 'False') +testlist_comp: (namedexpr_test|star_expr) ( comp_for | (',' (namedexpr_test|star_expr))* [','] ) +trailer: '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME +subscriptlist: subscript (',' subscript)* [','] +subscript: test | [test] ':' [test] [sliceop] +sliceop: ':' [test] +exprlist: (expr|star_expr) (',' (expr|star_expr))* [','] +testlist: test (',' test)* [','] +dictorsetmaker: ( ((test ':' test | '**' expr) + (comp_for | (',' (test ':' test | '**' expr))* [','])) | + ((test | star_expr) + (comp_for | (',' (test | star_expr))* [','])) ) + +classdef: 'class' NAME ['(' [arglist] ')'] ':' suite + +arglist: argument (',' argument)* [','] + +# The reason that keywords are test nodes instead of NAME is that using NAME +# results in an ambiguity. ast.c makes sure it's a NAME. +# "test '=' test" is really "keyword '=' test", but we have no such token. +# These need to be in a single rule to avoid grammar that is ambiguous +# to our LL(1) parser. Even though 'test' includes '*expr' in star_expr, +# we explicitly match '*' here, too, to give it proper precedence. +# Illegal combinations and orderings are blocked in ast.c: +# multiple (test comp_for) arguments are blocked; keyword unpackings +# that precede iterable unpackings are blocked; etc. +argument: ( test [comp_for] | + test ':=' test | + test '=' test | + '**' test | + '*' test ) + +comp_iter: comp_for | comp_if +sync_comp_for: 'for' exprlist 'in' or_test [comp_iter] +comp_for: ['async'] sync_comp_for +comp_if: 'if' test_nocond [comp_iter] + +# not used in grammar, but may appear in "node" passed from Parser to Compiler +encoding_decl: NAME + +yield_expr: 'yield' [yield_arg] +yield_arg: 'from' test | testlist_star_expr + +strings: (STRING | fstring)+ +fstring: FSTRING_START fstring_content* FSTRING_END +fstring_content: FSTRING_STRING | fstring_expr +fstring_conversion: '!' NAME +fstring_expr: '{' (testlist_comp | yield_expr) ['='] [ fstring_conversion ] [ fstring_format_spec ] '}' +fstring_format_spec: ':' fstring_content* diff --git a/lib/python3.12/site-packages/parso/python/grammar39.txt b/lib/python3.12/site-packages/parso/python/grammar39.txt new file mode 100644 index 0000000000000000000000000000000000000000..ae46033cf35962d2311f973c34cc3b743617a1a3 --- /dev/null +++ b/lib/python3.12/site-packages/parso/python/grammar39.txt @@ -0,0 +1,169 @@ +# Grammar for Python + +# NOTE WELL: You should also follow all the steps listed at +# https://devguide.python.org/grammar/ + +# Start symbols for the grammar: +# single_input is a single interactive statement; +# file_input is a module or sequence of commands read from an input file; +# eval_input is the input for the eval() functions. +# NB: compound_stmt in single_input is followed by extra NEWLINE! +single_input: NEWLINE | simple_stmt | compound_stmt NEWLINE +file_input: stmt* ENDMARKER +eval_input: testlist NEWLINE* ENDMARKER + +decorator: '@' namedexpr_test NEWLINE +decorators: decorator+ +decorated: decorators (classdef | funcdef | async_funcdef) + +async_funcdef: 'async' funcdef +funcdef: 'def' NAME parameters ['->' test] ':' suite + +parameters: '(' [typedargslist] ')' +typedargslist: ( + (tfpdef ['=' test] (',' tfpdef ['=' test])* ',' '/' [',' [ tfpdef ['=' test] ( + ',' tfpdef ['=' test])* ([',' [ + '*' [tfpdef] (',' tfpdef ['=' test])* [',' ['**' tfpdef [',']]] + | '**' tfpdef [',']]]) + | '*' [tfpdef] (',' tfpdef ['=' test])* ([',' ['**' tfpdef [',']]]) + | '**' tfpdef [',']]] ) +| (tfpdef ['=' test] (',' tfpdef ['=' test])* [',' [ + '*' [tfpdef] (',' tfpdef ['=' test])* [',' ['**' tfpdef [',']]] + | '**' tfpdef [',']]] + | '*' [tfpdef] (',' tfpdef ['=' test])* [',' ['**' tfpdef [',']]] + | '**' tfpdef [',']) +) +tfpdef: NAME [':' test] +varargslist: vfpdef ['=' test ](',' vfpdef ['=' test])* ',' '/' [',' [ (vfpdef ['=' test] (',' vfpdef ['=' test])* [',' [ + '*' [vfpdef] (',' vfpdef ['=' test])* [',' ['**' vfpdef [',']]] + | '**' vfpdef [',']]] + | '*' [vfpdef] (',' vfpdef ['=' test])* [',' ['**' vfpdef [',']]] + | '**' vfpdef [',']) ]] | (vfpdef ['=' test] (',' vfpdef ['=' test])* [',' [ + '*' [vfpdef] (',' vfpdef ['=' test])* [',' ['**' vfpdef [',']]] + | '**' vfpdef [',']]] + | '*' [vfpdef] (',' vfpdef ['=' test])* [',' ['**' vfpdef [',']]] + | '**' vfpdef [','] +) +vfpdef: NAME + +stmt: simple_stmt | compound_stmt | NEWLINE +simple_stmt: small_stmt (';' small_stmt)* [';'] NEWLINE +small_stmt: (expr_stmt | del_stmt | pass_stmt | flow_stmt | + import_stmt | global_stmt | nonlocal_stmt | assert_stmt) +expr_stmt: testlist_star_expr (annassign | augassign (yield_expr|testlist) | + ('=' (yield_expr|testlist_star_expr))*) +annassign: ':' test ['=' (yield_expr|testlist_star_expr)] +testlist_star_expr: (test|star_expr) (',' (test|star_expr))* [','] +augassign: ('+=' | '-=' | '*=' | '@=' | '/=' | '%=' | '&=' | '|=' | '^=' | + '<<=' | '>>=' | '**=' | '//=') +# For normal and annotated assignments, additional restrictions enforced by the interpreter +del_stmt: 'del' exprlist +pass_stmt: 'pass' +flow_stmt: break_stmt | continue_stmt | return_stmt | raise_stmt | yield_stmt +break_stmt: 'break' +continue_stmt: 'continue' +return_stmt: 'return' [testlist_star_expr] +yield_stmt: yield_expr +raise_stmt: 'raise' [test ['from' test]] +import_stmt: import_name | import_from +import_name: 'import' dotted_as_names +# note below: the ('.' | '...') is necessary because '...' is tokenized as ELLIPSIS +import_from: ('from' (('.' | '...')* dotted_name | ('.' | '...')+) + 'import' ('*' | '(' import_as_names ')' | import_as_names)) +import_as_name: NAME ['as' NAME] +dotted_as_name: dotted_name ['as' NAME] +import_as_names: import_as_name (',' import_as_name)* [','] +dotted_as_names: dotted_as_name (',' dotted_as_name)* +dotted_name: NAME ('.' NAME)* +global_stmt: 'global' NAME (',' NAME)* +nonlocal_stmt: 'nonlocal' NAME (',' NAME)* +assert_stmt: 'assert' test [',' test] + +compound_stmt: if_stmt | while_stmt | for_stmt | try_stmt | with_stmt | funcdef | classdef | decorated | async_stmt +async_stmt: 'async' (funcdef | with_stmt | for_stmt) +if_stmt: 'if' namedexpr_test ':' suite ('elif' namedexpr_test ':' suite)* ['else' ':' suite] +while_stmt: 'while' namedexpr_test ':' suite ['else' ':' suite] +for_stmt: 'for' exprlist 'in' testlist ':' suite ['else' ':' suite] +try_stmt: ('try' ':' suite + ((except_clause ':' suite)+ + ['else' ':' suite] + ['finally' ':' suite] | + 'finally' ':' suite)) +with_stmt: 'with' with_item (',' with_item)* ':' suite +with_item: test ['as' expr] +# NB compile.c makes sure that the default except clause is last +except_clause: 'except' [test ['as' NAME]] +suite: simple_stmt | NEWLINE INDENT stmt+ DEDENT + +namedexpr_test: test [':=' test] +test: or_test ['if' or_test 'else' test] | lambdef +lambdef: 'lambda' [varargslist] ':' test +or_test: and_test ('or' and_test)* +and_test: not_test ('and' not_test)* +not_test: 'not' not_test | comparison +comparison: expr (comp_op expr)* +# <> isn't actually a valid comparison operator in Python. It's here for the +# sake of a __future__ import described in PEP 401 (which really works :-) +comp_op: '<'|'>'|'=='|'>='|'<='|'<>'|'!='|'in'|'not' 'in'|'is'|'is' 'not' +star_expr: '*' expr +expr: xor_expr ('|' xor_expr)* +xor_expr: and_expr ('^' and_expr)* +and_expr: shift_expr ('&' shift_expr)* +shift_expr: arith_expr (('<<'|'>>') arith_expr)* +arith_expr: term (('+'|'-') term)* +term: factor (('*'|'@'|'/'|'%'|'//') factor)* +factor: ('+'|'-'|'~') factor | power +power: atom_expr ['**' factor] +atom_expr: ['await'] atom trailer* +atom: ('(' [yield_expr|testlist_comp] ')' | + '[' [testlist_comp] ']' | + '{' [dictorsetmaker] '}' | + NAME | NUMBER | strings | '...' | 'None' | 'True' | 'False') +testlist_comp: (namedexpr_test|star_expr) ( comp_for | (',' (namedexpr_test|star_expr))* [','] ) +trailer: '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME +subscriptlist: subscript (',' subscript)* [','] +subscript: test | [test] ':' [test] [sliceop] +sliceop: ':' [test] +exprlist: (expr|star_expr) (',' (expr|star_expr))* [','] +testlist: test (',' test)* [','] +dictorsetmaker: ( ((test ':' test | '**' expr) + (comp_for | (',' (test ':' test | '**' expr))* [','])) | + ((test [':=' test] | star_expr) + (comp_for | (',' (test [':=' test] | star_expr))* [','])) ) + +classdef: 'class' NAME ['(' [arglist] ')'] ':' suite + +arglist: argument (',' argument)* [','] + +# The reason that keywords are test nodes instead of NAME is that using NAME +# results in an ambiguity. ast.c makes sure it's a NAME. +# "test '=' test" is really "keyword '=' test", but we have no such token. +# These need to be in a single rule to avoid grammar that is ambiguous +# to our LL(1) parser. Even though 'test' includes '*expr' in star_expr, +# we explicitly match '*' here, too, to give it proper precedence. +# Illegal combinations and orderings are blocked in ast.c: +# multiple (test comp_for) arguments are blocked; keyword unpackings +# that precede iterable unpackings are blocked; etc. +argument: ( test [comp_for] | + test ':=' test | + test '=' test | + '**' test | + '*' test ) + +comp_iter: comp_for | comp_if +sync_comp_for: 'for' exprlist 'in' or_test [comp_iter] +comp_for: ['async'] sync_comp_for +comp_if: 'if' or_test [comp_iter] + +# not used in grammar, but may appear in "node" passed from Parser to Compiler +encoding_decl: NAME + +yield_expr: 'yield' [yield_arg] +yield_arg: 'from' test | testlist_star_expr + +strings: (STRING | fstring)+ +fstring: FSTRING_START fstring_content* FSTRING_END +fstring_content: FSTRING_STRING | fstring_expr +fstring_conversion: '!' NAME +fstring_expr: '{' (testlist_comp | yield_expr) ['='] [ fstring_conversion ] [ fstring_format_spec ] '}' +fstring_format_spec: ':' fstring_content* diff --git a/lib/python3.12/site-packages/parso/python/parser.py b/lib/python3.12/site-packages/parso/python/parser.py new file mode 100644 index 0000000000000000000000000000000000000000..fa45e8b1530a433a8e8e529a5376b6ff2f5612e7 --- /dev/null +++ b/lib/python3.12/site-packages/parso/python/parser.py @@ -0,0 +1,206 @@ +from parso.python import tree +from parso.python.token import PythonTokenTypes +from parso.parser import BaseParser + + +NAME = PythonTokenTypes.NAME +INDENT = PythonTokenTypes.INDENT +DEDENT = PythonTokenTypes.DEDENT + + +class Parser(BaseParser): + """ + This class is used to parse a Python file, it then divides them into a + class structure of different scopes. + + :param pgen_grammar: The grammar object of pgen2. Loaded by load_grammar. + """ + + node_map = { + 'expr_stmt': tree.ExprStmt, + 'classdef': tree.Class, + 'funcdef': tree.Function, + 'file_input': tree.Module, + 'import_name': tree.ImportName, + 'import_from': tree.ImportFrom, + 'break_stmt': tree.KeywordStatement, + 'continue_stmt': tree.KeywordStatement, + 'return_stmt': tree.ReturnStmt, + 'raise_stmt': tree.KeywordStatement, + 'yield_expr': tree.YieldExpr, + 'del_stmt': tree.KeywordStatement, + 'pass_stmt': tree.KeywordStatement, + 'global_stmt': tree.GlobalStmt, + 'nonlocal_stmt': tree.KeywordStatement, + 'print_stmt': tree.KeywordStatement, + 'assert_stmt': tree.AssertStmt, + 'if_stmt': tree.IfStmt, + 'with_stmt': tree.WithStmt, + 'for_stmt': tree.ForStmt, + 'while_stmt': tree.WhileStmt, + 'try_stmt': tree.TryStmt, + 'sync_comp_for': tree.SyncCompFor, + # Not sure if this is the best idea, but IMO it's the easiest way to + # avoid extreme amounts of work around the subtle difference of 2/3 + # grammar in list comoprehensions. + 'decorator': tree.Decorator, + 'lambdef': tree.Lambda, + 'lambdef_nocond': tree.Lambda, + 'namedexpr_test': tree.NamedExpr, + } + default_node = tree.PythonNode + + # Names/Keywords are handled separately + _leaf_map = { + PythonTokenTypes.STRING: tree.String, + PythonTokenTypes.NUMBER: tree.Number, + PythonTokenTypes.NEWLINE: tree.Newline, + PythonTokenTypes.ENDMARKER: tree.EndMarker, + PythonTokenTypes.FSTRING_STRING: tree.FStringString, + PythonTokenTypes.FSTRING_START: tree.FStringStart, + PythonTokenTypes.FSTRING_END: tree.FStringEnd, + } + + def __init__(self, pgen_grammar, error_recovery=True, start_nonterminal='file_input'): + super().__init__(pgen_grammar, start_nonterminal, + error_recovery=error_recovery) + + self.syntax_errors = [] + self._omit_dedent_list = [] + self._indent_counter = 0 + + def parse(self, tokens): + if self._error_recovery: + if self._start_nonterminal != 'file_input': + raise NotImplementedError + + tokens = self._recovery_tokenize(tokens) + + return super().parse(tokens) + + def convert_node(self, nonterminal, children): + """ + Convert raw node information to a PythonBaseNode instance. + + This is passed to the parser driver which calls it whenever a reduction of a + grammar rule produces a new complete node, so that the tree is build + strictly bottom-up. + """ + try: + node = self.node_map[nonterminal](children) + except KeyError: + if nonterminal == 'suite': + # We don't want the INDENT/DEDENT in our parser tree. Those + # leaves are just cancer. They are virtual leaves and not real + # ones and therefore have pseudo start/end positions and no + # prefixes. Just ignore them. + children = [children[0]] + children[2:-1] + node = self.default_node(nonterminal, children) + return node + + def convert_leaf(self, type, value, prefix, start_pos): + # print('leaf', repr(value), token.tok_name[type]) + if type == NAME: + if value in self._pgen_grammar.reserved_syntax_strings: + return tree.Keyword(value, start_pos, prefix) + else: + return tree.Name(value, start_pos, prefix) + + return self._leaf_map.get(type, tree.Operator)(value, start_pos, prefix) + + def error_recovery(self, token): + tos_nodes = self.stack[-1].nodes + if tos_nodes: + last_leaf = tos_nodes[-1].get_last_leaf() + else: + last_leaf = None + + if self._start_nonterminal == 'file_input' and \ + (token.type == PythonTokenTypes.ENDMARKER + or token.type == DEDENT and not last_leaf.value.endswith('\n') + and not last_leaf.value.endswith('\r')): + # In Python statements need to end with a newline. But since it's + # possible (and valid in Python) that there's no newline at the + # end of a file, we have to recover even if the user doesn't want + # error recovery. + if self.stack[-1].dfa.from_rule == 'simple_stmt': + try: + plan = self.stack[-1].dfa.transitions[PythonTokenTypes.NEWLINE] + except KeyError: + pass + else: + if plan.next_dfa.is_final and not plan.dfa_pushes: + # We are ignoring here that the newline would be + # required for a simple_stmt. + self.stack[-1].dfa = plan.next_dfa + self._add_token(token) + return + + if not self._error_recovery: + return super().error_recovery(token) + + def current_suite(stack): + # For now just discard everything that is not a suite or + # file_input, if we detect an error. + for until_index, stack_node in reversed(list(enumerate(stack))): + # `suite` can sometimes be only simple_stmt, not stmt. + if stack_node.nonterminal == 'file_input': + break + elif stack_node.nonterminal == 'suite': + # In the case where we just have a newline we don't want to + # do error recovery here. In all other cases, we want to do + # error recovery. + if len(stack_node.nodes) != 1: + break + return until_index + + until_index = current_suite(self.stack) + + if self._stack_removal(until_index + 1): + self._add_token(token) + else: + typ, value, start_pos, prefix = token + if typ == INDENT: + # For every deleted INDENT we have to delete a DEDENT as well. + # Otherwise the parser will get into trouble and DEDENT too early. + self._omit_dedent_list.append(self._indent_counter) + + error_leaf = tree.PythonErrorLeaf(typ.name, value, start_pos, prefix) + self.stack[-1].nodes.append(error_leaf) + + tos = self.stack[-1] + if tos.nonterminal == 'suite': + # Need at least one statement in the suite. This happend with the + # error recovery above. + try: + tos.dfa = tos.dfa.arcs['stmt'] + except KeyError: + # We're already in a final state. + pass + + def _stack_removal(self, start_index): + all_nodes = [node for stack_node in self.stack[start_index:] for node in stack_node.nodes] + + if all_nodes: + node = tree.PythonErrorNode(all_nodes) + self.stack[start_index - 1].nodes.append(node) + + self.stack[start_index:] = [] + return bool(all_nodes) + + def _recovery_tokenize(self, tokens): + for token in tokens: + typ = token[0] + if typ == DEDENT: + # We need to count indents, because if we just omit any DEDENT, + # we might omit them in the wrong place. + o = self._omit_dedent_list + if o and o[-1] == self._indent_counter: + o.pop() + self._indent_counter -= 1 + continue + + self._indent_counter -= 1 + elif typ == INDENT: + self._indent_counter += 1 + yield token diff --git a/lib/python3.12/site-packages/parso/python/pep8.py b/lib/python3.12/site-packages/parso/python/pep8.py new file mode 100644 index 0000000000000000000000000000000000000000..c492dae682e110f500cbfe48a286d3c6e14fbc2c --- /dev/null +++ b/lib/python3.12/site-packages/parso/python/pep8.py @@ -0,0 +1,767 @@ +import re +from contextlib import contextmanager +from typing import Tuple + +from parso.python.errors import ErrorFinder, ErrorFinderConfig +from parso.normalizer import Rule +from parso.python.tree import Flow, Scope + + +_IMPORT_TYPES = ('import_name', 'import_from') +_SUITE_INTRODUCERS = ('classdef', 'funcdef', 'if_stmt', 'while_stmt', + 'for_stmt', 'try_stmt', 'with_stmt') +_NON_STAR_TYPES = ('term', 'import_from', 'power') +_OPENING_BRACKETS = '(', '[', '{' +_CLOSING_BRACKETS = ')', ']', '}' +_FACTOR = '+', '-', '~' +_ALLOW_SPACE = '*', '+', '-', '**', '/', '//', '@' +_BITWISE_OPERATOR = '<<', '>>', '|', '&', '^' +_NEEDS_SPACE: Tuple[str, ...] = ( + '=', '%', '->', + '<', '>', '==', '>=', '<=', '<>', '!=', + '+=', '-=', '*=', '@=', '/=', '%=', '&=', '|=', '^=', '<<=', + '>>=', '**=', '//=') +_NEEDS_SPACE += _BITWISE_OPERATOR +_IMPLICIT_INDENTATION_TYPES = ('dictorsetmaker', 'argument') +_POSSIBLE_SLICE_PARENTS = ('subscript', 'subscriptlist', 'sliceop') + + +class IndentationTypes: + VERTICAL_BRACKET = object() + HANGING_BRACKET = object() + BACKSLASH = object() + SUITE = object() + IMPLICIT = object() + + +class IndentationNode(object): + type = IndentationTypes.SUITE + + def __init__(self, config, indentation, parent=None): + self.bracket_indentation = self.indentation = indentation + self.parent = parent + + def __repr__(self): + return '<%s>' % self.__class__.__name__ + + def get_latest_suite_node(self): + n = self + while n is not None: + if n.type == IndentationTypes.SUITE: + return n + + n = n.parent + + +class BracketNode(IndentationNode): + def __init__(self, config, leaf, parent, in_suite_introducer=False): + self.leaf = leaf + + # Figure out here what the indentation is. For chained brackets + # we can basically use the previous indentation. + previous_leaf = leaf + n = parent + if n.type == IndentationTypes.IMPLICIT: + n = n.parent + while True: + if hasattr(n, 'leaf') and previous_leaf.line != n.leaf.line: + break + + previous_leaf = previous_leaf.get_previous_leaf() + if not isinstance(n, BracketNode) or previous_leaf != n.leaf: + break + n = n.parent + parent_indentation = n.indentation + + next_leaf = leaf.get_next_leaf() + if '\n' in next_leaf.prefix or '\r' in next_leaf.prefix: + # This implies code like: + # foobarbaz( + # a, + # b, + # ) + self.bracket_indentation = parent_indentation \ + + config.closing_bracket_hanging_indentation + self.indentation = parent_indentation + config.indentation + self.type = IndentationTypes.HANGING_BRACKET + else: + # Implies code like: + # foobarbaz( + # a, + # b, + # ) + expected_end_indent = leaf.end_pos[1] + if '\t' in config.indentation: + self.indentation = None + else: + self.indentation = ' ' * expected_end_indent + self.bracket_indentation = self.indentation + self.type = IndentationTypes.VERTICAL_BRACKET + + if in_suite_introducer and parent.type == IndentationTypes.SUITE \ + and self.indentation == parent_indentation + config.indentation: + self.indentation += config.indentation + # The closing bracket should have the same indentation. + self.bracket_indentation = self.indentation + self.parent = parent + + +class ImplicitNode(BracketNode): + """ + Implicit indentation after keyword arguments, default arguments, + annotations and dict values. + """ + def __init__(self, config, leaf, parent): + super().__init__(config, leaf, parent) + self.type = IndentationTypes.IMPLICIT + + next_leaf = leaf.get_next_leaf() + if leaf == ':' and '\n' not in next_leaf.prefix and '\r' not in next_leaf.prefix: + self.indentation += ' ' + + +class BackslashNode(IndentationNode): + type = IndentationTypes.BACKSLASH + + def __init__(self, config, parent_indentation, containing_leaf, spacing, parent=None): + expr_stmt = containing_leaf.search_ancestor('expr_stmt') + if expr_stmt is not None: + equals = expr_stmt.children[-2] + + if '\t' in config.indentation: + # TODO unite with the code of BracketNode + self.indentation = None + else: + # If the backslash follows the equals, use normal indentation + # otherwise it should align with the equals. + if equals.end_pos == spacing.start_pos: + self.indentation = parent_indentation + config.indentation + else: + # +1 because there is a space. + self.indentation = ' ' * (equals.end_pos[1] + 1) + else: + self.indentation = parent_indentation + config.indentation + self.bracket_indentation = self.indentation + self.parent = parent + + +def _is_magic_name(name): + return name.value.startswith('__') and name.value.endswith('__') + + +class PEP8Normalizer(ErrorFinder): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self._previous_part = None + self._previous_leaf = None + self._on_newline = True + self._newline_count = 0 + self._wanted_newline_count = None + self._max_new_lines_in_prefix = 0 + self._new_statement = True + self._implicit_indentation_possible = False + # The top of stack of the indentation nodes. + self._indentation_tos = self._last_indentation_tos = \ + IndentationNode(self._config, indentation='') + self._in_suite_introducer = False + + if ' ' in self._config.indentation: + self._indentation_type = 'spaces' + self._wrong_indentation_char = '\t' + else: + self._indentation_type = 'tabs' + self._wrong_indentation_char = ' ' + + @contextmanager + def visit_node(self, node): + with super().visit_node(node): + with self._visit_node(node): + yield + + @contextmanager + def _visit_node(self, node): + typ = node.type + + if typ in 'import_name': + names = node.get_defined_names() + if len(names) > 1: + for name in names[:1]: + self.add_issue(name, 401, 'Multiple imports on one line') + elif typ == 'lambdef': + expr_stmt = node.parent + # Check if it's simply defining a single name, not something like + # foo.bar or x[1], where using a lambda could make more sense. + if expr_stmt.type == 'expr_stmt' and any(n.type == 'name' + for n in expr_stmt.children[:-2:2]): + self.add_issue(node, 731, 'Do not assign a lambda expression, use a def') + elif typ == 'try_stmt': + for child in node.children: + # Here we can simply check if it's an except, because otherwise + # it would be an except_clause. + if child.type == 'keyword' and child.value == 'except': + self.add_issue(child, 722, 'Do not use bare except, specify exception instead') + elif typ == 'comparison': + for child in node.children: + if child.type not in ('atom_expr', 'power'): + continue + if len(child.children) > 2: + continue + trailer = child.children[1] + atom = child.children[0] + if trailer.type == 'trailer' and atom.type == 'name' \ + and atom.value == 'type': + self.add_issue(node, 721, "Do not compare types, use 'isinstance()") + break + elif typ == 'file_input': + endmarker = node.children[-1] + prev = endmarker.get_previous_leaf() + prefix = endmarker.prefix + if (not prefix.endswith('\n') and not prefix.endswith('\r') and ( + prefix or prev is None or prev.value not in {'\n', '\r\n', '\r'})): + self.add_issue(endmarker, 292, "No newline at end of file") + + if typ in _IMPORT_TYPES: + simple_stmt = node.parent + module = simple_stmt.parent + if module.type == 'file_input': + index = module.children.index(simple_stmt) + for child in module.children[:index]: + children = [child] + if child.type == 'simple_stmt': + # Remove the newline. + children = child.children[:-1] + + found_docstring = False + for c in children: + if c.type == 'string' and not found_docstring: + continue + found_docstring = True + + if c.type == 'expr_stmt' and \ + all(_is_magic_name(n) for n in c.get_defined_names()): + continue + + if c.type in _IMPORT_TYPES or isinstance(c, Flow): + continue + + self.add_issue(node, 402, 'Module level import not at top of file') + break + else: + continue + break + + implicit_indentation_possible = typ in _IMPLICIT_INDENTATION_TYPES + in_introducer = typ in _SUITE_INTRODUCERS + if in_introducer: + self._in_suite_introducer = True + elif typ == 'suite': + if self._indentation_tos.type == IndentationTypes.BACKSLASH: + self._indentation_tos = self._indentation_tos.parent + + self._indentation_tos = IndentationNode( + self._config, + self._indentation_tos.indentation + self._config.indentation, + parent=self._indentation_tos + ) + elif implicit_indentation_possible: + self._implicit_indentation_possible = True + yield + if typ == 'suite': + assert self._indentation_tos.type == IndentationTypes.SUITE + self._indentation_tos = self._indentation_tos.parent + # If we dedent, no lines are needed anymore. + self._wanted_newline_count = None + elif implicit_indentation_possible: + self._implicit_indentation_possible = False + if self._indentation_tos.type == IndentationTypes.IMPLICIT: + self._indentation_tos = self._indentation_tos.parent + elif in_introducer: + self._in_suite_introducer = False + if typ in ('classdef', 'funcdef'): + self._wanted_newline_count = self._get_wanted_blank_lines_count() + + def _check_tabs_spaces(self, spacing): + if self._wrong_indentation_char in spacing.value: + self.add_issue(spacing, 101, 'Indentation contains ' + self._indentation_type) + return True + return False + + def _get_wanted_blank_lines_count(self): + suite_node = self._indentation_tos.get_latest_suite_node() + return int(suite_node.parent is None) + 1 + + def _reset_newlines(self, spacing, leaf, is_comment=False): + self._max_new_lines_in_prefix = \ + max(self._max_new_lines_in_prefix, self._newline_count) + + wanted = self._wanted_newline_count + if wanted is not None: + # Need to substract one + blank_lines = self._newline_count - 1 + if wanted > blank_lines and leaf.type != 'endmarker': + # In case of a comment we don't need to add the issue, yet. + if not is_comment: + # TODO end_pos wrong. + code = 302 if wanted == 2 else 301 + message = "expected %s blank line, found %s" \ + % (wanted, blank_lines) + self.add_issue(spacing, code, message) + self._wanted_newline_count = None + else: + self._wanted_newline_count = None + + if not is_comment: + wanted = self._get_wanted_blank_lines_count() + actual = self._max_new_lines_in_prefix - 1 + + val = leaf.value + needs_lines = ( + val == '@' and leaf.parent.type == 'decorator' + or ( + val == 'class' + or val == 'async' and leaf.get_next_leaf() == 'def' + or val == 'def' and self._previous_leaf != 'async' + ) and leaf.parent.parent.type != 'decorated' + ) + if needs_lines and actual < wanted: + func_or_cls = leaf.parent + suite = func_or_cls.parent + if suite.type == 'decorated': + suite = suite.parent + + # The first leaf of a file or a suite should not need blank + # lines. + if suite.children[int(suite.type == 'suite')] != func_or_cls: + code = 302 if wanted == 2 else 301 + message = "expected %s blank line, found %s" \ + % (wanted, actual) + self.add_issue(spacing, code, message) + + self._max_new_lines_in_prefix = 0 + + self._newline_count = 0 + + def visit_leaf(self, leaf): + super().visit_leaf(leaf) + for part in leaf._split_prefix(): + if part.type == 'spacing': + # This part is used for the part call after for. + break + self._visit_part(part, part.create_spacing_part(), leaf) + + self._analyse_non_prefix(leaf) + self._visit_part(leaf, part, leaf) + + # Cleanup + self._last_indentation_tos = self._indentation_tos + + self._new_statement = leaf.type == 'newline' + + # TODO does this work? with brackets and stuff? + if leaf.type == 'newline' and \ + self._indentation_tos.type == IndentationTypes.BACKSLASH: + self._indentation_tos = self._indentation_tos.parent + + if leaf.value == ':' and leaf.parent.type in _SUITE_INTRODUCERS: + self._in_suite_introducer = False + elif leaf.value == 'elif': + self._in_suite_introducer = True + + if not self._new_statement: + self._reset_newlines(part, leaf) + self._max_blank_lines = 0 + + self._previous_leaf = leaf + + return leaf.value + + def _visit_part(self, part, spacing, leaf): + value = part.value + type_ = part.type + if type_ == 'error_leaf': + return + + if value == ',' and part.parent.type == 'dictorsetmaker': + self._indentation_tos = self._indentation_tos.parent + + node = self._indentation_tos + + if type_ == 'comment': + if value.startswith('##'): + # Whole blocks of # should not raise an error. + if value.lstrip('#'): + self.add_issue(part, 266, "Too many leading '#' for block comment.") + elif self._on_newline: + if not re.match(r'#:? ', value) and not value == '#' \ + and not (value.startswith('#!') and part.start_pos == (1, 0)): + self.add_issue(part, 265, "Block comment should start with '# '") + else: + if not re.match(r'#:? [^ ]', value): + self.add_issue(part, 262, "Inline comment should start with '# '") + + self._reset_newlines(spacing, leaf, is_comment=True) + elif type_ == 'newline': + if self._newline_count > self._get_wanted_blank_lines_count(): + self.add_issue(part, 303, "Too many blank lines (%s)" % self._newline_count) + elif leaf in ('def', 'class') \ + and leaf.parent.parent.type == 'decorated': + self.add_issue(part, 304, "Blank lines found after function decorator") + + self._newline_count += 1 + + if type_ == 'backslash': + # TODO is this enough checking? What about ==? + if node.type != IndentationTypes.BACKSLASH: + if node.type != IndentationTypes.SUITE: + self.add_issue(part, 502, 'The backslash is redundant between brackets') + else: + indentation = node.indentation + if self._in_suite_introducer and node.type == IndentationTypes.SUITE: + indentation += self._config.indentation + + self._indentation_tos = BackslashNode( + self._config, + indentation, + part, + spacing, + parent=self._indentation_tos + ) + elif self._on_newline: + indentation = spacing.value + if node.type == IndentationTypes.BACKSLASH \ + and self._previous_part.type == 'newline': + self._indentation_tos = self._indentation_tos.parent + + if not self._check_tabs_spaces(spacing): + should_be_indentation = node.indentation + if type_ == 'comment': + # Comments can be dedented. So we have to care for that. + n = self._last_indentation_tos + while True: + if len(indentation) > len(n.indentation): + break + + should_be_indentation = n.indentation + + self._last_indentation_tos = n + if n == node: + break + n = n.parent + + if self._new_statement: + if type_ == 'newline': + if indentation: + self.add_issue(spacing, 291, 'Trailing whitespace') + elif indentation != should_be_indentation: + s = '%s %s' % (len(self._config.indentation), self._indentation_type) + self.add_issue(part, 111, 'Indentation is not a multiple of ' + s) + else: + if value in '])}': + should_be_indentation = node.bracket_indentation + else: + should_be_indentation = node.indentation + if self._in_suite_introducer and indentation == \ + node.get_latest_suite_node().indentation \ + + self._config.indentation: + self.add_issue(part, 129, "Line with same indent as next logical block") + elif indentation != should_be_indentation: + if not self._check_tabs_spaces(spacing) and part.value not in \ + {'\n', '\r\n', '\r'}: + if value in '])}': + if node.type == IndentationTypes.VERTICAL_BRACKET: + self.add_issue( + part, + 124, + "Closing bracket does not match visual indentation" + ) + else: + self.add_issue( + part, + 123, + "Losing bracket does not match " + "indentation of opening bracket's line" + ) + else: + if len(indentation) < len(should_be_indentation): + if node.type == IndentationTypes.VERTICAL_BRACKET: + self.add_issue( + part, + 128, + 'Continuation line under-indented for visual indent' + ) + elif node.type == IndentationTypes.BACKSLASH: + self.add_issue( + part, + 122, + 'Continuation line missing indentation or outdented' + ) + elif node.type == IndentationTypes.IMPLICIT: + self.add_issue(part, 135, 'xxx') + else: + self.add_issue( + part, + 121, + 'Continuation line under-indented for hanging indent' + ) + else: + if node.type == IndentationTypes.VERTICAL_BRACKET: + self.add_issue( + part, + 127, + 'Continuation line over-indented for visual indent' + ) + elif node.type == IndentationTypes.IMPLICIT: + self.add_issue(part, 136, 'xxx') + else: + self.add_issue( + part, + 126, + 'Continuation line over-indented for hanging indent' + ) + else: + self._check_spacing(part, spacing) + + self._check_line_length(part, spacing) + # ------------------------------- + # Finalizing. Updating the state. + # ------------------------------- + if value and value in '()[]{}' and type_ != 'error_leaf' \ + and part.parent.type != 'error_node': + if value in _OPENING_BRACKETS: + self._indentation_tos = BracketNode( + self._config, part, + parent=self._indentation_tos, + in_suite_introducer=self._in_suite_introducer + ) + else: + assert node.type != IndentationTypes.IMPLICIT + self._indentation_tos = self._indentation_tos.parent + elif value in ('=', ':') and self._implicit_indentation_possible \ + and part.parent.type in _IMPLICIT_INDENTATION_TYPES: + indentation = node.indentation + self._indentation_tos = ImplicitNode( + self._config, part, parent=self._indentation_tos + ) + + self._on_newline = type_ in ('newline', 'backslash', 'bom') + + self._previous_part = part + self._previous_spacing = spacing + + def _check_line_length(self, part, spacing): + if part.type == 'backslash': + last_column = part.start_pos[1] + 1 + else: + last_column = part.end_pos[1] + if last_column > self._config.max_characters \ + and spacing.start_pos[1] <= self._config.max_characters: + # Special case for long URLs in multi-line docstrings or comments, + # but still report the error when the 72 first chars are whitespaces. + report = True + if part.type == 'comment': + splitted = part.value[1:].split() + if len(splitted) == 1 \ + and (part.end_pos[1] - len(splitted[0])) < 72: + report = False + if report: + self.add_issue( + part, + 501, + 'Line too long (%s > %s characters)' % + (last_column, self._config.max_characters), + ) + + def _check_spacing(self, part, spacing): + def add_if_spaces(*args): + if spaces: + return self.add_issue(*args) + + def add_not_spaces(*args): + if not spaces: + return self.add_issue(*args) + + spaces = spacing.value + prev = self._previous_part + if prev is not None and prev.type == 'error_leaf' or part.type == 'error_leaf': + return + + type_ = part.type + if '\t' in spaces: + self.add_issue(spacing, 223, 'Used tab to separate tokens') + elif type_ == 'comment': + if len(spaces) < self._config.spaces_before_comment: + self.add_issue(spacing, 261, 'At least two spaces before inline comment') + elif type_ == 'newline': + add_if_spaces(spacing, 291, 'Trailing whitespace') + elif len(spaces) > 1: + self.add_issue(spacing, 221, 'Multiple spaces used') + else: + if prev in _OPENING_BRACKETS: + message = "Whitespace after '%s'" % part.value + add_if_spaces(spacing, 201, message) + elif part in _CLOSING_BRACKETS: + message = "Whitespace before '%s'" % part.value + add_if_spaces(spacing, 202, message) + elif part in (',', ';') or part == ':' \ + and part.parent.type not in _POSSIBLE_SLICE_PARENTS: + message = "Whitespace before '%s'" % part.value + add_if_spaces(spacing, 203, message) + elif prev == ':' and prev.parent.type in _POSSIBLE_SLICE_PARENTS: + pass # TODO + elif prev in (',', ';', ':'): + add_not_spaces(spacing, 231, "missing whitespace after '%s'") + elif part == ':': # Is a subscript + # TODO + pass + elif part in ('*', '**') and part.parent.type not in _NON_STAR_TYPES \ + or prev in ('*', '**') \ + and prev.parent.type not in _NON_STAR_TYPES: + # TODO + pass + elif prev in _FACTOR and prev.parent.type == 'factor': + pass + elif prev == '@' and prev.parent.type == 'decorator': + pass # TODO should probably raise an error if there's a space here + elif part in _NEEDS_SPACE or prev in _NEEDS_SPACE: + if part == '=' and part.parent.type in ('argument', 'param') \ + or prev == '=' and prev.parent.type in ('argument', 'param'): + if part == '=': + param = part.parent + else: + param = prev.parent + if param.type == 'param' and param.annotation: + add_not_spaces(spacing, 252, 'Expected spaces around annotation equals') + else: + add_if_spaces( + spacing, + 251, + 'Unexpected spaces around keyword / parameter equals' + ) + elif part in _BITWISE_OPERATOR or prev in _BITWISE_OPERATOR: + add_not_spaces( + spacing, + 227, + 'Missing whitespace around bitwise or shift operator' + ) + elif part == '%' or prev == '%': + add_not_spaces(spacing, 228, 'Missing whitespace around modulo operator') + else: + message_225 = 'Missing whitespace between tokens' + add_not_spaces(spacing, 225, message_225) + elif type_ == 'keyword' or prev.type == 'keyword': + add_not_spaces(spacing, 275, 'Missing whitespace around keyword') + else: + prev_spacing = self._previous_spacing + if prev in _ALLOW_SPACE and spaces != prev_spacing.value \ + and '\n' not in self._previous_leaf.prefix \ + and '\r' not in self._previous_leaf.prefix: + message = "Whitespace before operator doesn't match with whitespace after" + self.add_issue(spacing, 229, message) + + if spaces and part not in _ALLOW_SPACE and prev not in _ALLOW_SPACE: + message_225 = 'Missing whitespace between tokens' + # self.add_issue(spacing, 225, message_225) + # TODO why only brackets? + if part in _OPENING_BRACKETS: + message = "Whitespace before '%s'" % part.value + add_if_spaces(spacing, 211, message) + + def _analyse_non_prefix(self, leaf): + typ = leaf.type + if typ == 'name' and leaf.value in ('l', 'O', 'I'): + if leaf.is_definition(): + message = "Do not define %s named 'l', 'O', or 'I' one line" + if leaf.parent.type == 'class' and leaf.parent.name == leaf: + self.add_issue(leaf, 742, message % 'classes') + elif leaf.parent.type == 'function' and leaf.parent.name == leaf: + self.add_issue(leaf, 743, message % 'function') + else: + self.add_issuadd_issue(741, message % 'variables', leaf) + elif leaf.value == ':': + if isinstance(leaf.parent, (Flow, Scope)) and leaf.parent.type != 'lambdef': + next_leaf = leaf.get_next_leaf() + if next_leaf.type != 'newline': + if leaf.parent.type == 'funcdef': + self.add_issue(next_leaf, 704, 'Multiple statements on one line (def)') + else: + self.add_issue(next_leaf, 701, 'Multiple statements on one line (colon)') + elif leaf.value == ';': + if leaf.get_next_leaf().type in ('newline', 'endmarker'): + self.add_issue(leaf, 703, 'Statement ends with a semicolon') + else: + self.add_issue(leaf, 702, 'Multiple statements on one line (semicolon)') + elif leaf.value in ('==', '!='): + comparison = leaf.parent + index = comparison.children.index(leaf) + left = comparison.children[index - 1] + right = comparison.children[index + 1] + for node in left, right: + if node.type == 'keyword' or node.type == 'name': + if node.value == 'None': + message = "comparison to None should be 'if cond is None:'" + self.add_issue(leaf, 711, message) + break + elif node.value in ('True', 'False'): + message = "comparison to False/True should be " \ + "'if cond is True:' or 'if cond:'" + self.add_issue(leaf, 712, message) + break + elif leaf.value in ('in', 'is'): + comparison = leaf.parent + if comparison.type == 'comparison' and comparison.parent.type == 'not_test': + if leaf.value == 'in': + self.add_issue(leaf, 713, "test for membership should be 'not in'") + else: + self.add_issue(leaf, 714, "test for object identity should be 'is not'") + elif typ == 'string': + # Checking multiline strings + for i, line in enumerate(leaf.value.splitlines()[1:]): + indentation = re.match(r'[ \t]*', line).group(0) + start_pos = leaf.line + i, len(indentation) + # TODO check multiline indentation. + start_pos + elif typ == 'endmarker': + if self._newline_count >= 2: + self.add_issue(leaf, 391, 'Blank line at end of file') + + def add_issue(self, node, code, message): + if self._previous_leaf is not None: + if self._previous_leaf.search_ancestor('error_node') is not None: + return + if self._previous_leaf.type == 'error_leaf': + return + if node.search_ancestor('error_node') is not None: + return + if code in (901, 903): + # 901 and 903 are raised by the ErrorFinder. + super().add_issue(node, code, message) + else: + # Skip ErrorFinder here, because it has custom behavior. + super(ErrorFinder, self).add_issue(node, code, message) + + +class PEP8NormalizerConfig(ErrorFinderConfig): + normalizer_class = PEP8Normalizer + """ + Normalizing to PEP8. Not really implemented, yet. + """ + def __init__(self, indentation=' ' * 4, hanging_indentation=None, + max_characters=79, spaces_before_comment=2): + self.indentation = indentation + if hanging_indentation is None: + hanging_indentation = indentation + self.hanging_indentation = hanging_indentation + self.closing_bracket_hanging_indentation = '' + self.break_after_binary = False + self.max_characters = max_characters + self.spaces_before_comment = spaces_before_comment + + +# TODO this is not yet ready. +# @PEP8Normalizer.register_rule(type='endmarker') +class BlankLineAtEnd(Rule): + code = 392 + message = 'Blank line at end of file' + + def is_issue(self, leaf): + return self._newline_count >= 2 diff --git a/lib/python3.12/site-packages/parso/python/prefix.py b/lib/python3.12/site-packages/parso/python/prefix.py new file mode 100644 index 0000000000000000000000000000000000000000..6b8d59e69e37f971e7967585ed00c54735c6dc29 --- /dev/null +++ b/lib/python3.12/site-packages/parso/python/prefix.py @@ -0,0 +1,106 @@ +import re +from codecs import BOM_UTF8 +from typing import Tuple + +from parso.python.tokenize import group + +unicode_bom = BOM_UTF8.decode('utf-8') + + +class PrefixPart: + def __init__(self, leaf, typ, value, spacing='', start_pos=None): + assert start_pos is not None + self.parent = leaf + self.type = typ + self.value = value + self.spacing = spacing + self.start_pos: Tuple[int, int] = start_pos + + @property + def end_pos(self) -> Tuple[int, int]: + if self.value.endswith('\n') or self.value.endswith('\r'): + return self.start_pos[0] + 1, 0 + if self.value == unicode_bom: + # The bom doesn't have a length at the start of a Python file. + return self.start_pos + return self.start_pos[0], self.start_pos[1] + len(self.value) + + def create_spacing_part(self): + column = self.start_pos[1] - len(self.spacing) + return PrefixPart( + self.parent, 'spacing', self.spacing, + start_pos=(self.start_pos[0], column) + ) + + def __repr__(self): + return '%s(%s, %s, %s)' % ( + self.__class__.__name__, + self.type, + repr(self.value), + self.start_pos + ) + + def search_ancestor(self, *node_types): + node = self.parent + while node is not None: + if node.type in node_types: + return node + node = node.parent + return None + + +_comment = r'#[^\n\r\f]*' +_backslash = r'\\\r?\n|\\\r' +_newline = r'\r?\n|\r' +_form_feed = r'\f' +_only_spacing = '$' +_spacing = r'[ \t]*' +_bom = unicode_bom + +_regex = group( + _comment, _backslash, _newline, _form_feed, _only_spacing, _bom, + capture=True +) +_regex = re.compile(group(_spacing, capture=True) + _regex) + + +_types = { + '#': 'comment', + '\\': 'backslash', + '\f': 'formfeed', + '\n': 'newline', + '\r': 'newline', + unicode_bom: 'bom' +} + + +def split_prefix(leaf, start_pos): + line, column = start_pos + start = 0 + value = spacing = '' + bom = False + while start != len(leaf.prefix): + match = _regex.match(leaf.prefix, start) + spacing = match.group(1) + value = match.group(2) + if not value: + break + type_ = _types[value[0]] + yield PrefixPart( + leaf, type_, value, spacing, + start_pos=(line, column + start - int(bom) + len(spacing)) + ) + if type_ == 'bom': + bom = True + + start = match.end(0) + if value.endswith('\n') or value.endswith('\r'): + line += 1 + column = -start + + if value: + spacing = '' + yield PrefixPart( + leaf, 'spacing', spacing, + start_pos=(line, column + start) + ) diff --git a/lib/python3.12/site-packages/parso/python/token.py b/lib/python3.12/site-packages/parso/python/token.py new file mode 100644 index 0000000000000000000000000000000000000000..9b6f4c7c1985ccadf073a1bbf9e01ac906f49558 --- /dev/null +++ b/lib/python3.12/site-packages/parso/python/token.py @@ -0,0 +1,31 @@ +from __future__ import absolute_import + +from enum import Enum + + +class TokenType: + name: str + contains_syntax: bool + + def __init__(self, name: str, contains_syntax: bool = False): + self.name = name + self.contains_syntax = contains_syntax + + def __repr__(self): + return '%s(%s)' % (self.__class__.__name__, self.name) + + +class PythonTokenTypes(Enum): + STRING = TokenType('STRING') + NUMBER = TokenType('NUMBER') + NAME = TokenType('NAME', contains_syntax=True) + ERRORTOKEN = TokenType('ERRORTOKEN') + NEWLINE = TokenType('NEWLINE') + INDENT = TokenType('INDENT') + DEDENT = TokenType('DEDENT') + ERROR_DEDENT = TokenType('ERROR_DEDENT') + FSTRING_STRING = TokenType('FSTRING_STRING') + FSTRING_START = TokenType('FSTRING_START') + FSTRING_END = TokenType('FSTRING_END') + OP = TokenType('OP', contains_syntax=True) + ENDMARKER = TokenType('ENDMARKER') diff --git a/lib/python3.12/site-packages/parso/python/tokenize.py b/lib/python3.12/site-packages/parso/python/tokenize.py new file mode 100644 index 0000000000000000000000000000000000000000..e3ffe440b9ebd11e3fa17ed863eb933a33ec5b17 --- /dev/null +++ b/lib/python3.12/site-packages/parso/python/tokenize.py @@ -0,0 +1,687 @@ +# -*- coding: utf-8 -*- +""" +This tokenizer has been copied from the ``tokenize.py`` standard library +tokenizer. The reason was simple: The standard library tokenizer fails +if the indentation is not right. To make it possible to do error recovery the + tokenizer needed to be rewritten. + +Basically this is a stripped down version of the standard library module, so +you can read the documentation there. Additionally we included some speed and +memory optimizations here. +""" +from __future__ import absolute_import + +import sys +import re +import itertools as _itertools +from codecs import BOM_UTF8 +from typing import NamedTuple, Tuple, Iterator, Iterable, List, Dict, \ + Pattern, Set + +from parso.python.token import PythonTokenTypes +from parso.utils import split_lines, PythonVersionInfo, parse_version_string + + +# Maximum code point of Unicode 6.0: 0x10ffff (1,114,111) +MAX_UNICODE = '\U0010ffff' + +STRING = PythonTokenTypes.STRING +NAME = PythonTokenTypes.NAME +NUMBER = PythonTokenTypes.NUMBER +OP = PythonTokenTypes.OP +NEWLINE = PythonTokenTypes.NEWLINE +INDENT = PythonTokenTypes.INDENT +DEDENT = PythonTokenTypes.DEDENT +ENDMARKER = PythonTokenTypes.ENDMARKER +ERRORTOKEN = PythonTokenTypes.ERRORTOKEN +ERROR_DEDENT = PythonTokenTypes.ERROR_DEDENT +FSTRING_START = PythonTokenTypes.FSTRING_START +FSTRING_STRING = PythonTokenTypes.FSTRING_STRING +FSTRING_END = PythonTokenTypes.FSTRING_END + + +class TokenCollection(NamedTuple): + pseudo_token: Pattern + single_quoted: Set[str] + triple_quoted: Set[str] + endpats: Dict[str, Pattern] + whitespace: Pattern + fstring_pattern_map: Dict[str, str] + always_break_tokens: Tuple[str] + + +BOM_UTF8_STRING = BOM_UTF8.decode('utf-8') + +_token_collection_cache: Dict[PythonVersionInfo, TokenCollection] = {} + + +def group(*choices, capture=False, **kwargs): + assert not kwargs + + start = '(' + if not capture: + start += '?:' + return start + '|'.join(choices) + ')' + + +def maybe(*choices): + return group(*choices) + '?' + + +# Return the empty string, plus all of the valid string prefixes. +def _all_string_prefixes(*, include_fstring=False, only_fstring=False): + def different_case_versions(prefix): + for s in _itertools.product(*[(c, c.upper()) for c in prefix]): + yield ''.join(s) + # The valid string prefixes. Only contain the lower case versions, + # and don't contain any permuations (include 'fr', but not + # 'rf'). The various permutations will be generated. + valid_string_prefixes = ['b', 'r', 'u', 'br'] + + result = {''} + if include_fstring: + f = ['f', 'fr'] + if only_fstring: + valid_string_prefixes = f + result = set() + else: + valid_string_prefixes += f + elif only_fstring: + return set() + + # if we add binary f-strings, add: ['fb', 'fbr'] + for prefix in valid_string_prefixes: + for t in _itertools.permutations(prefix): + # create a list with upper and lower versions of each + # character + result.update(different_case_versions(t)) + return result + + +def _compile(expr): + return re.compile(expr, re.UNICODE) + + +def _get_token_collection(version_info): + try: + return _token_collection_cache[tuple(version_info)] + except KeyError: + _token_collection_cache[tuple(version_info)] = result = \ + _create_token_collection(version_info) + return result + + +unicode_character_name = r'[A-Za-z0-9\-]+(?: [A-Za-z0-9\-]+)*' +fstring_string_single_line = _compile( + r'(?:\{\{|\}\}|\\N\{' + unicode_character_name + + r'\}|\\(?:\r\n?|\n)|\\[^\r\nN]|[^{}\r\n\\])+' +) +fstring_string_multi_line = _compile( + r'(?:\{\{|\}\}|\\N\{' + unicode_character_name + r'\}|\\[^N]|[^{}\\])+' +) +fstring_format_spec_single_line = _compile(r'(?:\\(?:\r\n?|\n)|[^{}\r\n])+') +fstring_format_spec_multi_line = _compile(r'[^{}]+') + + +def _create_token_collection(version_info): + # Note: we use unicode matching for names ("\w") but ascii matching for + # number literals. + Whitespace = r'[ \f\t]*' + whitespace = _compile(Whitespace) + Comment = r'#[^\r\n]*' + Name = '([A-Za-z_0-9\u0080-' + MAX_UNICODE + ']+)' + + Hexnumber = r'0[xX](?:_?[0-9a-fA-F])+' + Binnumber = r'0[bB](?:_?[01])+' + Octnumber = r'0[oO](?:_?[0-7])+' + Decnumber = r'(?:0(?:_?0)*|[1-9](?:_?[0-9])*)' + Intnumber = group(Hexnumber, Binnumber, Octnumber, Decnumber) + Exponent = r'[eE][-+]?[0-9](?:_?[0-9])*' + Pointfloat = group(r'[0-9](?:_?[0-9])*\.(?:[0-9](?:_?[0-9])*)?', + r'\.[0-9](?:_?[0-9])*') + maybe(Exponent) + Expfloat = r'[0-9](?:_?[0-9])*' + Exponent + Floatnumber = group(Pointfloat, Expfloat) + Imagnumber = group(r'[0-9](?:_?[0-9])*[jJ]', Floatnumber + r'[jJ]') + Number = group(Imagnumber, Floatnumber, Intnumber) + + # Note that since _all_string_prefixes includes the empty string, + # StringPrefix can be the empty string (making it optional). + possible_prefixes = _all_string_prefixes() + StringPrefix = group(*possible_prefixes) + StringPrefixWithF = group(*_all_string_prefixes(include_fstring=True)) + fstring_prefixes = _all_string_prefixes(include_fstring=True, only_fstring=True) + FStringStart = group(*fstring_prefixes) + + # Tail end of ' string. + Single = r"(?:\\.|[^'\\])*'" + # Tail end of " string. + Double = r'(?:\\.|[^"\\])*"' + # Tail end of ''' string. + Single3 = r"(?:\\.|'(?!'')|[^'\\])*'''" + # Tail end of """ string. + Double3 = r'(?:\\.|"(?!"")|[^"\\])*"""' + Triple = group(StringPrefixWithF + "'''", StringPrefixWithF + '"""') + + # Because of leftmost-then-longest match semantics, be sure to put the + # longest operators first (e.g., if = came before ==, == would get + # recognized as two instances of =). + Operator = group(r"\*\*=?", r">>=?", r"<<=?", + r"//=?", r"->", + r"[+\-*/%&@`|^!=<>]=?", + r"~") + + Bracket = '[][(){}]' + + special_args = [r'\.\.\.', r'\r\n?', r'\n', r'[;.,@]'] + if version_info >= (3, 8): + special_args.insert(0, ":=?") + else: + special_args.insert(0, ":") + Special = group(*special_args) + + Funny = group(Operator, Bracket, Special) + + # First (or only) line of ' or " string. + ContStr = group(StringPrefix + r"'[^\r\n'\\]*(?:\\.[^\r\n'\\]*)*" + + group("'", r'\\(?:\r\n?|\n)'), + StringPrefix + r'"[^\r\n"\\]*(?:\\.[^\r\n"\\]*)*' + + group('"', r'\\(?:\r\n?|\n)')) + pseudo_extra_pool = [Comment, Triple] + all_quotes = '"', "'", '"""', "'''" + if fstring_prefixes: + pseudo_extra_pool.append(FStringStart + group(*all_quotes)) + + PseudoExtras = group(r'\\(?:\r\n?|\n)|\Z', *pseudo_extra_pool) + PseudoToken = group(Whitespace, capture=True) + \ + group(PseudoExtras, Number, Funny, ContStr, Name, capture=True) + + # For a given string prefix plus quotes, endpats maps it to a regex + # to match the remainder of that string. _prefix can be empty, for + # a normal single or triple quoted string (with no prefix). + endpats = {} + for _prefix in possible_prefixes: + endpats[_prefix + "'"] = _compile(Single) + endpats[_prefix + '"'] = _compile(Double) + endpats[_prefix + "'''"] = _compile(Single3) + endpats[_prefix + '"""'] = _compile(Double3) + + # A set of all of the single and triple quoted string prefixes, + # including the opening quotes. + single_quoted = set() + triple_quoted = set() + fstring_pattern_map = {} + for t in possible_prefixes: + for quote in '"', "'": + single_quoted.add(t + quote) + + for quote in '"""', "'''": + triple_quoted.add(t + quote) + + for t in fstring_prefixes: + for quote in all_quotes: + fstring_pattern_map[t + quote] = quote + + ALWAYS_BREAK_TOKENS = (';', 'import', 'class', 'def', 'try', 'except', + 'finally', 'while', 'with', 'return', 'continue', + 'break', 'del', 'pass', 'global', 'assert', 'nonlocal') + pseudo_token_compiled = _compile(PseudoToken) + return TokenCollection( + pseudo_token_compiled, single_quoted, triple_quoted, endpats, + whitespace, fstring_pattern_map, set(ALWAYS_BREAK_TOKENS) + ) + + +class Token(NamedTuple): + type: PythonTokenTypes + string: str + start_pos: Tuple[int, int] + prefix: str + + @property + def end_pos(self) -> Tuple[int, int]: + lines = split_lines(self.string) + if len(lines) > 1: + return self.start_pos[0] + len(lines) - 1, 0 + else: + return self.start_pos[0], self.start_pos[1] + len(self.string) + + +class PythonToken(Token): + def __repr__(self): + return ('TokenInfo(type=%s, string=%r, start_pos=%r, prefix=%r)' % + self._replace(type=self.type.name)) + + +class FStringNode: + def __init__(self, quote): + self.quote = quote + self.parentheses_count = 0 + self.previous_lines = '' + self.last_string_start_pos = None + # In the syntax there can be multiple format_spec's nested: + # {x:{y:3}} + self.format_spec_count = 0 + + def open_parentheses(self, character): + self.parentheses_count += 1 + + def close_parentheses(self, character): + self.parentheses_count -= 1 + if self.parentheses_count == 0: + # No parentheses means that the format spec is also finished. + self.format_spec_count = 0 + + def allow_multiline(self): + return len(self.quote) == 3 + + def is_in_expr(self): + return self.parentheses_count > self.format_spec_count + + def is_in_format_spec(self): + return not self.is_in_expr() and self.format_spec_count + + +def _close_fstring_if_necessary(fstring_stack, string, line_nr, column, additional_prefix): + for fstring_stack_index, node in enumerate(fstring_stack): + lstripped_string = string.lstrip() + len_lstrip = len(string) - len(lstripped_string) + if lstripped_string.startswith(node.quote): + token = PythonToken( + FSTRING_END, + node.quote, + (line_nr, column + len_lstrip), + prefix=additional_prefix+string[:len_lstrip], + ) + additional_prefix = '' + assert not node.previous_lines + del fstring_stack[fstring_stack_index:] + return token, '', len(node.quote) + len_lstrip + return None, additional_prefix, 0 + + +def _find_fstring_string(endpats, fstring_stack, line, lnum, pos): + tos = fstring_stack[-1] + allow_multiline = tos.allow_multiline() + if tos.is_in_format_spec(): + if allow_multiline: + regex = fstring_format_spec_multi_line + else: + regex = fstring_format_spec_single_line + else: + if allow_multiline: + regex = fstring_string_multi_line + else: + regex = fstring_string_single_line + + match = regex.match(line, pos) + if match is None: + return tos.previous_lines, pos + + if not tos.previous_lines: + tos.last_string_start_pos = (lnum, pos) + + string = match.group(0) + for fstring_stack_node in fstring_stack: + end_match = endpats[fstring_stack_node.quote].match(string) + if end_match is not None: + string = end_match.group(0)[:-len(fstring_stack_node.quote)] + + new_pos = pos + new_pos += len(string) + # even if allow_multiline is False, we still need to check for trailing + # newlines, because a single-line f-string can contain line continuations + if string.endswith('\n') or string.endswith('\r'): + tos.previous_lines += string + string = '' + else: + string = tos.previous_lines + string + + return string, new_pos + + +def tokenize( + code: str, *, version_info: PythonVersionInfo, start_pos: Tuple[int, int] = (1, 0) +) -> Iterator[PythonToken]: + """Generate tokens from a the source code (string).""" + lines = split_lines(code, keepends=True) + return tokenize_lines(lines, version_info=version_info, start_pos=start_pos) + + +def _print_tokens(func): + """ + A small helper function to help debug the tokenize_lines function. + """ + def wrapper(*args, **kwargs): + for token in func(*args, **kwargs): + print(token) # This print is intentional for debugging! + yield token + + return wrapper + + +# @_print_tokens +def tokenize_lines( + lines: Iterable[str], + *, + version_info: PythonVersionInfo, + indents: List[int] = None, + start_pos: Tuple[int, int] = (1, 0), + is_first_token=True, +) -> Iterator[PythonToken]: + """ + A heavily modified Python standard library tokenizer. + + Additionally to the default information, yields also the prefix of each + token. This idea comes from lib2to3. The prefix contains all information + that is irrelevant for the parser like newlines in parentheses or comments. + """ + def dedent_if_necessary(start): + while start < indents[-1]: + if start > indents[-2]: + yield PythonToken(ERROR_DEDENT, '', (lnum, start), '') + indents[-1] = start + break + indents.pop() + yield PythonToken(DEDENT, '', spos, '') + + pseudo_token, single_quoted, triple_quoted, endpats, whitespace, \ + fstring_pattern_map, always_break_tokens, = \ + _get_token_collection(version_info) + paren_level = 0 # count parentheses + if indents is None: + indents = [0] + max_ = 0 + numchars = '0123456789' + contstr = '' + contline: str + contstr_start: Tuple[int, int] + endprog: Pattern + # We start with a newline. This makes indent at the first position + # possible. It's not valid Python, but still better than an INDENT in the + # second line (and not in the first). This makes quite a few things in + # Jedi's fast parser possible. + new_line = True + prefix = '' # Should never be required, but here for safety + additional_prefix = '' + lnum = start_pos[0] - 1 + fstring_stack: List[FStringNode] = [] + for line in lines: # loop over lines in stream + lnum += 1 + pos = 0 + max_ = len(line) + if is_first_token: + if line.startswith(BOM_UTF8_STRING): + additional_prefix = BOM_UTF8_STRING + line = line[1:] + max_ = len(line) + + # Fake that the part before was already parsed. + line = '^' * start_pos[1] + line + pos = start_pos[1] + max_ += start_pos[1] + + is_first_token = False + + if contstr: # continued string + endmatch = endprog.match(line) # noqa: F821 + if endmatch: + pos = endmatch.end(0) + yield PythonToken( + STRING, contstr + line[:pos], + contstr_start, prefix) # noqa: F821 + contstr = '' + contline = '' + else: + contstr = contstr + line + contline = contline + line + continue + + while pos < max_: + if fstring_stack: + tos = fstring_stack[-1] + if not tos.is_in_expr(): + string, pos = _find_fstring_string(endpats, fstring_stack, line, lnum, pos) + if string: + yield PythonToken( + FSTRING_STRING, string, + tos.last_string_start_pos, + # Never has a prefix because it can start anywhere and + # include whitespace. + prefix='' + ) + tos.previous_lines = '' + continue + if pos == max_: + break + + rest = line[pos:] + fstring_end_token, additional_prefix, quote_length = _close_fstring_if_necessary( + fstring_stack, + rest, + lnum, + pos, + additional_prefix, + ) + pos += quote_length + if fstring_end_token is not None: + yield fstring_end_token + continue + + # in an f-string, match until the end of the string + if fstring_stack: + string_line = line + for fstring_stack_node in fstring_stack: + quote = fstring_stack_node.quote + end_match = endpats[quote].match(line, pos) + if end_match is not None: + end_match_string = end_match.group(0) + if len(end_match_string) - len(quote) + pos < len(string_line): + string_line = line[:pos] + end_match_string[:-len(quote)] + pseudomatch = pseudo_token.match(string_line, pos) + else: + pseudomatch = pseudo_token.match(line, pos) + + if pseudomatch: + prefix = additional_prefix + pseudomatch.group(1) + additional_prefix = '' + start, pos = pseudomatch.span(2) + spos = (lnum, start) + token = pseudomatch.group(2) + if token == '': + assert prefix + additional_prefix = prefix + # This means that we have a line with whitespace/comments at + # the end, which just results in an endmarker. + break + initial = token[0] + else: + match = whitespace.match(line, pos) + initial = line[match.end()] + start = match.end() + spos = (lnum, start) + + if new_line and initial not in '\r\n#' and (initial != '\\' or pseudomatch is None): + new_line = False + if paren_level == 0 and not fstring_stack: + indent_start = start + if indent_start > indents[-1]: + yield PythonToken(INDENT, '', spos, '') + indents.append(indent_start) + yield from dedent_if_necessary(indent_start) + + if not pseudomatch: # scan for tokens + match = whitespace.match(line, pos) + if new_line and paren_level == 0 and not fstring_stack: + yield from dedent_if_necessary(match.end()) + pos = match.end() + new_line = False + yield PythonToken( + ERRORTOKEN, line[pos], (lnum, pos), + additional_prefix + match.group(0) + ) + additional_prefix = '' + pos += 1 + continue + + if (initial in numchars # ordinary number + or (initial == '.' and token != '.' and token != '...')): + yield PythonToken(NUMBER, token, spos, prefix) + elif pseudomatch.group(3) is not None: # ordinary name + if token in always_break_tokens and (fstring_stack or paren_level): + fstring_stack[:] = [] + paren_level = 0 + # We only want to dedent if the token is on a new line. + m = re.match(r'[ \f\t]*$', line[:start]) + if m is not None: + yield from dedent_if_necessary(m.end()) + if token.isidentifier(): + yield PythonToken(NAME, token, spos, prefix) + else: + yield from _split_illegal_unicode_name(token, spos, prefix) + elif initial in '\r\n': + if any(not f.allow_multiline() for f in fstring_stack): + fstring_stack.clear() + + if not new_line and paren_level == 0 and not fstring_stack: + yield PythonToken(NEWLINE, token, spos, prefix) + else: + additional_prefix = prefix + token + new_line = True + elif initial == '#': # Comments + assert not token.endswith("\n") and not token.endswith("\r") + if fstring_stack and fstring_stack[-1].is_in_expr(): + # `#` is not allowed in f-string expressions + yield PythonToken(ERRORTOKEN, initial, spos, prefix) + pos = start + 1 + else: + additional_prefix = prefix + token + elif token in triple_quoted: + endprog = endpats[token] + endmatch = endprog.match(line, pos) + if endmatch: # all on one line + pos = endmatch.end(0) + token = line[start:pos] + yield PythonToken(STRING, token, spos, prefix) + else: + contstr_start = spos # multiple lines + contstr = line[start:] + contline = line + break + + # Check up to the first 3 chars of the token to see if + # they're in the single_quoted set. If so, they start + # a string. + # We're using the first 3, because we're looking for + # "rb'" (for example) at the start of the token. If + # we switch to longer prefixes, this needs to be + # adjusted. + # Note that initial == token[:1]. + # Also note that single quote checking must come after + # triple quote checking (above). + elif initial in single_quoted or \ + token[:2] in single_quoted or \ + token[:3] in single_quoted: + if token[-1] in '\r\n': # continued string + # This means that a single quoted string ends with a + # backslash and is continued. + contstr_start = lnum, start + endprog = (endpats.get(initial) or endpats.get(token[1]) + or endpats.get(token[2])) + contstr = line[start:] + contline = line + break + else: # ordinary string + yield PythonToken(STRING, token, spos, prefix) + elif token in fstring_pattern_map: # The start of an fstring. + fstring_stack.append(FStringNode(fstring_pattern_map[token])) + yield PythonToken(FSTRING_START, token, spos, prefix) + elif initial == '\\' and line[start:] in ('\\\n', '\\\r\n', '\\\r'): # continued stmt + additional_prefix += prefix + line[start:] + break + else: + if token in '([{': + if fstring_stack: + fstring_stack[-1].open_parentheses(token) + else: + paren_level += 1 + elif token in ')]}': + if fstring_stack: + fstring_stack[-1].close_parentheses(token) + else: + if paren_level: + paren_level -= 1 + elif token.startswith(':') and fstring_stack \ + and fstring_stack[-1].parentheses_count \ + - fstring_stack[-1].format_spec_count == 1: + # `:` and `:=` both count + fstring_stack[-1].format_spec_count += 1 + token = ':' + pos = start + 1 + + yield PythonToken(OP, token, spos, prefix) + + if contstr: + yield PythonToken(ERRORTOKEN, contstr, contstr_start, prefix) + if contstr.endswith('\n') or contstr.endswith('\r'): + new_line = True + + if fstring_stack: + tos = fstring_stack[-1] + if tos.previous_lines: + yield PythonToken( + FSTRING_STRING, tos.previous_lines, + tos.last_string_start_pos, + # Never has a prefix because it can start anywhere and + # include whitespace. + prefix='' + ) + + end_pos = lnum, max_ + # As the last position we just take the maximally possible position. We + # remove -1 for the last new line. + for indent in indents[1:]: + indents.pop() + yield PythonToken(DEDENT, '', end_pos, '') + yield PythonToken(ENDMARKER, '', end_pos, additional_prefix) + + +def _split_illegal_unicode_name(token, start_pos, prefix): + def create_token(): + return PythonToken(ERRORTOKEN if is_illegal else NAME, found, pos, prefix) + + found = '' + is_illegal = False + pos = start_pos + for i, char in enumerate(token): + if is_illegal: + if char.isidentifier(): + yield create_token() + found = char + is_illegal = False + prefix = '' + pos = start_pos[0], start_pos[1] + i + else: + found += char + else: + new_found = found + char + if new_found.isidentifier(): + found = new_found + else: + if found: + yield create_token() + prefix = '' + pos = start_pos[0], start_pos[1] + i + found = char + is_illegal = True + + if found: + yield create_token() + + +if __name__ == "__main__": + path = sys.argv[1] + with open(path) as f: + code = f.read() + + for token in tokenize(code, version_info=parse_version_string('3.10')): + print(token) diff --git a/lib/python3.12/site-packages/parso/python/tree.py b/lib/python3.12/site-packages/parso/python/tree.py new file mode 100644 index 0000000000000000000000000000000000000000..0624e6755d6608bb1220be0f5382f4e38c4e5073 --- /dev/null +++ b/lib/python3.12/site-packages/parso/python/tree.py @@ -0,0 +1,1245 @@ +""" +This is the syntax tree for Python 3 syntaxes. The classes represent +syntax elements like functions and imports. + +All of the nodes can be traced back to the `Python grammar file +`_. If you want to know how +a tree is structured, just analyse that file (for each Python version it's a +bit different). + +There's a lot of logic here that makes it easier for Jedi (and other libraries) +to deal with a Python syntax tree. + +By using :py:meth:`parso.tree.NodeOrLeaf.get_code` on a module, you can get +back the 1-to-1 representation of the input given to the parser. This is +important if you want to refactor a parser tree. + +>>> from parso import parse +>>> parser = parse('import os') +>>> module = parser.get_root_node() +>>> module + + +Any subclasses of :class:`Scope`, including :class:`Module` has an attribute +:attr:`iter_imports `: + +>>> list(module.iter_imports()) +[] + +Changes to the Python Grammar +----------------------------- + +A few things have changed when looking at Python grammar files: + +- :class:`Param` does not exist in Python grammar files. It is essentially a + part of a ``parameters`` node. |parso| splits it up to make it easier to + analyse parameters. However this just makes it easier to deal with the syntax + tree, it doesn't actually change the valid syntax. +- A few nodes like `lambdef` and `lambdef_nocond` have been merged in the + syntax tree to make it easier to do deal with them. + +Parser Tree Classes +------------------- +""" + +import re +try: + from collections.abc import Mapping +except ImportError: + from collections import Mapping +from typing import Tuple + +from parso.tree import Node, BaseNode, Leaf, ErrorNode, ErrorLeaf, search_ancestor # noqa +from parso.python.prefix import split_prefix +from parso.utils import split_lines + +_FLOW_CONTAINERS = set(['if_stmt', 'while_stmt', 'for_stmt', 'try_stmt', + 'with_stmt', 'async_stmt', 'suite']) +_RETURN_STMT_CONTAINERS = set(['suite', 'simple_stmt']) | _FLOW_CONTAINERS + +_FUNC_CONTAINERS = set( + ['suite', 'simple_stmt', 'decorated', 'async_funcdef'] +) | _FLOW_CONTAINERS + +_GET_DEFINITION_TYPES = set([ + 'expr_stmt', 'sync_comp_for', 'with_stmt', 'for_stmt', 'import_name', + 'import_from', 'param', 'del_stmt', 'namedexpr_test', +]) +_IMPORTS = set(['import_name', 'import_from']) + + +class DocstringMixin: + __slots__ = () + + def get_doc_node(self): + """ + Returns the string leaf of a docstring. e.g. ``r'''foo'''``. + """ + if self.type == 'file_input': + node = self.children[0] + elif self.type in ('funcdef', 'classdef'): + node = self.children[self.children.index(':') + 1] + if node.type == 'suite': # Normally a suite + node = node.children[1] # -> NEWLINE stmt + else: # ExprStmt + simple_stmt = self.parent + c = simple_stmt.parent.children + index = c.index(simple_stmt) + if not index: + return None + node = c[index - 1] + + if node.type == 'simple_stmt': + node = node.children[0] + if node.type == 'string': + return node + return None + + +class PythonMixin: + """ + Some Python specific utilities. + """ + __slots__ = () + + def get_name_of_position(self, position): + """ + Given a (line, column) tuple, returns a :py:class:`Name` or ``None`` if + there is no name at that position. + """ + for c in self.children: + if isinstance(c, Leaf): + if c.type == 'name' and c.start_pos <= position <= c.end_pos: + return c + else: + result = c.get_name_of_position(position) + if result is not None: + return result + return None + + +class PythonLeaf(PythonMixin, Leaf): + __slots__ = () + + def _split_prefix(self): + return split_prefix(self, self.get_start_pos_of_prefix()) + + def get_start_pos_of_prefix(self): + """ + Basically calls :py:meth:`parso.tree.NodeOrLeaf.get_start_pos_of_prefix`. + """ + # TODO it is really ugly that we have to override it. Maybe change + # indent error leafs somehow? No idea how, though. + previous_leaf = self.get_previous_leaf() + if previous_leaf is not None and previous_leaf.type == 'error_leaf' \ + and previous_leaf.token_type in ('INDENT', 'DEDENT', 'ERROR_DEDENT'): + previous_leaf = previous_leaf.get_previous_leaf() + + if previous_leaf is None: # It's the first leaf. + lines = split_lines(self.prefix) + # + 1 is needed because split_lines always returns at least ['']. + return self.line - len(lines) + 1, 0 # It's the first leaf. + return previous_leaf.end_pos + + +class _LeafWithoutNewlines(PythonLeaf): + """ + Simply here to optimize performance. + """ + __slots__ = () + + @property + def end_pos(self) -> Tuple[int, int]: + return self.line, self.column + len(self.value) + + +# Python base classes +class PythonBaseNode(PythonMixin, BaseNode): + __slots__ = () + + +class PythonNode(PythonMixin, Node): + __slots__ = () + + +class PythonErrorNode(PythonMixin, ErrorNode): + __slots__ = () + + +class PythonErrorLeaf(ErrorLeaf, PythonLeaf): + __slots__ = () + + +class EndMarker(_LeafWithoutNewlines): + __slots__ = () + type = 'endmarker' + + def __repr__(self): + return "<%s: prefix=%s end_pos=%s>" % ( + type(self).__name__, repr(self.prefix), self.end_pos + ) + + +class Newline(PythonLeaf): + """Contains NEWLINE and ENDMARKER tokens.""" + __slots__ = () + type = 'newline' + + def __repr__(self): + return "<%s: %s>" % (type(self).__name__, repr(self.value)) + + +class Name(_LeafWithoutNewlines): + """ + A string. Sometimes it is important to know if the string belongs to a name + or not. + """ + type = 'name' + __slots__ = () + + def __repr__(self): + return "<%s: %s@%s,%s>" % (type(self).__name__, self.value, + self.line, self.column) + + def is_definition(self, include_setitem=False): + """ + Returns True if the name is being defined. + """ + return self.get_definition(include_setitem=include_setitem) is not None + + def get_definition(self, import_name_always=False, include_setitem=False): + """ + Returns None if there's no definition for a name. + + :param import_name_always: Specifies if an import name is always a + definition. Normally foo in `from foo import bar` is not a + definition. + """ + node = self.parent + type_ = node.type + + if type_ in ('funcdef', 'classdef'): + if self == node.name: + return node + return None + + if type_ == 'except_clause': + if self.get_previous_sibling() == 'as': + return node.parent # The try_stmt. + return None + + while node is not None: + if node.type == 'suite': + return None + if node.type in _GET_DEFINITION_TYPES: + if self in node.get_defined_names(include_setitem): + return node + if import_name_always and node.type in _IMPORTS: + return node + return None + node = node.parent + return None + + +class Literal(PythonLeaf): + __slots__ = () + + +class Number(Literal): + type = 'number' + __slots__ = () + + +class String(Literal): + type = 'string' + __slots__ = () + + @property + def string_prefix(self): + return re.match(r'\w*(?=[\'"])', self.value).group(0) + + def _get_payload(self): + match = re.search( + r'''('{3}|"{3}|'|")(.*)$''', + self.value, + flags=re.DOTALL + ) + return match.group(2)[:-len(match.group(1))] + + +class FStringString(PythonLeaf): + """ + f-strings contain f-string expressions and normal python strings. These are + the string parts of f-strings. + """ + type = 'fstring_string' + __slots__ = () + + +class FStringStart(PythonLeaf): + """ + f-strings contain f-string expressions and normal python strings. These are + the string parts of f-strings. + """ + type = 'fstring_start' + __slots__ = () + + +class FStringEnd(PythonLeaf): + """ + f-strings contain f-string expressions and normal python strings. These are + the string parts of f-strings. + """ + type = 'fstring_end' + __slots__ = () + + +class _StringComparisonMixin: + __slots__ = () + + def __eq__(self, other): + """ + Make comparisons with strings easy. + Improves the readability of the parser. + """ + if isinstance(other, str): + return self.value == other + + return self is other + + def __hash__(self): + return hash(self.value) + + +class Operator(_LeafWithoutNewlines, _StringComparisonMixin): + type = 'operator' + __slots__ = () + + +class Keyword(_LeafWithoutNewlines, _StringComparisonMixin): + type = 'keyword' + __slots__ = () + + +class Scope(PythonBaseNode, DocstringMixin): + """ + Super class for the parser tree, which represents the state of a python + text file. + A Scope is either a function, class or lambda. + """ + __slots__ = () + + def __init__(self, children): + super().__init__(children) + + def iter_funcdefs(self): + """ + Returns a generator of `funcdef` nodes. + """ + return self._search_in_scope('funcdef') + + def iter_classdefs(self): + """ + Returns a generator of `classdef` nodes. + """ + return self._search_in_scope('classdef') + + def iter_imports(self): + """ + Returns a generator of `import_name` and `import_from` nodes. + """ + return self._search_in_scope('import_name', 'import_from') + + def _search_in_scope(self, *names): + def scan(children): + for element in children: + if element.type in names: + yield element + if element.type in _FUNC_CONTAINERS: + yield from scan(element.children) + + return scan(self.children) + + def get_suite(self): + """ + Returns the part that is executed by the function. + """ + return self.children[-1] + + def __repr__(self): + try: + name = self.name.value + except AttributeError: + name = '' + + return "<%s: %s@%s-%s>" % (type(self).__name__, name, + self.start_pos[0], self.end_pos[0]) + + +class Module(Scope): + """ + The top scope, which is always a module. + Depending on the underlying parser this may be a full module or just a part + of a module. + """ + __slots__ = ('_used_names',) + type = 'file_input' + + def __init__(self, children): + super().__init__(children) + self._used_names = None + + def _iter_future_import_names(self): + """ + :return: A list of future import names. + :rtype: list of str + """ + # In Python it's not allowed to use future imports after the first + # actual (non-future) statement. However this is not a linter here, + # just return all future imports. If people want to scan for issues + # they should use the API. + for imp in self.iter_imports(): + if imp.type == 'import_from' and imp.level == 0: + for path in imp.get_paths(): + names = [name.value for name in path] + if len(names) == 2 and names[0] == '__future__': + yield names[1] + + def get_used_names(self): + """ + Returns all the :class:`Name` leafs that exist in this module. This + includes both definitions and references of names. + """ + if self._used_names is None: + # Don't directly use self._used_names to eliminate a lookup. + dct = {} + + def recurse(node): + try: + children = node.children + except AttributeError: + if node.type == 'name': + arr = dct.setdefault(node.value, []) + arr.append(node) + else: + for child in children: + recurse(child) + + recurse(self) + self._used_names = UsedNamesMapping(dct) + return self._used_names + + +class Decorator(PythonBaseNode): + type = 'decorator' + __slots__ = () + + +class ClassOrFunc(Scope): + __slots__ = () + + @property + def name(self): + """ + Returns the `Name` leaf that defines the function or class name. + """ + return self.children[1] + + def get_decorators(self): + """ + :rtype: list of :class:`Decorator` + """ + decorated = self.parent + if decorated.type == 'async_funcdef': + decorated = decorated.parent + + if decorated.type == 'decorated': + if decorated.children[0].type == 'decorators': + return decorated.children[0].children + else: + return decorated.children[:1] + else: + return [] + + +class Class(ClassOrFunc): + """ + Used to store the parsed contents of a python class. + """ + type = 'classdef' + __slots__ = () + + def __init__(self, children): + super().__init__(children) + + def get_super_arglist(self): + """ + Returns the `arglist` node that defines the super classes. It returns + None if there are no arguments. + """ + if self.children[2] != '(': # Has no parentheses + return None + else: + if self.children[3] == ')': # Empty parentheses + return None + else: + return self.children[3] + + +def _create_params(parent, argslist_list): + """ + `argslist_list` is a list that can contain an argslist as a first item, but + most not. It's basically the items between the parameter brackets (which is + at most one item). + This function modifies the parser structure. It generates `Param` objects + from the normal ast. Those param objects do not exist in a normal ast, but + make the evaluation of the ast tree so much easier. + You could also say that this function replaces the argslist node with a + list of Param objects. + """ + try: + first = argslist_list[0] + except IndexError: + return [] + + if first.type in ('name', 'fpdef'): + return [Param([first], parent)] + elif first == '*': + return [first] + else: # argslist is a `typedargslist` or a `varargslist`. + if first.type == 'tfpdef': + children = [first] + else: + children = first.children + new_children = [] + start = 0 + # Start with offset 1, because the end is higher. + for end, child in enumerate(children + [None], 1): + if child is None or child == ',': + param_children = children[start:end] + if param_children: # Could as well be comma and then end. + if param_children[0] == '*' \ + and (len(param_children) == 1 + or param_children[1] == ',') \ + or param_children[0] == '/': + for p in param_children: + p.parent = parent + new_children += param_children + else: + new_children.append(Param(param_children, parent)) + start = end + return new_children + + +class Function(ClassOrFunc): + """ + Used to store the parsed contents of a python function. + + Children:: + + 0. + 1. + 2. parameter list (including open-paren and close-paren s) + 3. or 5. + 4. or 6. Node() representing function body + 3. -> (if annotation is also present) + 4. annotation (if present) + """ + type = 'funcdef' + __slots__ = () + + def __init__(self, children): + super().__init__(children) + parameters = self.children[2] # After `def foo` + parameters_children = parameters.children[1:-1] + # If input parameters list already has Param objects, keep it as is; + # otherwise, convert it to a list of Param objects. + if not any(isinstance(child, Param) for child in parameters_children): + parameters.children[1:-1] = _create_params(parameters, parameters_children) + + def _get_param_nodes(self): + return self.children[2].children + + def get_params(self): + """ + Returns a list of `Param()`. + """ + return [p for p in self._get_param_nodes() if p.type == 'param'] + + @property + def name(self): + return self.children[1] # First token after `def` + + def iter_yield_exprs(self): + """ + Returns a generator of `yield_expr`. + """ + def scan(children): + for element in children: + if element.type in ('classdef', 'funcdef', 'lambdef'): + continue + + try: + nested_children = element.children + except AttributeError: + if element.value == 'yield': + if element.parent.type == 'yield_expr': + yield element.parent + else: + yield element + else: + yield from scan(nested_children) + + return scan(self.children) + + def iter_return_stmts(self): + """ + Returns a generator of `return_stmt`. + """ + def scan(children): + for element in children: + if element.type == 'return_stmt' \ + or element.type == 'keyword' and element.value == 'return': + yield element + if element.type in _RETURN_STMT_CONTAINERS: + yield from scan(element.children) + + return scan(self.children) + + def iter_raise_stmts(self): + """ + Returns a generator of `raise_stmt`. Includes raise statements inside try-except blocks + """ + def scan(children): + for element in children: + if element.type == 'raise_stmt' \ + or element.type == 'keyword' and element.value == 'raise': + yield element + if element.type in _RETURN_STMT_CONTAINERS: + yield from scan(element.children) + + return scan(self.children) + + def is_generator(self): + """ + :return bool: Checks if a function is a generator or not. + """ + return next(self.iter_yield_exprs(), None) is not None + + @property + def annotation(self): + """ + Returns the test node after `->` or `None` if there is no annotation. + """ + try: + if self.children[3] == "->": + return self.children[4] + assert self.children[3] == ":" + return None + except IndexError: + return None + + +class Lambda(Function): + """ + Lambdas are basically trimmed functions, so give it the same interface. + + Children:: + + 0. + *. for each argument x + -2. + -1. Node() representing body + """ + type = 'lambdef' + __slots__ = () + + def __init__(self, children): + # We don't want to call the Function constructor, call its parent. + super(Function, self).__init__(children) + # Everything between `lambda` and the `:` operator is a parameter. + parameters_children = self.children[1:-2] + # If input children list already has Param objects, keep it as is; + # otherwise, convert it to a list of Param objects. + if not any(isinstance(child, Param) for child in parameters_children): + self.children[1:-2] = _create_params(self, parameters_children) + + @property + def name(self): + """ + Raises an AttributeError. Lambdas don't have a defined name. + """ + raise AttributeError("lambda is not named.") + + def _get_param_nodes(self): + return self.children[1:-2] + + @property + def annotation(self): + """ + Returns `None`, lambdas don't have annotations. + """ + return None + + def __repr__(self): + return "<%s@%s>" % (self.__class__.__name__, self.start_pos) + + +class Flow(PythonBaseNode): + __slots__ = () + + +class IfStmt(Flow): + type = 'if_stmt' + __slots__ = () + + def get_test_nodes(self): + """ + E.g. returns all the `test` nodes that are named as x, below: + + if x: + pass + elif x: + pass + """ + for i, c in enumerate(self.children): + if c in ('elif', 'if'): + yield self.children[i + 1] + + def get_corresponding_test_node(self, node): + """ + Searches for the branch in which the node is and returns the + corresponding test node (see function above). However if the node is in + the test node itself and not in the suite return None. + """ + start_pos = node.start_pos + for check_node in reversed(list(self.get_test_nodes())): + if check_node.start_pos < start_pos: + if start_pos < check_node.end_pos: + return None + # In this case the node is within the check_node itself, + # not in the suite + else: + return check_node + + def is_node_after_else(self, node): + """ + Checks if a node is defined after `else`. + """ + for c in self.children: + if c == 'else': + if node.start_pos > c.start_pos: + return True + else: + return False + + +class WhileStmt(Flow): + type = 'while_stmt' + __slots__ = () + + +class ForStmt(Flow): + type = 'for_stmt' + __slots__ = () + + def get_testlist(self): + """ + Returns the input node ``y`` from: ``for x in y:``. + """ + return self.children[3] + + def get_defined_names(self, include_setitem=False): + return _defined_names(self.children[1], include_setitem) + + +class TryStmt(Flow): + type = 'try_stmt' + __slots__ = () + + def get_except_clause_tests(self): + """ + Returns the ``test`` nodes found in ``except_clause`` nodes. + Returns ``[None]`` for except clauses without an exception given. + """ + for node in self.children: + if node.type == 'except_clause': + yield node.children[1] + elif node == 'except': + yield None + + +class WithStmt(Flow): + type = 'with_stmt' + __slots__ = () + + def get_defined_names(self, include_setitem=False): + """ + Returns the a list of `Name` that the with statement defines. The + defined names are set after `as`. + """ + names = [] + for with_item in self.children[1:-2:2]: + # Check with items for 'as' names. + if with_item.type == 'with_item': + names += _defined_names(with_item.children[2], include_setitem) + return names + + def get_test_node_from_name(self, name): + node = name.search_ancestor("with_item") + if node is None: + raise ValueError('The name is not actually part of a with statement.') + return node.children[0] + + +class Import(PythonBaseNode): + __slots__ = () + + def get_path_for_name(self, name): + """ + The path is the list of names that leads to the searched name. + + :return list of Name: + """ + try: + # The name may be an alias. If it is, just map it back to the name. + name = self._aliases()[name] + except KeyError: + pass + + for path in self.get_paths(): + if name in path: + return path[:path.index(name) + 1] + raise ValueError('Name should be defined in the import itself') + + def is_nested(self): + return False # By default, sub classes may overwrite this behavior + + def is_star_import(self): + return self.children[-1] == '*' + + +class ImportFrom(Import): + type = 'import_from' + __slots__ = () + + def get_defined_names(self, include_setitem=False): + """ + Returns the a list of `Name` that the import defines. The + defined names are set after `import` or in case an alias - `as` - is + present that name is returned. + """ + return [alias or name for name, alias in self._as_name_tuples()] + + def _aliases(self): + """Mapping from alias to its corresponding name.""" + return dict((alias, name) for name, alias in self._as_name_tuples() + if alias is not None) + + def get_from_names(self): + for n in self.children[1:]: + if n not in ('.', '...'): + break + if n.type == 'dotted_name': # from x.y import + return n.children[::2] + elif n == 'import': # from . import + return [] + else: # from x import + return [n] + + @property + def level(self): + """The level parameter of ``__import__``.""" + level = 0 + for n in self.children[1:]: + if n in ('.', '...'): + level += len(n.value) + else: + break + return level + + def _as_name_tuples(self): + last = self.children[-1] + if last == ')': + last = self.children[-2] + elif last == '*': + return # No names defined directly. + + if last.type == 'import_as_names': + as_names = last.children[::2] + else: + as_names = [last] + for as_name in as_names: + if as_name.type == 'name': + yield as_name, None + else: + yield as_name.children[::2] # yields x, y -> ``x as y`` + + def get_paths(self): + """ + The import paths defined in an import statement. Typically an array + like this: ``[, ]``. + + :return list of list of Name: + """ + dotted = self.get_from_names() + + if self.children[-1] == '*': + return [dotted] + return [dotted + [name] for name, alias in self._as_name_tuples()] + + +class ImportName(Import): + """For ``import_name`` nodes. Covers normal imports without ``from``.""" + type = 'import_name' + __slots__ = () + + def get_defined_names(self, include_setitem=False): + """ + Returns the a list of `Name` that the import defines. The defined names + is always the first name after `import` or in case an alias - `as` - is + present that name is returned. + """ + return [alias or path[0] for path, alias in self._dotted_as_names()] + + @property + def level(self): + """The level parameter of ``__import__``.""" + return 0 # Obviously 0 for imports without from. + + def get_paths(self): + return [path for path, alias in self._dotted_as_names()] + + def _dotted_as_names(self): + """Generator of (list(path), alias) where alias may be None.""" + dotted_as_names = self.children[1] + if dotted_as_names.type == 'dotted_as_names': + as_names = dotted_as_names.children[::2] + else: + as_names = [dotted_as_names] + + for as_name in as_names: + if as_name.type == 'dotted_as_name': + alias = as_name.children[2] + as_name = as_name.children[0] + else: + alias = None + if as_name.type == 'name': + yield [as_name], alias + else: + # dotted_names + yield as_name.children[::2], alias + + def is_nested(self): + """ + This checks for the special case of nested imports, without aliases and + from statement:: + + import foo.bar + """ + return bool([1 for path, alias in self._dotted_as_names() + if alias is None and len(path) > 1]) + + def _aliases(self): + """ + :return list of Name: Returns all the alias + """ + return dict((alias, path[-1]) for path, alias in self._dotted_as_names() + if alias is not None) + + +class KeywordStatement(PythonBaseNode): + """ + For the following statements: `assert`, `del`, `global`, `nonlocal`, + `raise`, `return`, `yield`. + + `pass`, `continue` and `break` are not in there, because they are just + simple keywords and the parser reduces it to a keyword. + """ + __slots__ = () + + @property + def type(self): + """ + Keyword statements start with the keyword and end with `_stmt`. You can + crosscheck this with the Python grammar. + """ + return '%s_stmt' % self.keyword + + @property + def keyword(self): + return self.children[0].value + + def get_defined_names(self, include_setitem=False): + keyword = self.keyword + if keyword == 'del': + return _defined_names(self.children[1], include_setitem) + if keyword in ('global', 'nonlocal'): + return self.children[1::2] + return [] + + +class AssertStmt(KeywordStatement): + __slots__ = () + + @property + def assertion(self): + return self.children[1] + + +class GlobalStmt(KeywordStatement): + __slots__ = () + + def get_global_names(self): + return self.children[1::2] + + +class ReturnStmt(KeywordStatement): + __slots__ = () + + +class YieldExpr(PythonBaseNode): + type = 'yield_expr' + __slots__ = () + + +def _defined_names(current, include_setitem): + """ + A helper function to find the defined names in statements, for loops and + list comprehensions. + """ + names = [] + if current.type in ('testlist_star_expr', 'testlist_comp', 'exprlist', 'testlist'): + for child in current.children[::2]: + names += _defined_names(child, include_setitem) + elif current.type in ('atom', 'star_expr'): + names += _defined_names(current.children[1], include_setitem) + elif current.type in ('power', 'atom_expr'): + if current.children[-2] != '**': # Just if there's no operation + trailer = current.children[-1] + if trailer.children[0] == '.': + names.append(trailer.children[1]) + elif trailer.children[0] == '[' and include_setitem: + for node in current.children[-2::-1]: + if node.type == 'trailer': + names.append(node.children[1]) + break + if node.type == 'name': + names.append(node) + break + else: + names.append(current) + return names + + +class ExprStmt(PythonBaseNode, DocstringMixin): + type = 'expr_stmt' + __slots__ = () + + def get_defined_names(self, include_setitem=False): + """ + Returns a list of `Name` defined before the `=` sign. + """ + names = [] + if self.children[1].type == 'annassign': + names = _defined_names(self.children[0], include_setitem) + return [ + name + for i in range(0, len(self.children) - 2, 2) + if '=' in self.children[i + 1].value + for name in _defined_names(self.children[i], include_setitem) + ] + names + + def get_rhs(self): + """Returns the right-hand-side of the equals.""" + node = self.children[-1] + if node.type == 'annassign': + if len(node.children) == 4: + node = node.children[3] + else: + node = node.children[1] + return node + + def yield_operators(self): + """ + Returns a generator of `+=`, `=`, etc. or None if there is no operation. + """ + first = self.children[1] + if first.type == 'annassign': + if len(first.children) <= 2: + return # No operator is available, it's just PEP 484. + + first = first.children[2] + yield first + + yield from self.children[3::2] + + +class NamedExpr(PythonBaseNode): + type = 'namedexpr_test' + + def get_defined_names(self, include_setitem=False): + return _defined_names(self.children[0], include_setitem) + + +class Param(PythonBaseNode): + """ + It's a helper class that makes business logic with params much easier. The + Python grammar defines no ``param`` node. It defines it in a different way + that is not really suited to working with parameters. + """ + type = 'param' + + def __init__(self, children, parent=None): + super().__init__(children) + self.parent = parent + + @property + def star_count(self): + """ + Is `0` in case of `foo`, `1` in case of `*foo` or `2` in case of + `**foo`. + """ + first = self.children[0] + if first in ('*', '**'): + return len(first.value) + return 0 + + @property + def default(self): + """ + The default is the test node that appears after the `=`. Is `None` in + case no default is present. + """ + has_comma = self.children[-1] == ',' + try: + if self.children[-2 - int(has_comma)] == '=': + return self.children[-1 - int(has_comma)] + except IndexError: + return None + + @property + def annotation(self): + """ + The default is the test node that appears after `:`. Is `None` in case + no annotation is present. + """ + tfpdef = self._tfpdef() + if tfpdef.type == 'tfpdef': + assert tfpdef.children[1] == ":" + assert len(tfpdef.children) == 3 + annotation = tfpdef.children[2] + return annotation + else: + return None + + def _tfpdef(self): + """ + tfpdef: see e.g. grammar36.txt. + """ + offset = int(self.children[0] in ('*', '**')) + return self.children[offset] + + @property + def name(self): + """ + The `Name` leaf of the param. + """ + if self._tfpdef().type == 'tfpdef': + return self._tfpdef().children[0] + else: + return self._tfpdef() + + def get_defined_names(self, include_setitem=False): + return [self.name] + + @property + def position_index(self): + """ + Property for the positional index of a paramter. + """ + index = self.parent.children.index(self) + try: + keyword_only_index = self.parent.children.index('*') + if index > keyword_only_index: + # Skip the ` *, ` + index -= 2 + except ValueError: + pass + try: + keyword_only_index = self.parent.children.index('/') + if index > keyword_only_index: + # Skip the ` /, ` + index -= 2 + except ValueError: + pass + return index - 1 + + def get_parent_function(self): + """ + Returns the function/lambda of a parameter. + """ + return self.search_ancestor('funcdef', 'lambdef') + + def get_code(self, include_prefix=True, include_comma=True): + """ + Like all the other get_code functions, but includes the param + `include_comma`. + + :param include_comma bool: If enabled includes the comma in the string output. + """ + if include_comma: + return super().get_code(include_prefix) + + children = self.children + if children[-1] == ',': + children = children[:-1] + return self._get_code_for_children( + children, + include_prefix=include_prefix + ) + + def __repr__(self): + default = '' if self.default is None else '=%s' % self.default.get_code() + return '<%s: %s>' % (type(self).__name__, str(self._tfpdef()) + default) + + +class SyncCompFor(PythonBaseNode): + type = 'sync_comp_for' + __slots__ = () + + def get_defined_names(self, include_setitem=False): + """ + Returns the a list of `Name` that the comprehension defines. + """ + # allow async for + return _defined_names(self.children[1], include_setitem) + + +# This is simply here so an older Jedi version can work with this new parso +# version. Can be deleted in the next release. +CompFor = SyncCompFor + + +class UsedNamesMapping(Mapping): + """ + This class exists for the sole purpose of creating an immutable dict. + """ + def __init__(self, dct): + self._dict = dct + + def __getitem__(self, key): + return self._dict[key] + + def __len__(self): + return len(self._dict) + + def __iter__(self): + return iter(self._dict) + + def __hash__(self): + return id(self) + + def __eq__(self, other): + # Comparing these dicts does not make sense. + return self is other diff --git a/lib/python3.12/site-packages/parso/tree.py b/lib/python3.12/site-packages/parso/tree.py new file mode 100644 index 0000000000000000000000000000000000000000..e5298711ead793683b7054190819a6d58167ef41 --- /dev/null +++ b/lib/python3.12/site-packages/parso/tree.py @@ -0,0 +1,488 @@ +from abc import abstractmethod, abstractproperty +from typing import List, Optional, Tuple, Union + +from parso.utils import split_lines + + +def search_ancestor(node: 'NodeOrLeaf', *node_types: str) -> 'Optional[BaseNode]': + """ + Recursively looks at the parents of a node and returns the first found node + that matches ``node_types``. Returns ``None`` if no matching node is found. + + This function is deprecated, use :meth:`NodeOrLeaf.search_ancestor` instead. + + :param node: The ancestors of this node will be checked. + :param node_types: type names that are searched for. + """ + n = node.parent + while n is not None: + if n.type in node_types: + return n + n = n.parent + return None + + +class NodeOrLeaf: + """ + The base class for nodes and leaves. + """ + __slots__ = ('parent',) + type: str + ''' + The type is a string that typically matches the types of the grammar file. + ''' + parent: 'Optional[BaseNode]' + ''' + The parent :class:`BaseNode` of this node or leaf. + None if this is the root node. + ''' + + def get_root_node(self): + """ + Returns the root node of a parser tree. The returned node doesn't have + a parent node like all the other nodes/leaves. + """ + scope = self + while scope.parent is not None: + scope = scope.parent + return scope + + def get_next_sibling(self): + """ + Returns the node immediately following this node in this parent's + children list. If this node does not have a next sibling, it is None + """ + parent = self.parent + if parent is None: + return None + + # Can't use index(); we need to test by identity + for i, child in enumerate(parent.children): + if child is self: + try: + return self.parent.children[i + 1] + except IndexError: + return None + + def get_previous_sibling(self): + """ + Returns the node immediately preceding this node in this parent's + children list. If this node does not have a previous sibling, it is + None. + """ + parent = self.parent + if parent is None: + return None + + # Can't use index(); we need to test by identity + for i, child in enumerate(parent.children): + if child is self: + if i == 0: + return None + return self.parent.children[i - 1] + + def get_previous_leaf(self): + """ + Returns the previous leaf in the parser tree. + Returns `None` if this is the first element in the parser tree. + """ + if self.parent is None: + return None + + node = self + while True: + c = node.parent.children + i = c.index(node) + if i == 0: + node = node.parent + if node.parent is None: + return None + else: + node = c[i - 1] + break + + while True: + try: + node = node.children[-1] + except AttributeError: # A Leaf doesn't have children. + return node + + def get_next_leaf(self): + """ + Returns the next leaf in the parser tree. + Returns None if this is the last element in the parser tree. + """ + if self.parent is None: + return None + + node = self + while True: + c = node.parent.children + i = c.index(node) + if i == len(c) - 1: + node = node.parent + if node.parent is None: + return None + else: + node = c[i + 1] + break + + while True: + try: + node = node.children[0] + except AttributeError: # A Leaf doesn't have children. + return node + + @abstractproperty + def start_pos(self) -> Tuple[int, int]: + """ + Returns the starting position of the prefix as a tuple, e.g. `(3, 4)`. + + :return tuple of int: (line, column) + """ + + @abstractproperty + def end_pos(self) -> Tuple[int, int]: + """ + Returns the end position of the prefix as a tuple, e.g. `(3, 4)`. + + :return tuple of int: (line, column) + """ + + @abstractmethod + def get_start_pos_of_prefix(self): + """ + Returns the start_pos of the prefix. This means basically it returns + the end_pos of the last prefix. The `get_start_pos_of_prefix()` of the + prefix `+` in `2 + 1` would be `(1, 1)`, while the start_pos is + `(1, 2)`. + + :return tuple of int: (line, column) + """ + + @abstractmethod + def get_first_leaf(self): + """ + Returns the first leaf of a node or itself if this is a leaf. + """ + + @abstractmethod + def get_last_leaf(self): + """ + Returns the last leaf of a node or itself if this is a leaf. + """ + + @abstractmethod + def get_code(self, include_prefix=True): + """ + Returns the code that was the input for the parser for this node. + + :param include_prefix: Removes the prefix (whitespace and comments) of + e.g. a statement. + """ + + def search_ancestor(self, *node_types: str) -> 'Optional[BaseNode]': + """ + Recursively looks at the parents of this node or leaf and returns the + first found node that matches ``node_types``. Returns ``None`` if no + matching node is found. + + :param node_types: type names that are searched for. + """ + node = self.parent + while node is not None: + if node.type in node_types: + return node + node = node.parent + return None + + def dump(self, *, indent: Optional[Union[int, str]] = 4) -> str: + """ + Returns a formatted dump of the parser tree rooted at this node or leaf. This is + mainly useful for debugging purposes. + + The ``indent`` parameter is interpreted in a similar way as :py:func:`ast.dump`. + If ``indent`` is a non-negative integer or string, then the tree will be + pretty-printed with that indent level. An indent level of 0, negative, or ``""`` + will only insert newlines. ``None`` selects the single line representation. + Using a positive integer indent indents that many spaces per level. If + ``indent`` is a string (such as ``"\\t"``), that string is used to indent each + level. + + :param indent: Indentation style as described above. The default indentation is + 4 spaces, which yields a pretty-printed dump. + + >>> import parso + >>> print(parso.parse("lambda x, y: x + y").dump()) + Module([ + Lambda([ + Keyword('lambda', (1, 0)), + Param([ + Name('x', (1, 7), prefix=' '), + Operator(',', (1, 8)), + ]), + Param([ + Name('y', (1, 10), prefix=' '), + ]), + Operator(':', (1, 11)), + PythonNode('arith_expr', [ + Name('x', (1, 13), prefix=' '), + Operator('+', (1, 15), prefix=' '), + Name('y', (1, 17), prefix=' '), + ]), + ]), + EndMarker('', (1, 18)), + ]) + """ + if indent is None: + newline = False + indent_string = '' + elif isinstance(indent, int): + newline = True + indent_string = ' ' * indent + elif isinstance(indent, str): + newline = True + indent_string = indent + else: + raise TypeError(f"expect 'indent' to be int, str or None, got {indent!r}") + + def _format_dump(node: NodeOrLeaf, indent: str = '', top_level: bool = True) -> str: + result = '' + node_type = type(node).__name__ + if isinstance(node, Leaf): + result += f'{indent}{node_type}(' + if isinstance(node, ErrorLeaf): + result += f'{node.token_type!r}, ' + elif isinstance(node, TypedLeaf): + result += f'{node.type!r}, ' + result += f'{node.value!r}, {node.start_pos!r}' + if node.prefix: + result += f', prefix={node.prefix!r}' + result += ')' + elif isinstance(node, BaseNode): + result += f'{indent}{node_type}(' + if isinstance(node, Node): + result += f'{node.type!r}, ' + result += '[' + if newline: + result += '\n' + for child in node.children: + result += _format_dump(child, indent=indent + indent_string, top_level=False) + result += f'{indent}])' + else: # pragma: no cover + # We shouldn't ever reach here, unless: + # - `NodeOrLeaf` is incorrectly subclassed else where + # - or a node's children list contains invalid nodes or leafs + # Both are unexpected internal errors. + raise TypeError(f'unsupported node encountered: {node!r}') + if not top_level: + if newline: + result += ',\n' + else: + result += ', ' + return result + + return _format_dump(self) + + +class Leaf(NodeOrLeaf): + ''' + Leafs are basically tokens with a better API. Leafs exactly know where they + were defined and what text preceeds them. + ''' + __slots__ = ('value', 'line', 'column', 'prefix') + prefix: str + + def __init__(self, value: str, start_pos: Tuple[int, int], prefix: str = '') -> None: + self.value = value + ''' + :py:func:`str` The value of the current token. + ''' + self.start_pos = start_pos + self.prefix = prefix + ''' + :py:func:`str` Typically a mixture of whitespace and comments. Stuff + that is syntactically irrelevant for the syntax tree. + ''' + self.parent: Optional[BaseNode] = None + ''' + The parent :class:`BaseNode` of this leaf. + ''' + + @property + def start_pos(self) -> Tuple[int, int]: + return self.line, self.column + + @start_pos.setter + def start_pos(self, value: Tuple[int, int]) -> None: + self.line = value[0] + self.column = value[1] + + def get_start_pos_of_prefix(self): + previous_leaf = self.get_previous_leaf() + if previous_leaf is None: + lines = split_lines(self.prefix) + # + 1 is needed because split_lines always returns at least ['']. + return self.line - len(lines) + 1, 0 # It's the first leaf. + return previous_leaf.end_pos + + def get_first_leaf(self): + return self + + def get_last_leaf(self): + return self + + def get_code(self, include_prefix=True): + if include_prefix: + return self.prefix + self.value + else: + return self.value + + @property + def end_pos(self) -> Tuple[int, int]: + lines = split_lines(self.value) + end_pos_line = self.line + len(lines) - 1 + # Check for multiline token + if self.line == end_pos_line: + end_pos_column = self.column + len(lines[-1]) + else: + end_pos_column = len(lines[-1]) + return end_pos_line, end_pos_column + + def __repr__(self): + value = self.value + if not value: + value = self.type + return "<%s: %s>" % (type(self).__name__, value) + + +class TypedLeaf(Leaf): + __slots__ = ('type',) + + def __init__(self, type, value, start_pos, prefix=''): + super().__init__(value, start_pos, prefix) + self.type = type + + +class BaseNode(NodeOrLeaf): + """ + The super class for all nodes. + A node has children, a type and possibly a parent node. + """ + __slots__ = ('children',) + + def __init__(self, children: List[NodeOrLeaf]) -> None: + self.children = children + """ + A list of :class:`NodeOrLeaf` child nodes. + """ + self.parent: Optional[BaseNode] = None + ''' + The parent :class:`BaseNode` of this node. + None if this is the root node. + ''' + for child in children: + child.parent = self + + @property + def start_pos(self) -> Tuple[int, int]: + return self.children[0].start_pos + + def get_start_pos_of_prefix(self): + return self.children[0].get_start_pos_of_prefix() + + @property + def end_pos(self) -> Tuple[int, int]: + return self.children[-1].end_pos + + def _get_code_for_children(self, children, include_prefix): + if include_prefix: + return "".join(c.get_code() for c in children) + else: + first = children[0].get_code(include_prefix=False) + return first + "".join(c.get_code() for c in children[1:]) + + def get_code(self, include_prefix=True): + return self._get_code_for_children(self.children, include_prefix) + + def get_leaf_for_position(self, position, include_prefixes=False): + """ + Get the :py:class:`parso.tree.Leaf` at ``position`` + + :param tuple position: A position tuple, row, column. Rows start from 1 + :param bool include_prefixes: If ``False``, ``None`` will be returned if ``position`` falls + on whitespace or comments before a leaf + :return: :py:class:`parso.tree.Leaf` at ``position``, or ``None`` + """ + def binary_search(lower, upper): + if lower == upper: + element = self.children[lower] + if not include_prefixes and position < element.start_pos: + # We're on a prefix. + return None + # In case we have prefixes, a leaf always matches + try: + return element.get_leaf_for_position(position, include_prefixes) + except AttributeError: + return element + + index = int((lower + upper) / 2) + element = self.children[index] + if position <= element.end_pos: + return binary_search(lower, index) + else: + return binary_search(index + 1, upper) + + if not ((1, 0) <= position <= self.children[-1].end_pos): + raise ValueError('Please provide a position that exists within this node.') + return binary_search(0, len(self.children) - 1) + + def get_first_leaf(self): + return self.children[0].get_first_leaf() + + def get_last_leaf(self): + return self.children[-1].get_last_leaf() + + def __repr__(self): + code = self.get_code().replace('\n', ' ').replace('\r', ' ').strip() + return "<%s: %s@%s,%s>" % \ + (type(self).__name__, code, self.start_pos[0], self.start_pos[1]) + + +class Node(BaseNode): + """Concrete implementation for interior nodes.""" + __slots__ = ('type',) + + def __init__(self, type, children): + super().__init__(children) + self.type = type + + def __repr__(self): + return "%s(%s, %r)" % (self.__class__.__name__, self.type, self.children) + + +class ErrorNode(BaseNode): + """ + A node that contains valid nodes/leaves that we're follow by a token that + was invalid. This basically means that the leaf after this node is where + Python would mark a syntax error. + """ + __slots__ = () + type = 'error_node' + + +class ErrorLeaf(Leaf): + """ + A leaf that is either completely invalid in a language (like `$` in Python) + or is invalid at that position. Like the star in `1 +* 1`. + """ + __slots__ = ('token_type',) + type = 'error_leaf' + + def __init__(self, token_type, value, start_pos, prefix=''): + super().__init__(value, start_pos, prefix) + self.token_type = token_type + + def __repr__(self): + return "<%s: %s:%s, %s>" % \ + (type(self).__name__, self.token_type, repr(self.value), self.start_pos) diff --git a/lib/python3.12/site-packages/parso/utils.py b/lib/python3.12/site-packages/parso/utils.py new file mode 100644 index 0000000000000000000000000000000000000000..6ad1ea26ad619d42e18cc4bcc72727e0c0eb18d0 --- /dev/null +++ b/lib/python3.12/site-packages/parso/utils.py @@ -0,0 +1,194 @@ +import re +import sys +from ast import literal_eval +from functools import total_ordering +from typing import NamedTuple, Sequence, Union + +# The following is a list in Python that are line breaks in str.splitlines, but +# not in Python. In Python only \r (Carriage Return, 0xD) and \n (Line Feed, +# 0xA) are allowed to split lines. +_NON_LINE_BREAKS = ( + '\v', # Vertical Tabulation 0xB + '\f', # Form Feed 0xC + '\x1C', # File Separator + '\x1D', # Group Separator + '\x1E', # Record Separator + '\x85', # Next Line (NEL - Equivalent to CR+LF. + # Used to mark end-of-line on some IBM mainframes.) + '\u2028', # Line Separator + '\u2029', # Paragraph Separator +) + + +class Version(NamedTuple): + major: int + minor: int + micro: int + + +def split_lines(string: str, keepends: bool = False) -> Sequence[str]: + r""" + Intended for Python code. In contrast to Python's :py:meth:`str.splitlines`, + looks at form feeds and other special characters as normal text. Just + splits ``\n`` and ``\r\n``. + Also different: Returns ``[""]`` for an empty string input. + + In Python 2.7 form feeds are used as normal characters when using + str.splitlines. However in Python 3 somewhere there was a decision to split + also on form feeds. + """ + if keepends: + lst = string.splitlines(True) + + # We have to merge lines that were broken by form feed characters. + merge = [] + for i, line in enumerate(lst): + try: + last_chr = line[-1] + except IndexError: + pass + else: + if last_chr in _NON_LINE_BREAKS: + merge.append(i) + + for index in reversed(merge): + try: + lst[index] = lst[index] + lst[index + 1] + del lst[index + 1] + except IndexError: + # index + 1 can be empty and therefore there's no need to + # merge. + pass + + # The stdlib's implementation of the end is inconsistent when calling + # it with/without keepends. One time there's an empty string in the + # end, one time there's none. + if string.endswith('\n') or string.endswith('\r') or string == '': + lst.append('') + return lst + else: + return re.split(r'\n|\r\n|\r', string) + + +def python_bytes_to_unicode( + source: Union[str, bytes], encoding: str = 'utf-8', errors: str = 'strict' +) -> str: + """ + Checks for unicode BOMs and PEP 263 encoding declarations. Then returns a + unicode object like in :py:meth:`bytes.decode`. + + :param encoding: See :py:meth:`bytes.decode` documentation. + :param errors: See :py:meth:`bytes.decode` documentation. ``errors`` can be + ``'strict'``, ``'replace'`` or ``'ignore'``. + """ + def detect_encoding(): + """ + For the implementation of encoding definitions in Python, look at: + - http://www.python.org/dev/peps/pep-0263/ + - http://docs.python.org/2/reference/lexical_analysis.html#encoding-declarations + """ + byte_mark = literal_eval(r"b'\xef\xbb\xbf'") + if source.startswith(byte_mark): + # UTF-8 byte-order mark + return 'utf-8' + + first_two_lines = re.match(br'(?:[^\r\n]*(?:\r\n|\r|\n)){0,2}', source).group(0) + possible_encoding = re.search(br"coding[=:]\s*([-\w.]+)", + first_two_lines) + if possible_encoding: + e = possible_encoding.group(1) + if not isinstance(e, str): + e = str(e, 'ascii', 'replace') + return e + else: + # the default if nothing else has been set -> PEP 263 + return encoding + + if isinstance(source, str): + # only cast str/bytes + return source + + encoding = detect_encoding() + try: + # Cast to unicode + return str(source, encoding, errors) + except LookupError: + if errors == 'replace': + # This is a weird case that can happen if the given encoding is not + # a valid encoding. This usually shouldn't happen with provided + # encodings, but can happen if somebody uses encoding declarations + # like `# coding: foo-8`. + return str(source, 'utf-8', errors) + raise + + +def version_info() -> Version: + """ + Returns a namedtuple of parso's version, similar to Python's + ``sys.version_info``. + """ + from parso import __version__ + tupl = re.findall(r'[a-z]+|\d+', __version__) + return Version(*[x if i == 3 else int(x) for i, x in enumerate(tupl)]) + + +class _PythonVersionInfo(NamedTuple): + major: int + minor: int + + +@total_ordering +class PythonVersionInfo(_PythonVersionInfo): + def __gt__(self, other): + if isinstance(other, tuple): + if len(other) != 2: + raise ValueError("Can only compare to tuples of length 2.") + return (self.major, self.minor) > other + super().__gt__(other) + + return (self.major, self.minor) + + def __eq__(self, other): + if isinstance(other, tuple): + if len(other) != 2: + raise ValueError("Can only compare to tuples of length 2.") + return (self.major, self.minor) == other + super().__eq__(other) + + def __ne__(self, other): + return not self.__eq__(other) + + +def _parse_version(version) -> PythonVersionInfo: + match = re.match(r'(\d+)(?:\.(\d{1,2})(?:\.\d+)?)?((a|b|rc)\d)?$', version) + if match is None: + raise ValueError('The given version is not in the right format. ' + 'Use something like "3.8" or "3".') + + major = int(match.group(1)) + minor = match.group(2) + if minor is None: + # Use the latest Python in case it's not exactly defined, because the + # grammars are typically backwards compatible? + if major == 2: + minor = "7" + elif major == 3: + minor = "6" + else: + raise NotImplementedError("Sorry, no support yet for those fancy new/old versions.") + minor = int(minor) + return PythonVersionInfo(major, minor) + + +def parse_version_string(version: str = None) -> PythonVersionInfo: + """ + Checks for a valid version number (e.g. `3.8` or `3.10.1` or `3`) and + returns a corresponding version info that is always two characters long in + decimal. + """ + if version is None: + version = '%s.%s' % sys.version_info[:2] + if not isinstance(version, str): + raise TypeError('version must be a string like "3.8"') + + return _parse_version(version) diff --git a/lib/python3.12/site-packages/prompt_toolkit-3.0.52.dist-info/INSTALLER b/lib/python3.12/site-packages/prompt_toolkit-3.0.52.dist-info/INSTALLER new file mode 100644 index 0000000000000000000000000000000000000000..a1b589e38a32041e49332e5e81c2d363dc418d68 --- /dev/null +++ b/lib/python3.12/site-packages/prompt_toolkit-3.0.52.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/lib/python3.12/site-packages/prompt_toolkit-3.0.52.dist-info/METADATA b/lib/python3.12/site-packages/prompt_toolkit-3.0.52.dist-info/METADATA new file mode 100644 index 0000000000000000000000000000000000000000..7fbbd56ce663f8b0805c5adfce699219b0408e13 --- /dev/null +++ b/lib/python3.12/site-packages/prompt_toolkit-3.0.52.dist-info/METADATA @@ -0,0 +1,172 @@ +Metadata-Version: 2.4 +Name: prompt_toolkit +Version: 3.0.52 +Summary: Library for building powerful interactive command lines in Python +Author: Jonathan Slenders +Project-URL: Homepage, https://github.com/prompt-toolkit/python-prompt-toolkit +Project-URL: Documentation, https://python-prompt-toolkit.readthedocs.io/en/stable/ +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +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 +Classifier: Topic :: Software Development +Requires-Python: >=3.8 +Description-Content-Type: text/x-rst +License-File: LICENSE +License-File: AUTHORS.rst +Requires-Dist: wcwidth +Dynamic: license-file + +Python Prompt Toolkit +===================== + +|AppVeyor| |PyPI| |RTD| |License| |Codecov| + +.. image :: /docs/images/logo_400px.png + +``prompt_toolkit`` *is a library for building powerful interactive command line applications in Python.* + +Read the `documentation on readthedocs +`_. + + +Gallery +******* + +`ptpython `_ is an interactive +Python Shell, build on top of ``prompt_toolkit``. + +.. image :: /docs/images/ptpython.png + +`More examples `_ + + +prompt_toolkit features +*********************** + +``prompt_toolkit`` could be a replacement for `GNU readline +`_, but it can be much +more than that. + +Some features: + +- **Pure Python**. +- Syntax highlighting of the input while typing. (For instance, with a Pygments lexer.) +- Multi-line input editing. +- Advanced code completion. +- Both Emacs and Vi key bindings. (Similar to readline.) +- Even some advanced Vi functionality, like named registers and digraphs. +- Reverse and forward incremental search. +- Works well with Unicode double width characters. (Chinese input.) +- Selecting text for copy/paste. (Both Emacs and Vi style.) +- Support for `bracketed paste `_. +- Mouse support for cursor positioning and scrolling. +- Auto suggestions. (Like `fish shell `_.) +- Multiple input buffers. +- No global state. +- Lightweight, the only dependencies are Pygments and wcwidth. +- Runs on Linux, OS X, FreeBSD, OpenBSD and Windows systems. +- And much more... + +Feel free to create tickets for bugs and feature requests, and create pull +requests if you have nice patches that you would like to share with others. + + +Installation +************ + +:: + + pip install prompt_toolkit + +For Conda, do: + +:: + + conda install -c https://conda.anaconda.org/conda-forge prompt_toolkit + + +About Windows support +********************* + +``prompt_toolkit`` is cross platform, and everything that you build on top +should run fine on both Unix and Windows systems. Windows support is best on +recent Windows 10 builds, for which the command line window supports vt100 +escape sequences. (If not supported, we fall back to using Win32 APIs for color +and cursor movements). + +It's worth noting that the implementation is a "best effort of what is +possible". Both Unix and Windows terminals have their limitations. But in +general, the Unix experience will still be a little better. + + +Getting started +*************** + +The most simple example of the library would look like this: + +.. code:: python + + from prompt_toolkit import prompt + + if __name__ == '__main__': + answer = prompt('Give me some input: ') + print('You said: %s' % answer) + +For more complex examples, have a look in the ``examples`` directory. All +examples are chosen to demonstrate only one thing. Also, don't be afraid to +look at the source code. The implementation of the ``prompt`` function could be +a good start. + +Philosophy +********** + +The source code of ``prompt_toolkit`` should be **readable**, **concise** and +**efficient**. We prefer short functions focusing each on one task and for which +the input and output types are clearly specified. We mostly prefer composition +over inheritance, because inheritance can result in too much functionality in +the same object. We prefer immutable objects where possible (objects don't +change after initialization). Reusability is important. We absolutely refrain +from having a changing global state, it should be possible to have multiple +independent instances of the same code in the same process. The architecture +should be layered: the lower levels operate on primitive operations and data +structures giving -- when correctly combined -- all the possible flexibility; +while at the higher level, there should be a simpler API, ready-to-use and +sufficient for most use cases. Thinking about algorithms and efficiency is +important, but avoid premature optimization. + + +`Projects using prompt_toolkit `_ +*********************************************** + +Special thanks to +***************** + +- `Pygments `_: Syntax highlighter. +- `wcwidth `_: Determine columns needed for a wide characters. + +.. |PyPI| image:: https://img.shields.io/pypi/v/prompt_toolkit.svg + :target: https://pypi.python.org/pypi/prompt-toolkit/ + :alt: Latest Version + +.. |AppVeyor| image:: https://ci.appveyor.com/api/projects/status/32r7s2skrgm9ubva?svg=true + :target: https://ci.appveyor.com/project/prompt-toolkit/python-prompt-toolkit/ + +.. |RTD| image:: https://readthedocs.org/projects/python-prompt-toolkit/badge/ + :target: https://python-prompt-toolkit.readthedocs.io/en/master/ + +.. |License| image:: https://img.shields.io/github/license/prompt-toolkit/python-prompt-toolkit.svg + :target: https://github.com/prompt-toolkit/python-prompt-toolkit/blob/master/LICENSE + +.. |Codecov| image:: https://codecov.io/gh/prompt-toolkit/python-prompt-toolkit/branch/master/graphs/badge.svg?style=flat + :target: https://codecov.io/gh/prompt-toolkit/python-prompt-toolkit/ + diff --git a/lib/python3.12/site-packages/prompt_toolkit-3.0.52.dist-info/RECORD b/lib/python3.12/site-packages/prompt_toolkit-3.0.52.dist-info/RECORD new file mode 100644 index 0000000000000000000000000000000000000000..e136a3f4ded7fbd88b3e3d5e4740694a26b748e1 --- /dev/null +++ b/lib/python3.12/site-packages/prompt_toolkit-3.0.52.dist-info/RECORD @@ -0,0 +1,298 @@ +prompt_toolkit-3.0.52.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +prompt_toolkit-3.0.52.dist-info/METADATA,sha256=gNS9QKahcd_SXDMrrr7h_YciNC8TPbU4Xi1XGaXaLik,6414 +prompt_toolkit-3.0.52.dist-info/RECORD,, +prompt_toolkit-3.0.52.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91 +prompt_toolkit-3.0.52.dist-info/licenses/AUTHORS.rst,sha256=09xixryENmWElauJrqN1Eef6k5HSgmVyOcnPuA29QuU,148 +prompt_toolkit-3.0.52.dist-info/licenses/LICENSE,sha256=MDV02b3YXHV9YCUBeUK_F7ru3yd49ivX9CXQfYgPTEo,1493 +prompt_toolkit-3.0.52.dist-info/top_level.txt,sha256=5rJXrEGx6st4KkmhOPR6l0ITDbV53x_Xy6MurOukXfA,15 +prompt_toolkit/__init__.py,sha256=zWTvKCSL-Qb_hO8opATHynv2gNdm0YMCA9x3obLJUH4,1376 +prompt_toolkit/__pycache__/__init__.cpython-312.pyc,, +prompt_toolkit/__pycache__/auto_suggest.cpython-312.pyc,, +prompt_toolkit/__pycache__/buffer.cpython-312.pyc,, +prompt_toolkit/__pycache__/cache.cpython-312.pyc,, +prompt_toolkit/__pycache__/cursor_shapes.cpython-312.pyc,, +prompt_toolkit/__pycache__/data_structures.cpython-312.pyc,, +prompt_toolkit/__pycache__/document.cpython-312.pyc,, +prompt_toolkit/__pycache__/enums.cpython-312.pyc,, +prompt_toolkit/__pycache__/history.cpython-312.pyc,, +prompt_toolkit/__pycache__/keys.cpython-312.pyc,, +prompt_toolkit/__pycache__/log.cpython-312.pyc,, +prompt_toolkit/__pycache__/mouse_events.cpython-312.pyc,, +prompt_toolkit/__pycache__/patch_stdout.cpython-312.pyc,, +prompt_toolkit/__pycache__/renderer.cpython-312.pyc,, +prompt_toolkit/__pycache__/search.cpython-312.pyc,, +prompt_toolkit/__pycache__/selection.cpython-312.pyc,, +prompt_toolkit/__pycache__/token.cpython-312.pyc,, +prompt_toolkit/__pycache__/utils.cpython-312.pyc,, +prompt_toolkit/__pycache__/validation.cpython-312.pyc,, +prompt_toolkit/__pycache__/win32_types.cpython-312.pyc,, +prompt_toolkit/application/__init__.py,sha256=rat9iPhYTmo7nd2BU8xZSU_-AfJpjnnBmxe9y3TQivM,657 +prompt_toolkit/application/__pycache__/__init__.cpython-312.pyc,, +prompt_toolkit/application/__pycache__/application.cpython-312.pyc,, +prompt_toolkit/application/__pycache__/current.cpython-312.pyc,, +prompt_toolkit/application/__pycache__/dummy.cpython-312.pyc,, +prompt_toolkit/application/__pycache__/run_in_terminal.cpython-312.pyc,, +prompt_toolkit/application/application.py,sha256=iTjvCjKjtPss4_L8MYidBFFQLumfHLgrkFQSGqE4O3c,63242 +prompt_toolkit/application/current.py,sha256=IyZmMpzOWEISG_o06Bj2vzgdDTxI7uAQnbTHFJGuL0s,6209 +prompt_toolkit/application/dummy.py,sha256=BCfThUgz5Eb5fWJSKBVeJaA5kwksw8jJQtN6g61xMXM,1619 +prompt_toolkit/application/run_in_terminal.py,sha256=qgTfpG3qGP4wRy9l_7zU8P7s59CARIvagulTyPn_oEg,3767 +prompt_toolkit/auto_suggest.py,sha256=qSiaxlaKjLyNEJ8bJN0641gqsIzZ3LB2cOyq88xBQb4,5798 +prompt_toolkit/buffer.py,sha256=VkAbKTJV7441aO4Ei-ozqc8IBlNPEnLGAt5t42tB6jg,74513 +prompt_toolkit/cache.py,sha256=Lo3ewsEIgn-LQBYNni79w74u5LSvvuVYF0e6giEArQg,3823 +prompt_toolkit/clipboard/__init__.py,sha256=yK0LonIfEZRyoXqcgLdh8kjOhechjO-Ej2-C1g3VegQ,439 +prompt_toolkit/clipboard/__pycache__/__init__.cpython-312.pyc,, +prompt_toolkit/clipboard/__pycache__/base.cpython-312.pyc,, +prompt_toolkit/clipboard/__pycache__/in_memory.cpython-312.pyc,, +prompt_toolkit/clipboard/__pycache__/pyperclip.cpython-312.pyc,, +prompt_toolkit/clipboard/base.py,sha256=rucEv1kKfvZUj6bwGRz04uSSTZie7rvnKUnyVXb2vv4,2515 +prompt_toolkit/clipboard/in_memory.py,sha256=U_iY6UUevkKMfTvir_XMsD0qwuLVKuoTeRdjkZW-A6I,1060 +prompt_toolkit/clipboard/pyperclip.py,sha256=H9HOlyGW0XItvx_Ji64zBQkiQPfLb6DFAw5J5irzK28,1160 +prompt_toolkit/completion/__init__.py,sha256=8Hm2yJ1nqBkaC-R9ugELgjhU32U308V89F6bJG0QDYo,992 +prompt_toolkit/completion/__pycache__/__init__.cpython-312.pyc,, +prompt_toolkit/completion/__pycache__/base.cpython-312.pyc,, +prompt_toolkit/completion/__pycache__/deduplicate.cpython-312.pyc,, +prompt_toolkit/completion/__pycache__/filesystem.cpython-312.pyc,, +prompt_toolkit/completion/__pycache__/fuzzy_completer.cpython-312.pyc,, +prompt_toolkit/completion/__pycache__/nested.cpython-312.pyc,, +prompt_toolkit/completion/__pycache__/word_completer.cpython-312.pyc,, +prompt_toolkit/completion/base.py,sha256=T7212aScNaGMaSrDIwsJIXeXLIM_eVCIcScNcDPZYwI,16103 +prompt_toolkit/completion/deduplicate.py,sha256=QibqYI23GPjsbyxaxiNoqAbKawzHmfYOlxeW2HPFbTE,1436 +prompt_toolkit/completion/filesystem.py,sha256=Z_RR72e14bVavdWnbxECw23qCt_TWTY9R6DpVqW7vxE,3949 +prompt_toolkit/completion/fuzzy_completer.py,sha256=RnREvA7y6nC7LKGqZUEvtuSm8eXVQYheJTsnhUvRbmM,7738 +prompt_toolkit/completion/nested.py,sha256=ig2Qy4dLyDvOT6O8Qb-iRZLWzlT11S5tVQ3GFZmpm-U,3844 +prompt_toolkit/completion/word_completer.py,sha256=VF1S7YCxYqI3pKmVXJaD82eMW1ZMq8_zAAIS1XKGU5M,3435 +prompt_toolkit/contrib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +prompt_toolkit/contrib/__pycache__/__init__.cpython-312.pyc,, +prompt_toolkit/contrib/completers/__init__.py,sha256=qJB_xNFGbhfiDv_zUaox9mkQEGqBYqP_jfByQDb93hA,103 +prompt_toolkit/contrib/completers/__pycache__/__init__.cpython-312.pyc,, +prompt_toolkit/contrib/completers/__pycache__/system.cpython-312.pyc,, +prompt_toolkit/contrib/completers/system.py,sha256=0Hc2dziheEx2qNog4YOl-4Tu8Fg5Dx2xjNURTx09BDg,2057 +prompt_toolkit/contrib/regular_languages/__init__.py,sha256=cgMQkakD4FbvLUozDGucRRFOk8yScfcKfqOMpCtvAPo,3279 +prompt_toolkit/contrib/regular_languages/__pycache__/__init__.cpython-312.pyc,, +prompt_toolkit/contrib/regular_languages/__pycache__/compiler.cpython-312.pyc,, +prompt_toolkit/contrib/regular_languages/__pycache__/completion.cpython-312.pyc,, +prompt_toolkit/contrib/regular_languages/__pycache__/lexer.cpython-312.pyc,, +prompt_toolkit/contrib/regular_languages/__pycache__/regex_parser.cpython-312.pyc,, +prompt_toolkit/contrib/regular_languages/__pycache__/validation.cpython-312.pyc,, +prompt_toolkit/contrib/regular_languages/compiler.py,sha256=3tnUJCE2jCcVI63vcpI0kG4KfuqIatSQRb8-F5UCgsI,21948 +prompt_toolkit/contrib/regular_languages/completion.py,sha256=jESF35RaYWj_rnT-OZc_zC9QZXYvPao4JZ8wx7yS3KM,3468 +prompt_toolkit/contrib/regular_languages/lexer.py,sha256=DBgyek9LkfJv6hz24eOaVM--w9Qaw4zIMWusMvGHBts,3415 +prompt_toolkit/contrib/regular_languages/regex_parser.py,sha256=zWGJfQSjomvdj2rD7MPpn2pWOUR7VMv4su5iAV0jzM4,7732 +prompt_toolkit/contrib/regular_languages/validation.py,sha256=4k5wxgUFc_KTOW5PmmZOrWb-Z-HjX8fjjKqul-oR8uc,2059 +prompt_toolkit/contrib/ssh/__init__.py,sha256=UcRG2wc28EEKtFEudoIXz_DFzWKKQjAVSv6cf-ufPiM,180 +prompt_toolkit/contrib/ssh/__pycache__/__init__.cpython-312.pyc,, +prompt_toolkit/contrib/ssh/__pycache__/server.cpython-312.pyc,, +prompt_toolkit/contrib/ssh/server.py,sha256=81McNn6r0Cbu9SPceH7fa5QirAnteHmNh1Gk4dFpgvI,6130 +prompt_toolkit/contrib/telnet/__init__.py,sha256=NyUfsmJdafGiUxD9gzYQNlVdHu_ILDH7F57VJw8efUM,104 +prompt_toolkit/contrib/telnet/__pycache__/__init__.cpython-312.pyc,, +prompt_toolkit/contrib/telnet/__pycache__/log.cpython-312.pyc,, +prompt_toolkit/contrib/telnet/__pycache__/protocol.cpython-312.pyc,, +prompt_toolkit/contrib/telnet/__pycache__/server.cpython-312.pyc,, +prompt_toolkit/contrib/telnet/log.py,sha256=LcFRDyRxoRKSZsVRVpBOrEgsEt_LQLyUHKtgVZklopI,167 +prompt_toolkit/contrib/telnet/protocol.py,sha256=2i-JYfaAse-uFWtNdVEoP_Q-OMbkl3YbUfv_wvaaS3k,5584 +prompt_toolkit/contrib/telnet/server.py,sha256=S2UGX30O0aYjYV_oy4hnJM7f7xllGxXA-WUnlKqLNTw,13461 +prompt_toolkit/cursor_shapes.py,sha256=k5g5yJONGl1ITgy29KX9yzspJvIJ6Jbbwd7WkYC9Z-4,3721 +prompt_toolkit/data_structures.py,sha256=w0BZy6Fpx4se-kAI9Kj8Q7lAKLln8U_Em_ncpqnC1xY,212 +prompt_toolkit/document.py,sha256=s83zyjuwE8pvEdFN8y46pRYa_YRveVBkf9As3xlYU-w,40547 +prompt_toolkit/enums.py,sha256=F3q9JmH9vhpMLA2OKKN7RrNQu_YDlNWoPU-0qsTUuAs,358 +prompt_toolkit/eventloop/__init__.py,sha256=pxSkV_zybeoj6Ff3lgNHhbD5ENmBW9mk_XkiyeRL_OY,730 +prompt_toolkit/eventloop/__pycache__/__init__.cpython-312.pyc,, +prompt_toolkit/eventloop/__pycache__/async_generator.cpython-312.pyc,, +prompt_toolkit/eventloop/__pycache__/inputhook.cpython-312.pyc,, +prompt_toolkit/eventloop/__pycache__/utils.cpython-312.pyc,, +prompt_toolkit/eventloop/__pycache__/win32.cpython-312.pyc,, +prompt_toolkit/eventloop/async_generator.py,sha256=nozLJR4z2MJKV7Qi0hsknA2mb1Jcp7XJx-AdUEDhDhw,3933 +prompt_toolkit/eventloop/inputhook.py,sha256=LDElZtmg-kLQiItMS8CFPxtLzxV8QzohWHsWUvw3h00,6130 +prompt_toolkit/eventloop/utils.py,sha256=VhYmsDZmRwVXnEPBF_C2LpiW-ranPn6EIXWIuMa6XaU,3200 +prompt_toolkit/eventloop/win32.py,sha256=wrLJVOtOw_tqVOeK6ttNF47Sk2oX342dLN1pxKBLCL4,2286 +prompt_toolkit/filters/__init__.py,sha256=2YSVwf4EnLf1VOXYmb8Dr0WoA93XGGO0iCUIr14KGXQ,1990 +prompt_toolkit/filters/__pycache__/__init__.cpython-312.pyc,, +prompt_toolkit/filters/__pycache__/app.cpython-312.pyc,, +prompt_toolkit/filters/__pycache__/base.cpython-312.pyc,, +prompt_toolkit/filters/__pycache__/cli.cpython-312.pyc,, +prompt_toolkit/filters/__pycache__/utils.cpython-312.pyc,, +prompt_toolkit/filters/app.py,sha256=GAQzU3An_pUIIV0ja4RV7Wfl0CbLI-U1HUMOItrCKNI,10374 +prompt_toolkit/filters/base.py,sha256=asrgKE-gzYlRLrS4w3kMFimvZtXQ9pk252Vs5ShVeeM,6855 +prompt_toolkit/filters/cli.py,sha256=QGV7JT7-BUXpPXNzBLUcNH3GI69ugFZCDV1nylOjq78,1867 +prompt_toolkit/filters/utils.py,sha256=4nOjHPEd451Pj3qpfg40fq3XSnt1kmq3WoAbhu2NV-8,859 +prompt_toolkit/formatted_text/__init__.py,sha256=aQtNhxOhIa_HmvlNOQ2RGGpplg-KX3sYFJWiXgNfQxY,1509 +prompt_toolkit/formatted_text/__pycache__/__init__.cpython-312.pyc,, +prompt_toolkit/formatted_text/__pycache__/ansi.cpython-312.pyc,, +prompt_toolkit/formatted_text/__pycache__/base.cpython-312.pyc,, +prompt_toolkit/formatted_text/__pycache__/html.cpython-312.pyc,, +prompt_toolkit/formatted_text/__pycache__/pygments.cpython-312.pyc,, +prompt_toolkit/formatted_text/__pycache__/utils.cpython-312.pyc,, +prompt_toolkit/formatted_text/ansi.py,sha256=9jajRZ4bIZ4rRDoSNMyDKF0ctErcEwoyFbvh96ownXU,9824 +prompt_toolkit/formatted_text/base.py,sha256=X3y5QIPH2IS9LesYzXneELtT4zGpik8gd-UQVh6I2bE,5162 +prompt_toolkit/formatted_text/html.py,sha256=-88VwuuCLRNkzEgK8FJKOHT9NDh939BxH8vGivvILdU,4374 +prompt_toolkit/formatted_text/pygments.py,sha256=sK-eFFzOnD2sgadVLgNkW-xOuTw_uIf8_z06DZ4CA8g,780 +prompt_toolkit/formatted_text/utils.py,sha256=r6tPtwo6dqvqf9gqZ7ARyvtNUjDDq6QZqrTWg6EMFuQ,3044 +prompt_toolkit/history.py,sha256=S9W9SgL83QftMQANdjdjBMm-yGmeM51_qCRRC1H4Mr8,9441 +prompt_toolkit/input/__init__.py,sha256=7g6kwNanG4Ml12FFdj9E1ivChpXWcfRUMUJzmTQMS7U,273 +prompt_toolkit/input/__pycache__/__init__.cpython-312.pyc,, +prompt_toolkit/input/__pycache__/ansi_escape_sequences.cpython-312.pyc,, +prompt_toolkit/input/__pycache__/base.cpython-312.pyc,, +prompt_toolkit/input/__pycache__/defaults.cpython-312.pyc,, +prompt_toolkit/input/__pycache__/posix_pipe.cpython-312.pyc,, +prompt_toolkit/input/__pycache__/posix_utils.cpython-312.pyc,, +prompt_toolkit/input/__pycache__/typeahead.cpython-312.pyc,, +prompt_toolkit/input/__pycache__/vt100.cpython-312.pyc,, +prompt_toolkit/input/__pycache__/vt100_parser.cpython-312.pyc,, +prompt_toolkit/input/__pycache__/win32.cpython-312.pyc,, +prompt_toolkit/input/__pycache__/win32_pipe.cpython-312.pyc,, +prompt_toolkit/input/ansi_escape_sequences.py,sha256=h7714SZgs2z80PZRVxsCfHHJjtEUmWlToVCBtFFvfR4,13663 +prompt_toolkit/input/base.py,sha256=nqdM61RerkAnTBxQCqxxIHErKvaEXUerPIn--gr6NLs,4030 +prompt_toolkit/input/defaults.py,sha256=a-vczSh7kngFArLhFsJ2CXNdkx5WQlzilxHLdzGDkFw,2500 +prompt_toolkit/input/posix_pipe.py,sha256=B_JS2-FB6Sk0da9gSH0NnhcUCkp3bw0m1-ogMOHmmcE,3158 +prompt_toolkit/input/posix_utils.py,sha256=ySaEGnt_IwG5nzxcpILgEXC60mbrIAbC3ZZ6kuE9zCw,3973 +prompt_toolkit/input/typeahead.py,sha256=mAaf5_XKTLpao1kw9ORIrhGGEz9gvu4oc-iZKKMQz3k,2545 +prompt_toolkit/input/vt100.py,sha256=soxxSLU7fwp6yn77j5gCYUZroEp7KBKm4a3Zn4vRAsk,10514 +prompt_toolkit/input/vt100_parser.py,sha256=qDrNbsnPukZblfyjgfjCvzMv8xQKRz0M84UvUWq7P44,8407 +prompt_toolkit/input/win32.py,sha256=gF_IXqZhFKeAbKWYGkMKRBG3kF4yqDgDzWG2tX073IM,31410 +prompt_toolkit/input/win32_pipe.py,sha256=OvjKHN5xfEoGHLygWwayyeB0RolHL6YHLNeOMK-54LU,4700 +prompt_toolkit/key_binding/__init__.py,sha256=IZWqJLBjQaQMfo0SJTjqJKQH0TZcSNa2Cdln-M4z8JI,447 +prompt_toolkit/key_binding/__pycache__/__init__.cpython-312.pyc,, +prompt_toolkit/key_binding/__pycache__/defaults.cpython-312.pyc,, +prompt_toolkit/key_binding/__pycache__/digraphs.cpython-312.pyc,, +prompt_toolkit/key_binding/__pycache__/emacs_state.cpython-312.pyc,, +prompt_toolkit/key_binding/__pycache__/key_bindings.cpython-312.pyc,, +prompt_toolkit/key_binding/__pycache__/key_processor.cpython-312.pyc,, +prompt_toolkit/key_binding/__pycache__/vi_state.cpython-312.pyc,, +prompt_toolkit/key_binding/bindings/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +prompt_toolkit/key_binding/bindings/__pycache__/__init__.cpython-312.pyc,, +prompt_toolkit/key_binding/bindings/__pycache__/auto_suggest.cpython-312.pyc,, +prompt_toolkit/key_binding/bindings/__pycache__/basic.cpython-312.pyc,, +prompt_toolkit/key_binding/bindings/__pycache__/completion.cpython-312.pyc,, +prompt_toolkit/key_binding/bindings/__pycache__/cpr.cpython-312.pyc,, +prompt_toolkit/key_binding/bindings/__pycache__/emacs.cpython-312.pyc,, +prompt_toolkit/key_binding/bindings/__pycache__/focus.cpython-312.pyc,, +prompt_toolkit/key_binding/bindings/__pycache__/mouse.cpython-312.pyc,, +prompt_toolkit/key_binding/bindings/__pycache__/named_commands.cpython-312.pyc,, +prompt_toolkit/key_binding/bindings/__pycache__/open_in_editor.cpython-312.pyc,, +prompt_toolkit/key_binding/bindings/__pycache__/page_navigation.cpython-312.pyc,, +prompt_toolkit/key_binding/bindings/__pycache__/scroll.cpython-312.pyc,, +prompt_toolkit/key_binding/bindings/__pycache__/search.cpython-312.pyc,, +prompt_toolkit/key_binding/bindings/__pycache__/vi.cpython-312.pyc,, +prompt_toolkit/key_binding/bindings/auto_suggest.py,sha256=4PrJVgIK_Nt2o3RtVtuRm2aFPGrackhuMCBVNtjPj7M,1855 +prompt_toolkit/key_binding/bindings/basic.py,sha256=Fp9mj-RYZlGmAU9UV9wIIEnlxELN7NJ0qakMVH7MuRU,7229 +prompt_toolkit/key_binding/bindings/completion.py,sha256=6nR3WfGe7FDsjq1xTsDazeajkV9KBLpCYQi3klujdLU,6903 +prompt_toolkit/key_binding/bindings/cpr.py,sha256=181XQNZ0-sgL-vV2B67aRitTFHadogvMUh6LWVMUTV0,786 +prompt_toolkit/key_binding/bindings/emacs.py,sha256=trIZUu8e5kJGSaq6Ndb-Exz4NdHV9SjUsfsw_UM8c6o,19634 +prompt_toolkit/key_binding/bindings/focus.py,sha256=LIP4InccUUvD7I4NZrqtY9WjVfO_wJLyrVcoxAw92uU,507 +prompt_toolkit/key_binding/bindings/mouse.py,sha256=6JPr0BqzFfLEVb7Ek_WO0CejUcwq0jIrrNwvSGkHeus,18586 +prompt_toolkit/key_binding/bindings/named_commands.py,sha256=jdkqQ-ltNYC2BgIW1QdG7Qx4mWIod2Ps6C2TpL6NJ-Y,18407 +prompt_toolkit/key_binding/bindings/open_in_editor.py,sha256=bgVmeDmVtHsgzJnc59b-dOZ-nO6WydBYI_7aOWMpe5c,1356 +prompt_toolkit/key_binding/bindings/page_navigation.py,sha256=RPLUEZuZvekErPazex7pK0c6LxeV9LshewBHp012iMI,2392 +prompt_toolkit/key_binding/bindings/scroll.py,sha256=hQeQ0m2AStUKjVNDXfa9DTMw3WS5qzW1n3gU0fkfWFk,5613 +prompt_toolkit/key_binding/bindings/search.py,sha256=rU6VYra1IDzN6mG4mrbGivrZ-hjy_kZcjsKqmdVJKAE,2632 +prompt_toolkit/key_binding/bindings/vi.py,sha256=TSglqzPZU9VMernOvn09GVxObFXpXuyCSiH9i1MpIIo,75602 +prompt_toolkit/key_binding/defaults.py,sha256=JZJTshyBV39cWH2AT7xDP9AXOiyXQpjaI-ckePTi7os,1975 +prompt_toolkit/key_binding/digraphs.py,sha256=rZvh9AdY5Te6bSlIHRQNskJYVIONYahYuu-w9Pti5yM,32785 +prompt_toolkit/key_binding/emacs_state.py,sha256=ZJBWcLTzgtRkUW9UiDuI-SRrnlLsxu3IrTOK0_UQt5Y,884 +prompt_toolkit/key_binding/key_bindings.py,sha256=HEbemzMuSdO4fhHc_p7em02dd-rt7ZqxGzs-Fpu6LcY,20933 +prompt_toolkit/key_binding/key_processor.py,sha256=0WLK4dcU8klL2Xna_RKxOpsW7t8ld67Y9Xmto3U-n0E,17555 +prompt_toolkit/key_binding/vi_state.py,sha256=GAlH1xPvYoJnRUM9s3bTMlWh5UnIZ40fFtYInKMC2x0,3337 +prompt_toolkit/keys.py,sha256=nDkIqJbm_dRsVjArp7oItGKIFAAnSxcSniSwc1O-BYA,4916 +prompt_toolkit/layout/__init__.py,sha256=gNbniLmlvkWwPE6Kg2ykyZJRTOKsWnHbwUjyO-VFDP8,3603 +prompt_toolkit/layout/__pycache__/__init__.cpython-312.pyc,, +prompt_toolkit/layout/__pycache__/containers.cpython-312.pyc,, +prompt_toolkit/layout/__pycache__/controls.cpython-312.pyc,, +prompt_toolkit/layout/__pycache__/dimension.cpython-312.pyc,, +prompt_toolkit/layout/__pycache__/dummy.cpython-312.pyc,, +prompt_toolkit/layout/__pycache__/layout.cpython-312.pyc,, +prompt_toolkit/layout/__pycache__/margins.cpython-312.pyc,, +prompt_toolkit/layout/__pycache__/menus.cpython-312.pyc,, +prompt_toolkit/layout/__pycache__/mouse_handlers.cpython-312.pyc,, +prompt_toolkit/layout/__pycache__/processors.cpython-312.pyc,, +prompt_toolkit/layout/__pycache__/screen.cpython-312.pyc,, +prompt_toolkit/layout/__pycache__/scrollable_pane.cpython-312.pyc,, +prompt_toolkit/layout/__pycache__/utils.cpython-312.pyc,, +prompt_toolkit/layout/containers.py,sha256=BpR90-WNGPBcoYyCCSorAn2eHTz-1aoeQQO7QGqbuYU,100179 +prompt_toolkit/layout/controls.py,sha256=9h6425oGeBwLO85MBNdHSh6XsrtEay5JwuIX-fuzsVI,35993 +prompt_toolkit/layout/dimension.py,sha256=GOd3W6bxtH9tPaGo31brUwF94K2UOz-ldB_6rearjSI,6948 +prompt_toolkit/layout/dummy.py,sha256=8zB3MwDDd4RpI880WUKhv719tTzy5bXS9gm9zdkBZ10,1047 +prompt_toolkit/layout/layout.py,sha256=VXqWAoL3EviGn4CxtOrFJekMALvl9xff1bTSnE-gXF8,13960 +prompt_toolkit/layout/margins.py,sha256=bt-IvD03uQvmLVYvGZLqPLluR6kUlBRBAGJwCc8F7II,10375 +prompt_toolkit/layout/menus.py,sha256=B4H2oCPF48gLy9cB0vkdGIoH_8gGyj95TDHtfxXRVSw,27195 +prompt_toolkit/layout/mouse_handlers.py,sha256=lwbGSdpn6_pcK7HQWJ6IvHsxTf1_ahBew4pkmtU6zUM,1589 +prompt_toolkit/layout/processors.py,sha256=0VE4UIGRzyXvDO4XqCB7LXNG9WkSxLz7FW7toOvHDSE,34296 +prompt_toolkit/layout/screen.py,sha256=2PWdPDkQxtJrMSv9oqdZrWa7ChCnC7J4SvfVIithi5E,10113 +prompt_toolkit/layout/scrollable_pane.py,sha256=JQtPfafU61RJt3MzGW2wsw96o1sjJH0g2DSVyO7J6qA,19264 +prompt_toolkit/layout/utils.py,sha256=qot9clyeG3FoplfAS2O6QxmnnA_PDln4-dUJ5Hu76fQ,2371 +prompt_toolkit/lexers/__init__.py,sha256=QldV9b8J2Kb9Exyv2fDss-YRzP07z2FYAhwPN4coWn8,409 +prompt_toolkit/lexers/__pycache__/__init__.cpython-312.pyc,, +prompt_toolkit/lexers/__pycache__/base.cpython-312.pyc,, +prompt_toolkit/lexers/__pycache__/pygments.cpython-312.pyc,, +prompt_toolkit/lexers/base.py,sha256=XdyKLj4rD25CVCqSCfElWE3ppBN1LGQ9fRLPi1oYfl0,2350 +prompt_toolkit/lexers/pygments.py,sha256=it89LjsltZpzlQJPb95GX4GdMu7gq1J1QzWC29lCQi4,11922 +prompt_toolkit/log.py,sha256=6typpL_jnewdUc3j2OoplVLwnP9dSMOkIsJy_sgR9IY,153 +prompt_toolkit/mouse_events.py,sha256=4mUHJbG8WrrQznw7z_jsOrdmldC5ZMRM4gDDUy51pyk,2473 +prompt_toolkit/output/__init__.py,sha256=GVlT-U_W0EuIP-c1Qjyp0DN6Fl2PsCEhFzjUMRHsGWI,280 +prompt_toolkit/output/__pycache__/__init__.cpython-312.pyc,, +prompt_toolkit/output/__pycache__/base.cpython-312.pyc,, +prompt_toolkit/output/__pycache__/color_depth.cpython-312.pyc,, +prompt_toolkit/output/__pycache__/conemu.cpython-312.pyc,, +prompt_toolkit/output/__pycache__/defaults.cpython-312.pyc,, +prompt_toolkit/output/__pycache__/flush_stdout.cpython-312.pyc,, +prompt_toolkit/output/__pycache__/plain_text.cpython-312.pyc,, +prompt_toolkit/output/__pycache__/vt100.cpython-312.pyc,, +prompt_toolkit/output/__pycache__/win32.cpython-312.pyc,, +prompt_toolkit/output/__pycache__/windows10.cpython-312.pyc,, +prompt_toolkit/output/base.py,sha256=o74Vok7cXLxgHoAaqKHQAGcNZILn5B5g6Z0pUXU6x7s,8348 +prompt_toolkit/output/color_depth.py,sha256=KEFTlxCYTqOvA-VDx4wUb8G6HaYD5Hbf5GKmPZwssCs,1569 +prompt_toolkit/output/conemu.py,sha256=_w2IEFR-mXsaMFINgZITiJNRCS9QowLUxeskPEpz2GE,1865 +prompt_toolkit/output/defaults.py,sha256=72RecTuugrjvfZinbvsFRYDwMcczE9Zw3ttmmiG0Ivg,3689 +prompt_toolkit/output/flush_stdout.py,sha256=ReT0j0IwVJEcth7VJj2zE6UcY0OVz5Ut1rpANnbCyYQ,3236 +prompt_toolkit/output/plain_text.py,sha256=VnjoDmy0pKQoubXXQJQ_MljoDYi1FcLdNZB2KN_TQIs,3296 +prompt_toolkit/output/vt100.py,sha256=a_vswyv0w0AyWZoI5O2a13y9q0Fj3cxdK2wqwi2zS0U,23474 +prompt_toolkit/output/win32.py,sha256=_aM4-pJq91l2mFkpBtRHksQ3xaUjw_Jj0yVJdle1Vbo,22639 +prompt_toolkit/output/windows10.py,sha256=yf0i1xAs-mbqOCwq25K78hkJjju1jXZ5b0e-w9aSBBA,4362 +prompt_toolkit/patch_stdout.py,sha256=8gEaQdqykdBczlvp3FrOjDlEG02yeXoYKrDAGqj48Wg,9477 +prompt_toolkit/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +prompt_toolkit/renderer.py,sha256=h4r7bShanQyvh9nrSQfxvHZUPT6ZPUH1kD5Nbeu2RwY,29398 +prompt_toolkit/search.py,sha256=6Go_LtBeBlIMkdUCqb-WFCBKLchd70kgtccqP5dyv08,6951 +prompt_toolkit/selection.py,sha256=P6zQOahBqqt1YZmfQ2-V9iJjOo4cxl0bdmU_-0jezJI,1274 +prompt_toolkit/shortcuts/__init__.py,sha256=ENuc0MPbDtxMnPNhYKUeYDzp4_J_ycQ-aQDfgg7imRw,1020 +prompt_toolkit/shortcuts/__pycache__/__init__.cpython-312.pyc,, +prompt_toolkit/shortcuts/__pycache__/choice_input.cpython-312.pyc,, +prompt_toolkit/shortcuts/__pycache__/dialogs.cpython-312.pyc,, +prompt_toolkit/shortcuts/__pycache__/prompt.cpython-312.pyc,, +prompt_toolkit/shortcuts/__pycache__/utils.cpython-312.pyc,, +prompt_toolkit/shortcuts/choice_input.py,sha256=Jzs6y2x6BLo5Otetx3szDKks7ikLwqlx_gYhXYP7HWY,10523 +prompt_toolkit/shortcuts/dialogs.py,sha256=gFibLlbaii8ijuurk9TpbNi5fMTHu99T6m1wfFilbE8,9007 +prompt_toolkit/shortcuts/progress_bar/__init__.py,sha256=QeAssmFBDPCC5VRoObAp4UkebwETP3qS7-na4acstWM,540 +prompt_toolkit/shortcuts/progress_bar/__pycache__/__init__.cpython-312.pyc,, +prompt_toolkit/shortcuts/progress_bar/__pycache__/base.cpython-312.pyc,, +prompt_toolkit/shortcuts/progress_bar/__pycache__/formatters.cpython-312.pyc,, +prompt_toolkit/shortcuts/progress_bar/base.py,sha256=_cqp7coZMFDc7ZoAUL1iz3fL1Dt5hw3hi1HEfBvUpK8,14402 +prompt_toolkit/shortcuts/progress_bar/formatters.py,sha256=VfRADwUm8op-DzoM51UrKI8pSa1T1LAz5q9VMUW2siI,11739 +prompt_toolkit/shortcuts/prompt.py,sha256=JpGwxOW7nd0maObh8H4njxJ_PLALJozCiW_xCYBf8ug,60747 +prompt_toolkit/shortcuts/utils.py,sha256=NNjBY0Brkcb13Gxhh7Yc72_YpDFsQbkIlm7ZXvW3rK0,6950 +prompt_toolkit/styles/__init__.py,sha256=7N1NNE1gTQo5mjT9f7mRwRodkrBoNpT9pmqWK-lrSeY,1640 +prompt_toolkit/styles/__pycache__/__init__.cpython-312.pyc,, +prompt_toolkit/styles/__pycache__/base.cpython-312.pyc,, +prompt_toolkit/styles/__pycache__/defaults.cpython-312.pyc,, +prompt_toolkit/styles/__pycache__/named_colors.cpython-312.pyc,, +prompt_toolkit/styles/__pycache__/pygments.cpython-312.pyc,, +prompt_toolkit/styles/__pycache__/style.cpython-312.pyc,, +prompt_toolkit/styles/__pycache__/style_transformation.cpython-312.pyc,, +prompt_toolkit/styles/base.py,sha256=J0Q7q4Nyrxig6KVLMnxZ-Jh3HlbPSKRPDSgtHjGMx_U,5064 +prompt_toolkit/styles/defaults.py,sha256=TRnP1PeuauYa_Ru1PpJ_ImsfaldvLE1JjmPV8tvfJjs,8699 +prompt_toolkit/styles/named_colors.py,sha256=yZ30oKB-fCRk6RMASYg8q3Uz2zgdfy_YNbuQWYpyYas,4367 +prompt_toolkit/styles/pygments.py,sha256=yWJEcvYCFo1e2EN9IF5HWpxHQ104J0HOJg1LUsSA9oM,1974 +prompt_toolkit/styles/style.py,sha256=GXYGYLJolUiErIHmuxk5VuPgwehoV2l8Cvfdy9_GKc0,13263 +prompt_toolkit/styles/style_transformation.py,sha256=cGaOo-jqhP79QoEHLQxrOZo9QMrxWxtXgfXKsHlx1Jg,12427 +prompt_toolkit/token.py,sha256=do3EnxLrCDVbq47MzJ2vqSYps-CjVKWNCWzCZgdf5Jo,121 +prompt_toolkit/utils.py,sha256=7O8hILpI2VZb0KoC7J-5z1S2aXICf_kwtmRq5xdfDTg,8631 +prompt_toolkit/validation.py,sha256=XTdmExMgaqj-Whym9yYyQxOAaKce97KYyyGXwCxMr-A,5807 +prompt_toolkit/widgets/__init__.py,sha256=RZXj6UzZWFuxOQXc1TwHLIwwZYJU-YBAaV4oLrC2dCA,1218 +prompt_toolkit/widgets/__pycache__/__init__.cpython-312.pyc,, +prompt_toolkit/widgets/__pycache__/base.cpython-312.pyc,, +prompt_toolkit/widgets/__pycache__/dialogs.cpython-312.pyc,, +prompt_toolkit/widgets/__pycache__/menus.cpython-312.pyc,, +prompt_toolkit/widgets/__pycache__/toolbars.cpython-312.pyc,, +prompt_toolkit/widgets/base.py,sha256=MEqDogz8BCH7KEQEH0L6w9BndCFhsiaBhg0ZFm91VQo,35546 +prompt_toolkit/widgets/dialogs.py,sha256=K2ACcf0rKXwpBQGQcjSTq2aNeSInGmklzZRPnhdtZTc,3380 +prompt_toolkit/widgets/menus.py,sha256=SeX-llaTpF1pVak2lw37mAP0SFDONIRZT5oq23mARg8,13419 +prompt_toolkit/widgets/toolbars.py,sha256=MoxOxaa8Yi3nJvH4G8OCwlNuwx3XWUJ07J0a7D17_w0,12178 +prompt_toolkit/win32_types.py,sha256=3xVjabRA3Q-RN2x3DLqTOrstuYj4_uCq6w2i8t6LZ6E,5551 diff --git a/lib/python3.12/site-packages/prompt_toolkit-3.0.52.dist-info/WHEEL b/lib/python3.12/site-packages/prompt_toolkit-3.0.52.dist-info/WHEEL new file mode 100644 index 0000000000000000000000000000000000000000..e7fa31b6f3f78deb1022c1f7927f07d4d16da822 --- /dev/null +++ b/lib/python3.12/site-packages/prompt_toolkit-3.0.52.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: setuptools (80.9.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/lib/python3.12/site-packages/prompt_toolkit-3.0.52.dist-info/licenses/AUTHORS.rst b/lib/python3.12/site-packages/prompt_toolkit-3.0.52.dist-info/licenses/AUTHORS.rst new file mode 100644 index 0000000000000000000000000000000000000000..f7c8f60f4006dee49df0321c3786f9e413fa2cce --- /dev/null +++ b/lib/python3.12/site-packages/prompt_toolkit-3.0.52.dist-info/licenses/AUTHORS.rst @@ -0,0 +1,11 @@ +Authors +======= + +Creator +------- +Jonathan Slenders + +Contributors +------------ + +- Amjith Ramanujam diff --git a/lib/python3.12/site-packages/prompt_toolkit-3.0.52.dist-info/licenses/LICENSE b/lib/python3.12/site-packages/prompt_toolkit-3.0.52.dist-info/licenses/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..e1720e0fb70684043842e94ede48622e6bffc62d --- /dev/null +++ b/lib/python3.12/site-packages/prompt_toolkit-3.0.52.dist-info/licenses/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2014, Jonathan Slenders +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, this + list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + +* Neither the name of the {organization} nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/lib/python3.12/site-packages/prompt_toolkit-3.0.52.dist-info/top_level.txt b/lib/python3.12/site-packages/prompt_toolkit-3.0.52.dist-info/top_level.txt new file mode 100644 index 0000000000000000000000000000000000000000..29392dfc5b3128bc4ebb5ad6ef6d4f76c42e3e24 --- /dev/null +++ b/lib/python3.12/site-packages/prompt_toolkit-3.0.52.dist-info/top_level.txt @@ -0,0 +1 @@ +prompt_toolkit diff --git a/lib/python3.12/site-packages/ptyprocess/__init__.py b/lib/python3.12/site-packages/ptyprocess/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..3a6268e8a6d26c2ff6c5e695d39402cfc5f34584 --- /dev/null +++ b/lib/python3.12/site-packages/ptyprocess/__init__.py @@ -0,0 +1,4 @@ +"""Run a subprocess in a pseudo terminal""" +from .ptyprocess import PtyProcess, PtyProcessUnicode, PtyProcessError + +__version__ = '0.7.0' diff --git a/lib/python3.12/site-packages/ptyprocess/__pycache__/__init__.cpython-312.pyc b/lib/python3.12/site-packages/ptyprocess/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..128a2a5fcb052281baf7f53f44d720b57f8ee24d Binary files /dev/null and b/lib/python3.12/site-packages/ptyprocess/__pycache__/__init__.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/ptyprocess/__pycache__/_fork_pty.cpython-312.pyc b/lib/python3.12/site-packages/ptyprocess/__pycache__/_fork_pty.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..59e42466c693eae34f1982f14bf5b637af1ae5a6 Binary files /dev/null and b/lib/python3.12/site-packages/ptyprocess/__pycache__/_fork_pty.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/ptyprocess/__pycache__/ptyprocess.cpython-312.pyc b/lib/python3.12/site-packages/ptyprocess/__pycache__/ptyprocess.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bfe51e23fd9e47b22af6726d497d5a6b99244c1c Binary files /dev/null and b/lib/python3.12/site-packages/ptyprocess/__pycache__/ptyprocess.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/ptyprocess/__pycache__/util.cpython-312.pyc b/lib/python3.12/site-packages/ptyprocess/__pycache__/util.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e6d286a5ad2143966b0c9a5b774a87324498017c Binary files /dev/null and b/lib/python3.12/site-packages/ptyprocess/__pycache__/util.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/ptyprocess/_fork_pty.py b/lib/python3.12/site-packages/ptyprocess/_fork_pty.py new file mode 100644 index 0000000000000000000000000000000000000000..a8d05fe5a3d1a7ea2159a9a4fabb805966245a75 --- /dev/null +++ b/lib/python3.12/site-packages/ptyprocess/_fork_pty.py @@ -0,0 +1,78 @@ +"""Substitute for the forkpty system call, to support Solaris. +""" +import os +import errno + +from pty import (STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO, CHILD) +from .util import PtyProcessError + +def fork_pty(): + '''This implements a substitute for the forkpty system call. This + should be more portable than the pty.fork() function. Specifically, + this should work on Solaris. + + Modified 10.06.05 by Geoff Marshall: Implemented __fork_pty() method to + resolve the issue with Python's pty.fork() not supporting Solaris, + particularly ssh. Based on patch to posixmodule.c authored by Noah + Spurrier:: + + http://mail.python.org/pipermail/python-dev/2003-May/035281.html + + ''' + + parent_fd, child_fd = os.openpty() + if parent_fd < 0 or child_fd < 0: + raise OSError("os.openpty() failed") + + pid = os.fork() + if pid == CHILD: + # Child. + os.close(parent_fd) + pty_make_controlling_tty(child_fd) + + os.dup2(child_fd, STDIN_FILENO) + os.dup2(child_fd, STDOUT_FILENO) + os.dup2(child_fd, STDERR_FILENO) + + else: + # Parent. + os.close(child_fd) + + return pid, parent_fd + +def pty_make_controlling_tty(tty_fd): + '''This makes the pseudo-terminal the controlling tty. This should be + more portable than the pty.fork() function. Specifically, this should + work on Solaris. ''' + + child_name = os.ttyname(tty_fd) + + # Disconnect from controlling tty, if any. Raises OSError of ENXIO + # if there was no controlling tty to begin with, such as when + # executed by a cron(1) job. + try: + fd = os.open("/dev/tty", os.O_RDWR | os.O_NOCTTY) + os.close(fd) + except OSError as err: + if err.errno != errno.ENXIO: + raise + + os.setsid() + + # Verify we are disconnected from controlling tty by attempting to open + # it again. We expect that OSError of ENXIO should always be raised. + try: + fd = os.open("/dev/tty", os.O_RDWR | os.O_NOCTTY) + os.close(fd) + raise PtyProcessError("OSError of errno.ENXIO should be raised.") + except OSError as err: + if err.errno != errno.ENXIO: + raise + + # Verify we can open child pty. + fd = os.open(child_name, os.O_RDWR) + os.close(fd) + + # Verify we now have a controlling tty. + fd = os.open("/dev/tty", os.O_WRONLY) + os.close(fd) diff --git a/lib/python3.12/site-packages/ptyprocess/ptyprocess.py b/lib/python3.12/site-packages/ptyprocess/ptyprocess.py new file mode 100644 index 0000000000000000000000000000000000000000..78d19fdf8fd840c0debff6da7ad2440928c760a5 --- /dev/null +++ b/lib/python3.12/site-packages/ptyprocess/ptyprocess.py @@ -0,0 +1,842 @@ +import codecs +import errno +import fcntl +import io +import os +import pty +import resource +import signal +import struct +import sys +import termios +import time + +try: + import builtins # Python 3 +except ImportError: + import __builtin__ as builtins # Python 2 + +# Constants +from pty import (STDIN_FILENO, CHILD) + +from .util import which, PtyProcessError + +_platform = sys.platform.lower() + +# Solaris uses internal __fork_pty(). All others use pty.fork(). +_is_solaris = ( + _platform.startswith('solaris') or + _platform.startswith('sunos')) + +if _is_solaris: + use_native_pty_fork = False + from . import _fork_pty +else: + use_native_pty_fork = True + +PY3 = sys.version_info[0] >= 3 + +if PY3: + def _byte(i): + return bytes([i]) +else: + def _byte(i): + return chr(i) + + class FileNotFoundError(OSError): pass + class TimeoutError(OSError): pass + +_EOF, _INTR = None, None + +def _make_eof_intr(): + """Set constants _EOF and _INTR. + + This avoids doing potentially costly operations on module load. + """ + global _EOF, _INTR + if (_EOF is not None) and (_INTR is not None): + return + + # inherit EOF and INTR definitions from controlling process. + try: + from termios import VEOF, VINTR + fd = None + for name in 'stdin', 'stdout': + stream = getattr(sys, '__%s__' % name, None) + if stream is None or not hasattr(stream, 'fileno'): + continue + try: + fd = stream.fileno() + except ValueError: + continue + if fd is None: + # no fd, raise ValueError to fallback on CEOF, CINTR + raise ValueError("No stream has a fileno") + intr = ord(termios.tcgetattr(fd)[6][VINTR]) + eof = ord(termios.tcgetattr(fd)[6][VEOF]) + except (ImportError, OSError, IOError, ValueError, termios.error): + # unless the controlling process is also not a terminal, + # such as cron(1), or when stdin and stdout are both closed. + # Fall-back to using CEOF and CINTR. There + try: + from termios import CEOF, CINTR + (intr, eof) = (CINTR, CEOF) + except ImportError: + # ^C, ^D + (intr, eof) = (3, 4) + + _INTR = _byte(intr) + _EOF = _byte(eof) + +# setecho and setwinsize are pulled out here because on some platforms, we need +# to do this from the child before we exec() + +def _setecho(fd, state): + errmsg = 'setecho() may not be called on this platform (it may still be possible to enable/disable echo when spawning the child process)' + + try: + attr = termios.tcgetattr(fd) + except termios.error as err: + if err.args[0] == errno.EINVAL: + raise IOError(err.args[0], '%s: %s.' % (err.args[1], errmsg)) + raise + + if state: + attr[3] = attr[3] | termios.ECHO + else: + attr[3] = attr[3] & ~termios.ECHO + + try: + # I tried TCSADRAIN and TCSAFLUSH, but these were inconsistent and + # blocked on some platforms. TCSADRAIN would probably be ideal. + termios.tcsetattr(fd, termios.TCSANOW, attr) + except IOError as err: + if err.args[0] == errno.EINVAL: + raise IOError(err.args[0], '%s: %s.' % (err.args[1], errmsg)) + raise + +def _setwinsize(fd, rows, cols): + # Some very old platforms have a bug that causes the value for + # termios.TIOCSWINSZ to be truncated. There was a hack here to work + # around this, but it caused problems with newer platforms so has been + # removed. For details see https://github.com/pexpect/pexpect/issues/39 + TIOCSWINSZ = getattr(termios, 'TIOCSWINSZ', -2146929561) + # Note, assume ws_xpixel and ws_ypixel are zero. + s = struct.pack('HHHH', rows, cols, 0, 0) + fcntl.ioctl(fd, TIOCSWINSZ, s) + +class PtyProcess(object): + '''This class represents a process running in a pseudoterminal. + + The main constructor is the :meth:`spawn` classmethod. + ''' + string_type = bytes + if PY3: + linesep = os.linesep.encode('ascii') + crlf = '\r\n'.encode('ascii') + + @staticmethod + def write_to_stdout(b): + try: + return sys.stdout.buffer.write(b) + except AttributeError: + # If stdout has been replaced, it may not have .buffer + return sys.stdout.write(b.decode('ascii', 'replace')) + else: + linesep = os.linesep + crlf = '\r\n' + write_to_stdout = sys.stdout.write + + encoding = None + + argv = None + env = None + launch_dir = None + + def __init__(self, pid, fd): + _make_eof_intr() # Ensure _EOF and _INTR are calculated + self.pid = pid + self.fd = fd + readf = io.open(fd, 'rb', buffering=0) + writef = io.open(fd, 'wb', buffering=0, closefd=False) + self.fileobj = io.BufferedRWPair(readf, writef) + + self.terminated = False + self.closed = False + self.exitstatus = None + self.signalstatus = None + # status returned by os.waitpid + self.status = None + self.flag_eof = False + # Used by close() to give kernel time to update process status. + # Time in seconds. + self.delayafterclose = 0.1 + # Used by terminate() to give kernel time to update process status. + # Time in seconds. + self.delayafterterminate = 0.1 + + @classmethod + def spawn( + cls, argv, cwd=None, env=None, echo=True, preexec_fn=None, + dimensions=(24, 80), pass_fds=()): + '''Start the given command in a child process in a pseudo terminal. + + This does all the fork/exec type of stuff for a pty, and returns an + instance of PtyProcess. + + If preexec_fn is supplied, it will be called with no arguments in the + child process before exec-ing the specified command. + It may, for instance, set signal handlers to SIG_DFL or SIG_IGN. + + Dimensions of the psuedoterminal used for the subprocess can be + specified as a tuple (rows, cols), or the default (24, 80) will be used. + + By default, all file descriptors except 0, 1 and 2 are closed. This + behavior can be overridden with pass_fds, a list of file descriptors to + keep open between the parent and the child. + ''' + # Note that it is difficult for this method to fail. + # You cannot detect if the child process cannot start. + # So the only way you can tell if the child process started + # or not is to try to read from the file descriptor. If you get + # EOF immediately then it means that the child is already dead. + # That may not necessarily be bad because you may have spawned a child + # that performs some task; creates no stdout output; and then dies. + + if not isinstance(argv, (list, tuple)): + raise TypeError("Expected a list or tuple for argv, got %r" % argv) + + # Shallow copy of argv so we can modify it + argv = argv[:] + command = argv[0] + + command_with_path = which(command) + if command_with_path is None: + raise FileNotFoundError('The command was not found or was not ' + + 'executable: %s.' % command) + command = command_with_path + argv[0] = command + + # [issue #119] To prevent the case where exec fails and the user is + # stuck interacting with a python child process instead of whatever + # was expected, we implement the solution from + # http://stackoverflow.com/a/3703179 to pass the exception to the + # parent process + + # [issue #119] 1. Before forking, open a pipe in the parent process. + exec_err_pipe_read, exec_err_pipe_write = os.pipe() + + if use_native_pty_fork: + pid, fd = pty.fork() + else: + # Use internal fork_pty, for Solaris + pid, fd = _fork_pty.fork_pty() + + # Some platforms must call setwinsize() and setecho() from the + # child process, and others from the master process. We do both, + # allowing IOError for either. + + if pid == CHILD: + # set window size + try: + _setwinsize(STDIN_FILENO, *dimensions) + except IOError as err: + if err.args[0] not in (errno.EINVAL, errno.ENOTTY): + raise + + # disable echo if spawn argument echo was unset + if not echo: + try: + _setecho(STDIN_FILENO, False) + except (IOError, termios.error) as err: + if err.args[0] not in (errno.EINVAL, errno.ENOTTY): + raise + + # [issue #119] 3. The child closes the reading end and sets the + # close-on-exec flag for the writing end. + os.close(exec_err_pipe_read) + fcntl.fcntl(exec_err_pipe_write, fcntl.F_SETFD, fcntl.FD_CLOEXEC) + + # Do not allow child to inherit open file descriptors from parent, + # with the exception of the exec_err_pipe_write of the pipe + # and pass_fds. + # Impose ceiling on max_fd: AIX bugfix for users with unlimited + # nofiles where resource.RLIMIT_NOFILE is 2^63-1 and os.closerange() + # occasionally raises out of range error + max_fd = min(1048576, resource.getrlimit(resource.RLIMIT_NOFILE)[0]) + spass_fds = sorted(set(pass_fds) | {exec_err_pipe_write}) + for pair in zip([2] + spass_fds, spass_fds + [max_fd]): + os.closerange(pair[0]+1, pair[1]) + + if cwd is not None: + os.chdir(cwd) + + if preexec_fn is not None: + try: + preexec_fn() + except Exception as e: + ename = type(e).__name__ + tosend = '{}:0:{}'.format(ename, str(e)) + if PY3: + tosend = tosend.encode('utf-8') + + os.write(exec_err_pipe_write, tosend) + os.close(exec_err_pipe_write) + os._exit(1) + + try: + if env is None: + os.execv(command, argv) + else: + os.execvpe(command, argv, env) + except OSError as err: + # [issue #119] 5. If exec fails, the child writes the error + # code back to the parent using the pipe, then exits. + tosend = 'OSError:{}:{}'.format(err.errno, str(err)) + if PY3: + tosend = tosend.encode('utf-8') + os.write(exec_err_pipe_write, tosend) + os.close(exec_err_pipe_write) + os._exit(os.EX_OSERR) + + # Parent + inst = cls(pid, fd) + + # Set some informational attributes + inst.argv = argv + if env is not None: + inst.env = env + if cwd is not None: + inst.launch_dir = cwd + + # [issue #119] 2. After forking, the parent closes the writing end + # of the pipe and reads from the reading end. + os.close(exec_err_pipe_write) + exec_err_data = os.read(exec_err_pipe_read, 4096) + os.close(exec_err_pipe_read) + + # [issue #119] 6. The parent reads eof (a zero-length read) if the + # child successfully performed exec, since close-on-exec made + # successful exec close the writing end of the pipe. Or, if exec + # failed, the parent reads the error code and can proceed + # accordingly. Either way, the parent blocks until the child calls + # exec. + if len(exec_err_data) != 0: + try: + errclass, errno_s, errmsg = exec_err_data.split(b':', 2) + exctype = getattr(builtins, errclass.decode('ascii'), Exception) + + exception = exctype(errmsg.decode('utf-8', 'replace')) + if exctype is OSError: + exception.errno = int(errno_s) + except: + raise Exception('Subprocess failed, got bad error data: %r' + % exec_err_data) + else: + raise exception + + try: + inst.setwinsize(*dimensions) + except IOError as err: + if err.args[0] not in (errno.EINVAL, errno.ENOTTY, errno.ENXIO): + raise + + return inst + + def __repr__(self): + clsname = type(self).__name__ + if self.argv is not None: + args = [repr(self.argv)] + if self.env is not None: + args.append("env=%r" % self.env) + if self.launch_dir is not None: + args.append("cwd=%r" % self.launch_dir) + + return "{}.spawn({})".format(clsname, ", ".join(args)) + + else: + return "{}(pid={}, fd={})".format(clsname, self.pid, self.fd) + + @staticmethod + def _coerce_send_string(s): + if not isinstance(s, bytes): + return s.encode('utf-8') + return s + + @staticmethod + def _coerce_read_string(s): + return s + + def __del__(self): + '''This makes sure that no system resources are left open. Python only + garbage collects Python objects. OS file descriptors are not Python + objects, so they must be handled explicitly. If the child file + descriptor was opened outside of this class (passed to the constructor) + then this does not close it. ''' + + if not self.closed: + # It is possible for __del__ methods to execute during the + # teardown of the Python VM itself. Thus self.close() may + # trigger an exception because os.close may be None. + try: + self.close() + # which exception, shouldn't we catch explicitly .. ? + except: + pass + + + def fileno(self): + '''This returns the file descriptor of the pty for the child. + ''' + return self.fd + + def close(self, force=True): + '''This closes the connection with the child application. Note that + calling close() more than once is valid. This emulates standard Python + behavior with files. Set force to True if you want to make sure that + the child is terminated (SIGKILL is sent if the child ignores SIGHUP + and SIGINT). ''' + if not self.closed: + self.flush() + self.fileobj.close() # Closes the file descriptor + # Give kernel time to update process status. + time.sleep(self.delayafterclose) + if self.isalive(): + if not self.terminate(force): + raise PtyProcessError('Could not terminate the child.') + self.fd = -1 + self.closed = True + #self.pid = None + + def flush(self): + '''This does nothing. It is here to support the interface for a + File-like object. ''' + + pass + + def isatty(self): + '''This returns True if the file descriptor is open and connected to a + tty(-like) device, else False. + + On SVR4-style platforms implementing streams, such as SunOS and HP-UX, + the child pty may not appear as a terminal device. This means + methods such as setecho(), setwinsize(), getwinsize() may raise an + IOError. ''' + + return os.isatty(self.fd) + + def waitnoecho(self, timeout=None): + '''This waits until the terminal ECHO flag is set False. This returns + True if the echo mode is off. This returns False if the ECHO flag was + not set False before the timeout. This can be used to detect when the + child is waiting for a password. Usually a child application will turn + off echo mode when it is waiting for the user to enter a password. For + example, instead of expecting the "password:" prompt you can wait for + the child to set ECHO off:: + + p = pexpect.spawn('ssh user@example.com') + p.waitnoecho() + p.sendline(mypassword) + + If timeout==None then this method to block until ECHO flag is False. + ''' + + if timeout is not None: + end_time = time.time() + timeout + while True: + if not self.getecho(): + return True + if timeout < 0 and timeout is not None: + return False + if timeout is not None: + timeout = end_time - time.time() + time.sleep(0.1) + + def getecho(self): + '''This returns the terminal echo mode. This returns True if echo is + on or False if echo is off. Child applications that are expecting you + to enter a password often set ECHO False. See waitnoecho(). + + Not supported on platforms where ``isatty()`` returns False. ''' + + try: + attr = termios.tcgetattr(self.fd) + except termios.error as err: + errmsg = 'getecho() may not be called on this platform' + if err.args[0] == errno.EINVAL: + raise IOError(err.args[0], '%s: %s.' % (err.args[1], errmsg)) + raise + + self.echo = bool(attr[3] & termios.ECHO) + return self.echo + + def setecho(self, state): + '''This sets the terminal echo mode on or off. Note that anything the + child sent before the echo will be lost, so you should be sure that + your input buffer is empty before you call setecho(). For example, the + following will work as expected:: + + p = pexpect.spawn('cat') # Echo is on by default. + p.sendline('1234') # We expect see this twice from the child... + p.expect(['1234']) # ... once from the tty echo... + p.expect(['1234']) # ... and again from cat itself. + p.setecho(False) # Turn off tty echo + p.sendline('abcd') # We will set this only once (echoed by cat). + p.sendline('wxyz') # We will set this only once (echoed by cat) + p.expect(['abcd']) + p.expect(['wxyz']) + + The following WILL NOT WORK because the lines sent before the setecho + will be lost:: + + p = pexpect.spawn('cat') + p.sendline('1234') + p.setecho(False) # Turn off tty echo + p.sendline('abcd') # We will set this only once (echoed by cat). + p.sendline('wxyz') # We will set this only once (echoed by cat) + p.expect(['1234']) + p.expect(['1234']) + p.expect(['abcd']) + p.expect(['wxyz']) + + + Not supported on platforms where ``isatty()`` returns False. + ''' + _setecho(self.fd, state) + + self.echo = state + + def read(self, size=1024): + """Read and return at most ``size`` bytes from the pty. + + Can block if there is nothing to read. Raises :exc:`EOFError` if the + terminal was closed. + + Unlike Pexpect's ``read_nonblocking`` method, this doesn't try to deal + with the vagaries of EOF on platforms that do strange things, like IRIX + or older Solaris systems. It handles the errno=EIO pattern used on + Linux, and the empty-string return used on BSD platforms and (seemingly) + on recent Solaris. + """ + try: + s = self.fileobj.read1(size) + except (OSError, IOError) as err: + if err.args[0] == errno.EIO: + # Linux-style EOF + self.flag_eof = True + raise EOFError('End Of File (EOF). Exception style platform.') + raise + if s == b'': + # BSD-style EOF (also appears to work on recent Solaris (OpenIndiana)) + self.flag_eof = True + raise EOFError('End Of File (EOF). Empty string style platform.') + + return s + + def readline(self): + """Read one line from the pseudoterminal, and return it as unicode. + + Can block if there is nothing to read. Raises :exc:`EOFError` if the + terminal was closed. + """ + try: + s = self.fileobj.readline() + except (OSError, IOError) as err: + if err.args[0] == errno.EIO: + # Linux-style EOF + self.flag_eof = True + raise EOFError('End Of File (EOF). Exception style platform.') + raise + if s == b'': + # BSD-style EOF (also appears to work on recent Solaris (OpenIndiana)) + self.flag_eof = True + raise EOFError('End Of File (EOF). Empty string style platform.') + + return s + + def _writeb(self, b, flush=True): + n = self.fileobj.write(b) + if flush: + self.fileobj.flush() + return n + + def write(self, s, flush=True): + """Write bytes to the pseudoterminal. + + Returns the number of bytes written. + """ + return self._writeb(s, flush=flush) + + def sendcontrol(self, char): + '''Helper method that wraps send() with mnemonic access for sending control + character to the child (such as Ctrl-C or Ctrl-D). For example, to send + Ctrl-G (ASCII 7, bell, '\a'):: + + child.sendcontrol('g') + + See also, sendintr() and sendeof(). + ''' + char = char.lower() + a = ord(char) + if 97 <= a <= 122: + a = a - ord('a') + 1 + byte = _byte(a) + return self._writeb(byte), byte + d = {'@': 0, '`': 0, + '[': 27, '{': 27, + '\\': 28, '|': 28, + ']': 29, '}': 29, + '^': 30, '~': 30, + '_': 31, + '?': 127} + if char not in d: + return 0, b'' + + byte = _byte(d[char]) + return self._writeb(byte), byte + + def sendeof(self): + '''This sends an EOF to the child. This sends a character which causes + the pending parent output buffer to be sent to the waiting child + program without waiting for end-of-line. If it is the first character + of the line, the read() in the user program returns 0, which signifies + end-of-file. This means to work as expected a sendeof() has to be + called at the beginning of a line. This method does not send a newline. + It is the responsibility of the caller to ensure the eof is sent at the + beginning of a line. ''' + + return self._writeb(_EOF), _EOF + + def sendintr(self): + '''This sends a SIGINT to the child. It does not require + the SIGINT to be the first character on a line. ''' + + return self._writeb(_INTR), _INTR + + def eof(self): + '''This returns True if the EOF exception was ever raised. + ''' + + return self.flag_eof + + def terminate(self, force=False): + '''This forces a child process to terminate. It starts nicely with + SIGHUP and SIGINT. If "force" is True then moves onto SIGKILL. This + returns True if the child was terminated. This returns False if the + child could not be terminated. ''' + + if not self.isalive(): + return True + try: + self.kill(signal.SIGHUP) + time.sleep(self.delayafterterminate) + if not self.isalive(): + return True + self.kill(signal.SIGCONT) + time.sleep(self.delayafterterminate) + if not self.isalive(): + return True + self.kill(signal.SIGINT) + time.sleep(self.delayafterterminate) + if not self.isalive(): + return True + if force: + self.kill(signal.SIGKILL) + time.sleep(self.delayafterterminate) + if not self.isalive(): + return True + else: + return False + return False + except OSError: + # I think there are kernel timing issues that sometimes cause + # this to happen. I think isalive() reports True, but the + # process is dead to the kernel. + # Make one last attempt to see if the kernel is up to date. + time.sleep(self.delayafterterminate) + if not self.isalive(): + return True + else: + return False + + def wait(self): + '''This waits until the child exits. This is a blocking call. This will + not read any data from the child, so this will block forever if the + child has unread output and has terminated. In other words, the child + may have printed output then called exit(), but, the child is + technically still alive until its output is read by the parent. ''' + + if self.isalive(): + pid, status = os.waitpid(self.pid, 0) + else: + return self.exitstatus + self.exitstatus = os.WEXITSTATUS(status) + if os.WIFEXITED(status): + self.status = status + self.exitstatus = os.WEXITSTATUS(status) + self.signalstatus = None + self.terminated = True + elif os.WIFSIGNALED(status): + self.status = status + self.exitstatus = None + self.signalstatus = os.WTERMSIG(status) + self.terminated = True + elif os.WIFSTOPPED(status): # pragma: no cover + # You can't call wait() on a child process in the stopped state. + raise PtyProcessError('Called wait() on a stopped child ' + + 'process. This is not supported. Is some other ' + + 'process attempting job control with our child pid?') + return self.exitstatus + + def isalive(self): + '''This tests if the child process is running or not. This is + non-blocking. If the child was terminated then this will read the + exitstatus or signalstatus of the child. This returns True if the child + process appears to be running or False if not. It can take literally + SECONDS for Solaris to return the right status. ''' + + if self.terminated: + return False + + if self.flag_eof: + # This is for Linux, which requires the blocking form + # of waitpid to get the status of a defunct process. + # This is super-lame. The flag_eof would have been set + # in read_nonblocking(), so this should be safe. + waitpid_options = 0 + else: + waitpid_options = os.WNOHANG + + try: + pid, status = os.waitpid(self.pid, waitpid_options) + except OSError as e: + # No child processes + if e.errno == errno.ECHILD: + raise PtyProcessError('isalive() encountered condition ' + + 'where "terminated" is 0, but there was no child ' + + 'process. Did someone else call waitpid() ' + + 'on our process?') + else: + raise + + # I have to do this twice for Solaris. + # I can't even believe that I figured this out... + # If waitpid() returns 0 it means that no child process + # wishes to report, and the value of status is undefined. + if pid == 0: + try: + ### os.WNOHANG) # Solaris! + pid, status = os.waitpid(self.pid, waitpid_options) + except OSError as e: # pragma: no cover + # This should never happen... + if e.errno == errno.ECHILD: + raise PtyProcessError('isalive() encountered condition ' + + 'that should never happen. There was no child ' + + 'process. Did someone else call waitpid() ' + + 'on our process?') + else: + raise + + # If pid is still 0 after two calls to waitpid() then the process + # really is alive. This seems to work on all platforms, except for + # Irix which seems to require a blocking call on waitpid or select, + # so I let read_nonblocking take care of this situation + # (unfortunately, this requires waiting through the timeout). + if pid == 0: + return True + + if pid == 0: + return True + + if os.WIFEXITED(status): + self.status = status + self.exitstatus = os.WEXITSTATUS(status) + self.signalstatus = None + self.terminated = True + elif os.WIFSIGNALED(status): + self.status = status + self.exitstatus = None + self.signalstatus = os.WTERMSIG(status) + self.terminated = True + elif os.WIFSTOPPED(status): + raise PtyProcessError('isalive() encountered condition ' + + 'where child process is stopped. This is not ' + + 'supported. Is some other process attempting ' + + 'job control with our child pid?') + return False + + def kill(self, sig): + """Send the given signal to the child application. + + In keeping with UNIX tradition it has a misleading name. It does not + necessarily kill the child unless you send the right signal. See the + :mod:`signal` module for constants representing signal numbers. + """ + + # Same as os.kill, but the pid is given for you. + if self.isalive(): + os.kill(self.pid, sig) + + def getwinsize(self): + """Return the window size of the pseudoterminal as a tuple (rows, cols). + """ + TIOCGWINSZ = getattr(termios, 'TIOCGWINSZ', 1074295912) + s = struct.pack('HHHH', 0, 0, 0, 0) + x = fcntl.ioctl(self.fd, TIOCGWINSZ, s) + return struct.unpack('HHHH', x)[0:2] + + def setwinsize(self, rows, cols): + """Set the terminal window size of the child tty. + + This will cause a SIGWINCH signal to be sent to the child. This does not + change the physical window size. It changes the size reported to + TTY-aware applications like vi or curses -- applications that respond to + the SIGWINCH signal. + """ + return _setwinsize(self.fd, rows, cols) + + +class PtyProcessUnicode(PtyProcess): + """Unicode wrapper around a process running in a pseudoterminal. + + This class exposes a similar interface to :class:`PtyProcess`, but its read + methods return unicode, and its :meth:`write` accepts unicode. + """ + if PY3: + string_type = str + else: + string_type = unicode # analysis:ignore + + def __init__(self, pid, fd, encoding='utf-8', codec_errors='strict'): + super(PtyProcessUnicode, self).__init__(pid, fd) + self.encoding = encoding + self.codec_errors = codec_errors + self.decoder = codecs.getincrementaldecoder(encoding)(errors=codec_errors) + + def read(self, size=1024): + """Read at most ``size`` bytes from the pty, return them as unicode. + + Can block if there is nothing to read. Raises :exc:`EOFError` if the + terminal was closed. + + The size argument still refers to bytes, not unicode code points. + """ + b = super(PtyProcessUnicode, self).read(size) + return self.decoder.decode(b, final=False) + + def readline(self): + """Read one line from the pseudoterminal, and return it as unicode. + + Can block if there is nothing to read. Raises :exc:`EOFError` if the + terminal was closed. + """ + b = super(PtyProcessUnicode, self).readline() + return self.decoder.decode(b, final=False) + + def write(self, s): + """Write the unicode string ``s`` to the pseudoterminal. + + Returns the number of bytes written. + """ + b = s.encode(self.encoding) + return super(PtyProcessUnicode, self).write(b) diff --git a/lib/python3.12/site-packages/ptyprocess/util.py b/lib/python3.12/site-packages/ptyprocess/util.py new file mode 100644 index 0000000000000000000000000000000000000000..aadbd62c801dc98425c2297cc4c6f12639bc2d5e --- /dev/null +++ b/lib/python3.12/site-packages/ptyprocess/util.py @@ -0,0 +1,71 @@ +try: + from shutil import which # Python >= 3.3 +except ImportError: + import os, sys + + # This is copied from Python 3.4.1 + def which(cmd, mode=os.F_OK | os.X_OK, path=None): + """Given a command, mode, and a PATH string, return the path which + conforms to the given mode on the PATH, or None if there is no such + file. + + `mode` defaults to os.F_OK | os.X_OK. `path` defaults to the result + of os.environ.get("PATH"), or can be overridden with a custom search + path. + + """ + # Check that a given file can be accessed with the correct mode. + # Additionally check that `file` is not a directory, as on Windows + # directories pass the os.access check. + def _access_check(fn, mode): + return (os.path.exists(fn) and os.access(fn, mode) + and not os.path.isdir(fn)) + + # If we're given a path with a directory part, look it up directly rather + # than referring to PATH directories. This includes checking relative to the + # current directory, e.g. ./script + if os.path.dirname(cmd): + if _access_check(cmd, mode): + return cmd + return None + + if path is None: + path = os.environ.get("PATH", os.defpath) + if not path: + return None + path = path.split(os.pathsep) + + if sys.platform == "win32": + # The current directory takes precedence on Windows. + if not os.curdir in path: + path.insert(0, os.curdir) + + # PATHEXT is necessary to check on Windows. + pathext = os.environ.get("PATHEXT", "").split(os.pathsep) + # See if the given file matches any of the expected path extensions. + # This will allow us to short circuit when given "python.exe". + # If it does match, only test that one, otherwise we have to try + # others. + if any(cmd.lower().endswith(ext.lower()) for ext in pathext): + files = [cmd] + else: + files = [cmd + ext for ext in pathext] + else: + # On other platforms you don't have things like PATHEXT to tell you + # what file suffixes are executable, so just pass on cmd as-is. + files = [cmd] + + seen = set() + for dir in path: + normdir = os.path.normcase(dir) + if not normdir in seen: + seen.add(normdir) + for thefile in files: + name = os.path.join(dir, thefile) + if _access_check(name, mode): + return name + return None + + +class PtyProcessError(Exception): + """Generic error class for this package.""" diff --git a/lib/python3.12/site-packages/referencing-0.37.0.dist-info/INSTALLER b/lib/python3.12/site-packages/referencing-0.37.0.dist-info/INSTALLER new file mode 100644 index 0000000000000000000000000000000000000000..a1b589e38a32041e49332e5e81c2d363dc418d68 --- /dev/null +++ b/lib/python3.12/site-packages/referencing-0.37.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/lib/python3.12/site-packages/referencing-0.37.0.dist-info/METADATA b/lib/python3.12/site-packages/referencing-0.37.0.dist-info/METADATA new file mode 100644 index 0000000000000000000000000000000000000000..9a6abbc83beb35659b354df6e9951043c6b10b42 --- /dev/null +++ b/lib/python3.12/site-packages/referencing-0.37.0.dist-info/METADATA @@ -0,0 +1,64 @@ +Metadata-Version: 2.4 +Name: referencing +Version: 0.37.0 +Summary: JSON Referencing + Python +Project-URL: Documentation, https://referencing.readthedocs.io/ +Project-URL: Homepage, https://github.com/python-jsonschema/referencing +Project-URL: Issues, https://github.com/python-jsonschema/referencing/issues/ +Project-URL: Funding, https://github.com/sponsors/Julian +Project-URL: Tidelift, https://tidelift.com/subscription/pkg/pypi-referencing?utm_source=pypi-referencing&utm_medium=referral&utm_campaign=pypi-link +Project-URL: Changelog, https://referencing.readthedocs.io/en/stable/changes/ +Project-URL: Source, https://github.com/python-jsonschema/referencing +Author-email: Julian Berman +License-Expression: MIT +License-File: COPYING +Keywords: asyncapi,json,jsonschema,openapi,referencing +Classifier: Development Status :: 3 - Alpha +Classifier: Intended Audience :: Developers +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +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.14 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: File Formats :: JSON +Classifier: Topic :: File Formats :: JSON :: JSON Schema +Requires-Python: >=3.10 +Requires-Dist: attrs>=22.2.0 +Requires-Dist: rpds-py>=0.7.0 +Requires-Dist: typing-extensions>=4.4.0; python_version < '3.13' +Description-Content-Type: text/x-rst + +=============== +``referencing`` +=============== + +|PyPI| |Pythons| |CI| |ReadTheDocs| |pre-commit| + +.. |PyPI| image:: https://img.shields.io/pypi/v/referencing.svg + :alt: PyPI version + :target: https://pypi.org/project/referencing/ + +.. |Pythons| image:: https://img.shields.io/pypi/pyversions/referencing.svg + :alt: Supported Python versions + :target: https://pypi.org/project/referencing/ + +.. |CI| image:: https://github.com/python-jsonschema/referencing/workflows/CI/badge.svg + :alt: Build status + :target: https://github.com/python-jsonschema/referencing/actions?query=workflow%3ACI + +.. |ReadTheDocs| image:: https://readthedocs.org/projects/referencing/badge/?version=stable&style=flat + :alt: ReadTheDocs status + :target: https://referencing.readthedocs.io/en/stable/ + +.. |pre-commit| image:: https://results.pre-commit.ci/badge/github/python-jsonschema/referencing/main.svg + :alt: pre-commit.ci status + :target: https://results.pre-commit.ci/latest/github/python-jsonschema/referencing/main + + +An implementation-agnostic implementation of JSON reference resolution. + +See `the documentation `_ for more details. diff --git a/lib/python3.12/site-packages/referencing-0.37.0.dist-info/RECORD b/lib/python3.12/site-packages/referencing-0.37.0.dist-info/RECORD new file mode 100644 index 0000000000000000000000000000000000000000..9dc99ade049bf567de8f74434df712dba2140970 --- /dev/null +++ b/lib/python3.12/site-packages/referencing-0.37.0.dist-info/RECORD @@ -0,0 +1,33 @@ +referencing-0.37.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +referencing-0.37.0.dist-info/METADATA,sha256=yTsA5qsVJP1ghIN2YEwYmhFDdHdkdcJaMdV75Hsp3mo,2845 +referencing-0.37.0.dist-info/RECORD,, +referencing-0.37.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87 +referencing-0.37.0.dist-info/licenses/COPYING,sha256=QtzWNJX4e063x3V6-jebtVpT-Ur9el9lfZrfVyNuUVw,1057 +referencing/__init__.py,sha256=5IZKXaAH_FWyCJRkaTn1XcptLfg9cveLb9u5nYUxJKs,207 +referencing/__pycache__/__init__.cpython-312.pyc,, +referencing/__pycache__/_attrs.cpython-312.pyc,, +referencing/__pycache__/_core.cpython-312.pyc,, +referencing/__pycache__/exceptions.cpython-312.pyc,, +referencing/__pycache__/jsonschema.cpython-312.pyc,, +referencing/__pycache__/retrieval.cpython-312.pyc,, +referencing/__pycache__/typing.cpython-312.pyc,, +referencing/_attrs.py,sha256=bgT-KMhDVLeGtWxM_SGKYeLaZBFzT2kUVFdAkOcXi8g,791 +referencing/_attrs.pyi,sha256=g2wX-aLEqQAvWU-s0qIN2OMGAqjOA0R72R_uSffO8_M,573 +referencing/_core.py,sha256=2SPGfaTvzA3sRqbj_4R2wC--fiXolCUVpLFLJJ6zdZ8,24732 +referencing/exceptions.py,sha256=zFgaEg6WiKeT58MQuKNsgGDnHszp26c4oReC6sF9gHM,4176 +referencing/jsonschema.py,sha256=jXZ6t6x9oTvTHMIC0tRyaFjW5r2qeHObYuu7UhSfK3Q,18615 +referencing/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +referencing/retrieval.py,sha256=cn9EeAdaaTbnMDdC4JRIhiEpXDjflRdHCOLFrw1ZhLA,2729 +referencing/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +referencing/tests/__pycache__/__init__.cpython-312.pyc,, +referencing/tests/__pycache__/test_core.cpython-312.pyc,, +referencing/tests/__pycache__/test_exceptions.cpython-312.pyc,, +referencing/tests/__pycache__/test_jsonschema.cpython-312.pyc,, +referencing/tests/__pycache__/test_referencing_suite.cpython-312.pyc,, +referencing/tests/__pycache__/test_retrieval.cpython-312.pyc,, +referencing/tests/test_core.py,sha256=eap0CAaI23vjMIbVyEj92qLddp3iHH3AxC55CKUN4LU,37854 +referencing/tests/test_exceptions.py,sha256=7eOdHyobXMt7-h5AnnH7u8iw2uHPaH7U4Bs9JhLgjWo,934 +referencing/tests/test_jsonschema.py,sha256=4QnjUWOAMAn5yeA8ZtldJkhI54vwKWJWB0LDzNdx5xc,11687 +referencing/tests/test_referencing_suite.py,sha256=wD6veMfLsUu0s4MLjm7pS8cg4cIfL7FMBENngk73zCI,2335 +referencing/tests/test_retrieval.py,sha256=vcbnfA4TqVeqUzW073wO-nLeqVIv0rQZWNWv0z9km48,3719 +referencing/typing.py,sha256=WjUbnZ6jPAd31cnCFAaeWIVENzyHtHdJyOlelv1GY70,1445 diff --git a/lib/python3.12/site-packages/referencing-0.37.0.dist-info/WHEEL b/lib/python3.12/site-packages/referencing-0.37.0.dist-info/WHEEL new file mode 100644 index 0000000000000000000000000000000000000000..12228d414b6cfed7c39d3781c85c63256a1d7fb5 --- /dev/null +++ b/lib/python3.12/site-packages/referencing-0.37.0.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: hatchling 1.27.0 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/lib/python3.12/site-packages/referencing-0.37.0.dist-info/licenses/COPYING b/lib/python3.12/site-packages/referencing-0.37.0.dist-info/licenses/COPYING new file mode 100644 index 0000000000000000000000000000000000000000..a9f853e43069b8e3f8a156a4af2b1198a004230d --- /dev/null +++ b/lib/python3.12/site-packages/referencing-0.37.0.dist-info/licenses/COPYING @@ -0,0 +1,19 @@ +Copyright (c) 2022 Julian Berman + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/lib/python3.12/site-packages/requests-2.32.3.dist-info/INSTALLER b/lib/python3.12/site-packages/requests-2.32.3.dist-info/INSTALLER new file mode 100644 index 0000000000000000000000000000000000000000..a1b589e38a32041e49332e5e81c2d363dc418d68 --- /dev/null +++ b/lib/python3.12/site-packages/requests-2.32.3.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/lib/python3.12/site-packages/requests-2.32.3.dist-info/LICENSE b/lib/python3.12/site-packages/requests-2.32.3.dist-info/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..67db8588217f266eb561f75fae738656325deac9 --- /dev/null +++ b/lib/python3.12/site-packages/requests-2.32.3.dist-info/LICENSE @@ -0,0 +1,175 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. diff --git a/lib/python3.12/site-packages/requests-2.32.3.dist-info/METADATA b/lib/python3.12/site-packages/requests-2.32.3.dist-info/METADATA new file mode 100644 index 0000000000000000000000000000000000000000..72d9dc5313849bf6c7322781ef6bbe2400d02c45 --- /dev/null +++ b/lib/python3.12/site-packages/requests-2.32.3.dist-info/METADATA @@ -0,0 +1,119 @@ +Metadata-Version: 2.1 +Name: requests +Version: 2.32.3 +Summary: Python HTTP for Humans. +Home-page: https://requests.readthedocs.io +Author: Kenneth Reitz +Author-email: me@kennethreitz.org +License: Apache-2.0 +Project-URL: Documentation, https://requests.readthedocs.io +Project-URL: Source, https://github.com/psf/requests +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Web Environment +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: Apache Software License +Classifier: Natural Language :: English +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +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 :: Only +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Internet :: WWW/HTTP +Classifier: Topic :: Software Development :: Libraries +Requires-Python: >=3.8 +Description-Content-Type: text/markdown +License-File: LICENSE +Requires-Dist: charset-normalizer <4,>=2 +Requires-Dist: idna <4,>=2.5 +Requires-Dist: urllib3 <3,>=1.21.1 +Requires-Dist: certifi >=2017.4.17 +Provides-Extra: security +Provides-Extra: socks +Requires-Dist: PySocks !=1.5.7,>=1.5.6 ; extra == 'socks' +Provides-Extra: use_chardet_on_py3 +Requires-Dist: chardet <6,>=3.0.2 ; extra == 'use_chardet_on_py3' + +# Requests + +**Requests** is a simple, yet elegant, HTTP library. + +```python +>>> import requests +>>> r = requests.get('https://httpbin.org/basic-auth/user/pass', auth=('user', 'pass')) +>>> r.status_code +200 +>>> r.headers['content-type'] +'application/json; charset=utf8' +>>> r.encoding +'utf-8' +>>> r.text +'{"authenticated": true, ...' +>>> r.json() +{'authenticated': True, ...} +``` + +Requests allows you to send HTTP/1.1 requests extremely easily. There’s no need to manually add query strings to your URLs, or to form-encode your `PUT` & `POST` data — but nowadays, just use the `json` method! + +Requests is one of the most downloaded Python packages today, pulling in around `30M downloads / week`— according to GitHub, Requests is currently [depended upon](https://github.com/psf/requests/network/dependents?package_id=UGFja2FnZS01NzA4OTExNg%3D%3D) by `1,000,000+` repositories. You may certainly put your trust in this code. + +[![Downloads](https://static.pepy.tech/badge/requests/month)](https://pepy.tech/project/requests) +[![Supported Versions](https://img.shields.io/pypi/pyversions/requests.svg)](https://pypi.org/project/requests) +[![Contributors](https://img.shields.io/github/contributors/psf/requests.svg)](https://github.com/psf/requests/graphs/contributors) + +## Installing Requests and Supported Versions + +Requests is available on PyPI: + +```console +$ python -m pip install requests +``` + +Requests officially supports Python 3.8+. + +## Supported Features & Best–Practices + +Requests is ready for the demands of building robust and reliable HTTP–speaking applications, for the needs of today. + +- Keep-Alive & Connection Pooling +- International Domains and URLs +- Sessions with Cookie Persistence +- Browser-style TLS/SSL Verification +- Basic & Digest Authentication +- Familiar `dict`–like Cookies +- Automatic Content Decompression and Decoding +- Multi-part File Uploads +- SOCKS Proxy Support +- Connection Timeouts +- Streaming Downloads +- Automatic honoring of `.netrc` +- Chunked HTTP Requests + +## API Reference and User Guide available on [Read the Docs](https://requests.readthedocs.io) + +[![Read the Docs](https://raw.githubusercontent.com/psf/requests/main/ext/ss.png)](https://requests.readthedocs.io) + +## Cloning the repository + +When cloning the Requests repository, you may need to add the `-c +fetch.fsck.badTimezone=ignore` flag to avoid an error about a bad commit (see +[this issue](https://github.com/psf/requests/issues/2690) for more background): + +```shell +git clone -c fetch.fsck.badTimezone=ignore https://github.com/psf/requests.git +``` + +You can also apply this setting to your global Git config: + +```shell +git config --global fetch.fsck.badTimezone ignore +``` + +--- + +[![Kenneth Reitz](https://raw.githubusercontent.com/psf/requests/main/ext/kr.png)](https://kennethreitz.org) [![Python Software Foundation](https://raw.githubusercontent.com/psf/requests/main/ext/psf.png)](https://www.python.org/psf) diff --git a/lib/python3.12/site-packages/requests-2.32.3.dist-info/RECORD b/lib/python3.12/site-packages/requests-2.32.3.dist-info/RECORD new file mode 100644 index 0000000000000000000000000000000000000000..f34e5a1d03d15d5db1a8f2b069bd4be5f8d4d6d7 --- /dev/null +++ b/lib/python3.12/site-packages/requests-2.32.3.dist-info/RECORD @@ -0,0 +1,43 @@ +requests-2.32.3.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +requests-2.32.3.dist-info/LICENSE,sha256=CeipvOyAZxBGUsFoaFqwkx54aPnIKEtm9a5u2uXxEws,10142 +requests-2.32.3.dist-info/METADATA,sha256=ZY7oRUweLnb7jCEnEW9hFWs7IpQbNVnAA4ncpwA4WBo,4610 +requests-2.32.3.dist-info/RECORD,, +requests-2.32.3.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +requests-2.32.3.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92 +requests-2.32.3.dist-info/top_level.txt,sha256=fMSVmHfb5rbGOo6xv-O_tUX6j-WyixssE-SnwcDRxNQ,9 +requests/__init__.py,sha256=4xaAERmPDIBPsa2PsjpU9r06yooK-2mZKHTZAhWRWts,5072 +requests/__pycache__/__init__.cpython-312.pyc,, +requests/__pycache__/__version__.cpython-312.pyc,, +requests/__pycache__/_internal_utils.cpython-312.pyc,, +requests/__pycache__/adapters.cpython-312.pyc,, +requests/__pycache__/api.cpython-312.pyc,, +requests/__pycache__/auth.cpython-312.pyc,, +requests/__pycache__/certs.cpython-312.pyc,, +requests/__pycache__/compat.cpython-312.pyc,, +requests/__pycache__/cookies.cpython-312.pyc,, +requests/__pycache__/exceptions.cpython-312.pyc,, +requests/__pycache__/help.cpython-312.pyc,, +requests/__pycache__/hooks.cpython-312.pyc,, +requests/__pycache__/models.cpython-312.pyc,, +requests/__pycache__/packages.cpython-312.pyc,, +requests/__pycache__/sessions.cpython-312.pyc,, +requests/__pycache__/status_codes.cpython-312.pyc,, +requests/__pycache__/structures.cpython-312.pyc,, +requests/__pycache__/utils.cpython-312.pyc,, +requests/__version__.py,sha256=FVfglgZmNQnmYPXpOohDU58F5EUb_-VnSTaAesS187g,435 +requests/_internal_utils.py,sha256=nMQymr4hs32TqVo5AbCrmcJEhvPUh7xXlluyqwslLiQ,1495 +requests/adapters.py,sha256=KIcecscqam6reOCXRl4DwP4jX8Jcl8sd57ft17KR2cQ,27451 +requests/api.py,sha256=_Zb9Oa7tzVIizTKwFrPjDEY9ejtm_OnSRERnADxGsQs,6449 +requests/auth.py,sha256=kF75tqnLctZ9Mf_hm9TZIj4cQWnN5uxRz8oWsx5wmR0,10186 +requests/certs.py,sha256=Z9Sb410Anv6jUFTyss0jFFhU6xst8ctELqfy8Ev23gw,429 +requests/compat.py,sha256=C5w_DPLSurXPgcdWU78fora0APmbYkX2G89QvH5xzPA,1817 +requests/cookies.py,sha256=bNi-iqEj4NPZ00-ob-rHvzkvObzN3lEpgw3g6paS3Xw,18590 +requests/exceptions.py,sha256=jJPS1UWATs86ShVUaLorTiJb1SaGuoNEWgICJep-VkY,4260 +requests/help.py,sha256=gPX5d_H7Xd88aDABejhqGgl9B1VFRTt5BmiYvL3PzIQ,3875 +requests/hooks.py,sha256=CiuysiHA39V5UfcCBXFIx83IrDpuwfN9RcTUgv28ftQ,733 +requests/models.py,sha256=k42roXzC8u_OagAPQi9U4MkfO7i4r2FdaqvMqstPehc,35418 +requests/packages.py,sha256=_g0gZ681UyAlKHRjH6kanbaoxx2eAb6qzcXiODyTIoc,904 +requests/sessions.py,sha256=ykTI8UWGSltOfH07HKollH7kTBGw4WhiBVaQGmckTw4,30495 +requests/status_codes.py,sha256=iJUAeA25baTdw-6PfD0eF4qhpINDJRJI-yaMqxs4LEI,4322 +requests/structures.py,sha256=-IbmhVz06S-5aPSZuUthZ6-6D9XOjRuTXHOabY041XM,2912 +requests/utils.py,sha256=HiQC6Nq_Da3ktaMiFzQkh-dCk3iQHHKEsYS5kDc-8Cw,33619 diff --git a/lib/python3.12/site-packages/requests-2.32.3.dist-info/REQUESTED b/lib/python3.12/site-packages/requests-2.32.3.dist-info/REQUESTED new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/lib/python3.12/site-packages/requests-2.32.3.dist-info/WHEEL b/lib/python3.12/site-packages/requests-2.32.3.dist-info/WHEEL new file mode 100644 index 0000000000000000000000000000000000000000..bab98d675883cc7567a79df485cd7b4f015e376f --- /dev/null +++ b/lib/python3.12/site-packages/requests-2.32.3.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.43.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/lib/python3.12/site-packages/requests-2.32.3.dist-info/top_level.txt b/lib/python3.12/site-packages/requests-2.32.3.dist-info/top_level.txt new file mode 100644 index 0000000000000000000000000000000000000000..f2293605cf1b01dca72aad0a15c45b72ed5429a2 --- /dev/null +++ b/lib/python3.12/site-packages/requests-2.32.3.dist-info/top_level.txt @@ -0,0 +1 @@ +requests diff --git a/lib/python3.12/site-packages/starlette/__init__.py b/lib/python3.12/site-packages/starlette/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..8583baf64fc9f16d9a5dfe0e269214eb9fe60fa3 --- /dev/null +++ b/lib/python3.12/site-packages/starlette/__init__.py @@ -0,0 +1 @@ +__version__ = "0.46.2" diff --git a/lib/python3.12/site-packages/starlette/__pycache__/__init__.cpython-312.pyc b/lib/python3.12/site-packages/starlette/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..afb0c7e8307961602295eae52163dee639a6c4d4 Binary files /dev/null and b/lib/python3.12/site-packages/starlette/__pycache__/__init__.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/starlette/__pycache__/_exception_handler.cpython-312.pyc b/lib/python3.12/site-packages/starlette/__pycache__/_exception_handler.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8dc253f645eb0941d790699901da425b41dc4bac Binary files /dev/null and b/lib/python3.12/site-packages/starlette/__pycache__/_exception_handler.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/starlette/__pycache__/_utils.cpython-312.pyc b/lib/python3.12/site-packages/starlette/__pycache__/_utils.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..aafb3aa903c664663038f23a8f90663b830aa6e0 Binary files /dev/null and b/lib/python3.12/site-packages/starlette/__pycache__/_utils.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/starlette/__pycache__/applications.cpython-312.pyc b/lib/python3.12/site-packages/starlette/__pycache__/applications.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f7e85c1ec8468ea405f3997114b4231953d11484 Binary files /dev/null and b/lib/python3.12/site-packages/starlette/__pycache__/applications.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/starlette/__pycache__/authentication.cpython-312.pyc b/lib/python3.12/site-packages/starlette/__pycache__/authentication.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..22b335abb0625e8f2557cf8ff0b0fec6d0cb6b25 Binary files /dev/null and b/lib/python3.12/site-packages/starlette/__pycache__/authentication.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/starlette/__pycache__/background.cpython-312.pyc b/lib/python3.12/site-packages/starlette/__pycache__/background.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..22819d47c6f5c90112bd7c0e6bcf8d9036ee041f Binary files /dev/null and b/lib/python3.12/site-packages/starlette/__pycache__/background.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/starlette/__pycache__/concurrency.cpython-312.pyc b/lib/python3.12/site-packages/starlette/__pycache__/concurrency.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b91b5107006d925c616e9d86b31be509ea27b035 Binary files /dev/null and b/lib/python3.12/site-packages/starlette/__pycache__/concurrency.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/starlette/__pycache__/config.cpython-312.pyc b/lib/python3.12/site-packages/starlette/__pycache__/config.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..15c932177a3f23f333b87daaa652ca7efd5177e9 Binary files /dev/null and b/lib/python3.12/site-packages/starlette/__pycache__/config.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/starlette/__pycache__/convertors.cpython-312.pyc b/lib/python3.12/site-packages/starlette/__pycache__/convertors.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d0f774d197f29263b0ec66d29be9303d667903ce Binary files /dev/null and b/lib/python3.12/site-packages/starlette/__pycache__/convertors.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/starlette/__pycache__/datastructures.cpython-312.pyc b/lib/python3.12/site-packages/starlette/__pycache__/datastructures.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b5b30b84d99b4fd4a01e87d60fcd72db3ccaf561 Binary files /dev/null and b/lib/python3.12/site-packages/starlette/__pycache__/datastructures.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/starlette/__pycache__/endpoints.cpython-312.pyc b/lib/python3.12/site-packages/starlette/__pycache__/endpoints.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..916d5f678f6e2519be870485b0a0ba6c305e60a6 Binary files /dev/null and b/lib/python3.12/site-packages/starlette/__pycache__/endpoints.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/starlette/__pycache__/exceptions.cpython-312.pyc b/lib/python3.12/site-packages/starlette/__pycache__/exceptions.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cebf1218672cfec94b50a80f1bf26dd38742436f Binary files /dev/null and b/lib/python3.12/site-packages/starlette/__pycache__/exceptions.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/starlette/__pycache__/formparsers.cpython-312.pyc b/lib/python3.12/site-packages/starlette/__pycache__/formparsers.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b4ba0b516f8c7c45f74ea49373ce34bb75fd14ba Binary files /dev/null and b/lib/python3.12/site-packages/starlette/__pycache__/formparsers.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/starlette/__pycache__/requests.cpython-312.pyc b/lib/python3.12/site-packages/starlette/__pycache__/requests.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..213ca2f7ea4875d2ad9792d2e4b3b3d7ad2c4c46 Binary files /dev/null and b/lib/python3.12/site-packages/starlette/__pycache__/requests.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/starlette/__pycache__/responses.cpython-312.pyc b/lib/python3.12/site-packages/starlette/__pycache__/responses.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a1737f2b47616b172007e9aeb133467373471492 Binary files /dev/null and b/lib/python3.12/site-packages/starlette/__pycache__/responses.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/starlette/__pycache__/routing.cpython-312.pyc b/lib/python3.12/site-packages/starlette/__pycache__/routing.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cd0dbd706fe0634703832b88d8bfb217b4d19ce1 Binary files /dev/null and b/lib/python3.12/site-packages/starlette/__pycache__/routing.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/starlette/__pycache__/schemas.cpython-312.pyc b/lib/python3.12/site-packages/starlette/__pycache__/schemas.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c343b3bb26f9b11d13a8eedc873f346e1fdce1ec Binary files /dev/null and b/lib/python3.12/site-packages/starlette/__pycache__/schemas.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/starlette/__pycache__/staticfiles.cpython-312.pyc b/lib/python3.12/site-packages/starlette/__pycache__/staticfiles.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2b592855187ee3b5250a1e48d23e1a41a1905367 Binary files /dev/null and b/lib/python3.12/site-packages/starlette/__pycache__/staticfiles.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/starlette/__pycache__/status.cpython-312.pyc b/lib/python3.12/site-packages/starlette/__pycache__/status.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3d85386cf650608b0c29c18127a96cc40cf33dd3 Binary files /dev/null and b/lib/python3.12/site-packages/starlette/__pycache__/status.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/starlette/__pycache__/templating.cpython-312.pyc b/lib/python3.12/site-packages/starlette/__pycache__/templating.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6dc57b2d98a4eef4a0ca9c9c818b81b82fe437b0 Binary files /dev/null and b/lib/python3.12/site-packages/starlette/__pycache__/templating.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/starlette/__pycache__/testclient.cpython-312.pyc b/lib/python3.12/site-packages/starlette/__pycache__/testclient.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..918eb6bf40877f112322934df2541dafb298153a Binary files /dev/null and b/lib/python3.12/site-packages/starlette/__pycache__/testclient.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/starlette/__pycache__/types.cpython-312.pyc b/lib/python3.12/site-packages/starlette/__pycache__/types.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7af427591d99a022fb0b389edb57e75f3f16fe73 Binary files /dev/null and b/lib/python3.12/site-packages/starlette/__pycache__/types.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/starlette/__pycache__/websockets.cpython-312.pyc b/lib/python3.12/site-packages/starlette/__pycache__/websockets.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dde69867ec3a208501976af860855a4710b0eb1c Binary files /dev/null and b/lib/python3.12/site-packages/starlette/__pycache__/websockets.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/starlette/_exception_handler.py b/lib/python3.12/site-packages/starlette/_exception_handler.py new file mode 100644 index 0000000000000000000000000000000000000000..72bc89d91a4e7c2400511e280c26dd1cb2fc6502 --- /dev/null +++ b/lib/python3.12/site-packages/starlette/_exception_handler.py @@ -0,0 +1,65 @@ +from __future__ import annotations + +import typing + +from starlette._utils import is_async_callable +from starlette.concurrency import run_in_threadpool +from starlette.exceptions import HTTPException +from starlette.requests import Request +from starlette.types import ASGIApp, ExceptionHandler, Message, Receive, Scope, Send +from starlette.websockets import WebSocket + +ExceptionHandlers = dict[typing.Any, ExceptionHandler] +StatusHandlers = dict[int, ExceptionHandler] + + +def _lookup_exception_handler(exc_handlers: ExceptionHandlers, exc: Exception) -> ExceptionHandler | None: + for cls in type(exc).__mro__: + if cls in exc_handlers: + return exc_handlers[cls] + return None + + +def wrap_app_handling_exceptions(app: ASGIApp, conn: Request | WebSocket) -> ASGIApp: + exception_handlers: ExceptionHandlers + status_handlers: StatusHandlers + try: + exception_handlers, status_handlers = conn.scope["starlette.exception_handlers"] + except KeyError: + exception_handlers, status_handlers = {}, {} + + async def wrapped_app(scope: Scope, receive: Receive, send: Send) -> None: + response_started = False + + async def sender(message: Message) -> None: + nonlocal response_started + + if message["type"] == "http.response.start": + response_started = True + await send(message) + + try: + await app(scope, receive, sender) + except Exception as exc: + handler = None + + if isinstance(exc, HTTPException): + handler = status_handlers.get(exc.status_code) + + if handler is None: + handler = _lookup_exception_handler(exception_handlers, exc) + + if handler is None: + raise exc + + if response_started: + raise RuntimeError("Caught handled exception, but response already started.") from exc + + if is_async_callable(handler): + response = await handler(conn, exc) + else: + response = await run_in_threadpool(handler, conn, exc) # type: ignore + if response is not None: + await response(scope, receive, sender) + + return wrapped_app diff --git a/lib/python3.12/site-packages/starlette/_utils.py b/lib/python3.12/site-packages/starlette/_utils.py new file mode 100644 index 0000000000000000000000000000000000000000..8001c4722f881b3a4cee13c1aa82c8a150b992c8 --- /dev/null +++ b/lib/python3.12/site-packages/starlette/_utils.py @@ -0,0 +1,100 @@ +from __future__ import annotations + +import functools +import inspect +import sys +import typing +from contextlib import contextmanager + +from starlette.types import Scope + +if sys.version_info >= (3, 10): # pragma: no cover + from typing import TypeGuard +else: # pragma: no cover + from typing_extensions import TypeGuard + +has_exceptiongroups = True +if sys.version_info < (3, 11): # pragma: no cover + try: + from exceptiongroup import BaseExceptionGroup # type: ignore[unused-ignore,import-not-found] + except ImportError: + has_exceptiongroups = False + +T = typing.TypeVar("T") +AwaitableCallable = typing.Callable[..., typing.Awaitable[T]] + + +@typing.overload +def is_async_callable(obj: AwaitableCallable[T]) -> TypeGuard[AwaitableCallable[T]]: ... + + +@typing.overload +def is_async_callable(obj: typing.Any) -> TypeGuard[AwaitableCallable[typing.Any]]: ... + + +def is_async_callable(obj: typing.Any) -> typing.Any: + while isinstance(obj, functools.partial): + obj = obj.func + + return inspect.iscoroutinefunction(obj) or (callable(obj) and inspect.iscoroutinefunction(obj.__call__)) + + +T_co = typing.TypeVar("T_co", covariant=True) + + +class AwaitableOrContextManager(typing.Awaitable[T_co], typing.AsyncContextManager[T_co], typing.Protocol[T_co]): ... + + +class SupportsAsyncClose(typing.Protocol): + async def close(self) -> None: ... # pragma: no cover + + +SupportsAsyncCloseType = typing.TypeVar("SupportsAsyncCloseType", bound=SupportsAsyncClose, covariant=False) + + +class AwaitableOrContextManagerWrapper(typing.Generic[SupportsAsyncCloseType]): + __slots__ = ("aw", "entered") + + def __init__(self, aw: typing.Awaitable[SupportsAsyncCloseType]) -> None: + self.aw = aw + + def __await__(self) -> typing.Generator[typing.Any, None, SupportsAsyncCloseType]: + return self.aw.__await__() + + async def __aenter__(self) -> SupportsAsyncCloseType: + self.entered = await self.aw + return self.entered + + async def __aexit__(self, *args: typing.Any) -> None | bool: + await self.entered.close() + return None + + +@contextmanager +def collapse_excgroups() -> typing.Generator[None, None, None]: + try: + yield + except BaseException as exc: + if has_exceptiongroups: # pragma: no cover + while isinstance(exc, BaseExceptionGroup) and len(exc.exceptions) == 1: + exc = exc.exceptions[0] + + raise exc + + +def get_route_path(scope: Scope) -> str: + path: str = scope["path"] + root_path = scope.get("root_path", "") + if not root_path: + return path + + if not path.startswith(root_path): + return path + + if path == root_path: + return "" + + if path[len(root_path)] == "/": + return path[len(root_path) :] + + return path diff --git a/lib/python3.12/site-packages/starlette/applications.py b/lib/python3.12/site-packages/starlette/applications.py new file mode 100644 index 0000000000000000000000000000000000000000..6df5a707cc31fc0b6eda65a5fc61d972819f315a --- /dev/null +++ b/lib/python3.12/site-packages/starlette/applications.py @@ -0,0 +1,249 @@ +from __future__ import annotations + +import sys +import typing +import warnings + +if sys.version_info >= (3, 10): # pragma: no cover + from typing import ParamSpec +else: # pragma: no cover + from typing_extensions import ParamSpec + +from starlette.datastructures import State, URLPath +from starlette.middleware import Middleware, _MiddlewareFactory +from starlette.middleware.base import BaseHTTPMiddleware +from starlette.middleware.errors import ServerErrorMiddleware +from starlette.middleware.exceptions import ExceptionMiddleware +from starlette.requests import Request +from starlette.responses import Response +from starlette.routing import BaseRoute, Router +from starlette.types import ASGIApp, ExceptionHandler, Lifespan, Receive, Scope, Send +from starlette.websockets import WebSocket + +AppType = typing.TypeVar("AppType", bound="Starlette") +P = ParamSpec("P") + + +class Starlette: + """Creates an Starlette application.""" + + def __init__( + self: AppType, + debug: bool = False, + routes: typing.Sequence[BaseRoute] | None = None, + middleware: typing.Sequence[Middleware] | None = None, + exception_handlers: typing.Mapping[typing.Any, ExceptionHandler] | None = None, + on_startup: typing.Sequence[typing.Callable[[], typing.Any]] | None = None, + on_shutdown: typing.Sequence[typing.Callable[[], typing.Any]] | None = None, + lifespan: Lifespan[AppType] | None = None, + ) -> None: + """Initializes the application. + + Parameters: + debug: Boolean indicating if debug tracebacks should be returned on errors. + routes: A list of routes to serve incoming HTTP and WebSocket requests. + middleware: A list of middleware to run for every request. A starlette + application will always automatically include two middleware classes. + `ServerErrorMiddleware` is added as the very outermost middleware, to handle + any uncaught errors occurring anywhere in the entire stack. + `ExceptionMiddleware` is added as the very innermost middleware, to deal + with handled exception cases occurring in the routing or endpoints. + exception_handlers: A mapping of either integer status codes, + or exception class types onto callables which handle the exceptions. + Exception handler callables should be of the form + `handler(request, exc) -> response` and may be either standard functions, or + async functions. + on_startup: A list of callables to run on application startup. + Startup handler callables do not take any arguments, and may be either + standard functions, or async functions. + on_shutdown: A list of callables to run on application shutdown. + Shutdown handler callables do not take any arguments, and may be either + standard functions, or async functions. + lifespan: A lifespan context function, which can be used to perform + startup and shutdown tasks. This is a newer style that replaces the + `on_startup` and `on_shutdown` handlers. Use one or the other, not both. + """ + # The lifespan context function is a newer style that replaces + # on_startup / on_shutdown handlers. Use one or the other, not both. + assert lifespan is None or (on_startup is None and on_shutdown is None), ( + "Use either 'lifespan' or 'on_startup'/'on_shutdown', not both." + ) + + self.debug = debug + self.state = State() + self.router = Router(routes, on_startup=on_startup, on_shutdown=on_shutdown, lifespan=lifespan) + self.exception_handlers = {} if exception_handlers is None else dict(exception_handlers) + self.user_middleware = [] if middleware is None else list(middleware) + self.middleware_stack: ASGIApp | None = None + + def build_middleware_stack(self) -> ASGIApp: + debug = self.debug + error_handler = None + exception_handlers: dict[typing.Any, typing.Callable[[Request, Exception], Response]] = {} + + for key, value in self.exception_handlers.items(): + if key in (500, Exception): + error_handler = value + else: + exception_handlers[key] = value + + middleware = ( + [Middleware(ServerErrorMiddleware, handler=error_handler, debug=debug)] + + self.user_middleware + + [Middleware(ExceptionMiddleware, handlers=exception_handlers, debug=debug)] + ) + + app = self.router + for cls, args, kwargs in reversed(middleware): + app = cls(app, *args, **kwargs) + return app + + @property + def routes(self) -> list[BaseRoute]: + return self.router.routes + + def url_path_for(self, name: str, /, **path_params: typing.Any) -> URLPath: + return self.router.url_path_for(name, **path_params) + + async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None: + scope["app"] = self + if self.middleware_stack is None: + self.middleware_stack = self.build_middleware_stack() + await self.middleware_stack(scope, receive, send) + + def on_event(self, event_type: str) -> typing.Callable: # type: ignore[type-arg] + return self.router.on_event(event_type) # pragma: no cover + + def mount(self, path: str, app: ASGIApp, name: str | None = None) -> None: + self.router.mount(path, app=app, name=name) # pragma: no cover + + def host(self, host: str, app: ASGIApp, name: str | None = None) -> None: + self.router.host(host, app=app, name=name) # pragma: no cover + + def add_middleware( + self, + middleware_class: _MiddlewareFactory[P], + *args: P.args, + **kwargs: P.kwargs, + ) -> None: + if self.middleware_stack is not None: # pragma: no cover + raise RuntimeError("Cannot add middleware after an application has started") + self.user_middleware.insert(0, Middleware(middleware_class, *args, **kwargs)) + + def add_exception_handler( + self, + exc_class_or_status_code: int | type[Exception], + handler: ExceptionHandler, + ) -> None: # pragma: no cover + self.exception_handlers[exc_class_or_status_code] = handler + + def add_event_handler( + self, + event_type: str, + func: typing.Callable, # type: ignore[type-arg] + ) -> None: # pragma: no cover + self.router.add_event_handler(event_type, func) + + def add_route( + self, + path: str, + route: typing.Callable[[Request], typing.Awaitable[Response] | Response], + methods: list[str] | None = None, + name: str | None = None, + include_in_schema: bool = True, + ) -> None: # pragma: no cover + self.router.add_route(path, route, methods=methods, name=name, include_in_schema=include_in_schema) + + def add_websocket_route( + self, + path: str, + route: typing.Callable[[WebSocket], typing.Awaitable[None]], + name: str | None = None, + ) -> None: # pragma: no cover + self.router.add_websocket_route(path, route, name=name) + + def exception_handler(self, exc_class_or_status_code: int | type[Exception]) -> typing.Callable: # type: ignore[type-arg] + warnings.warn( + "The `exception_handler` decorator is deprecated, and will be removed in version 1.0.0. " + "Refer to https://www.starlette.io/exceptions/ for the recommended approach.", + DeprecationWarning, + ) + + def decorator(func: typing.Callable) -> typing.Callable: # type: ignore[type-arg] + self.add_exception_handler(exc_class_or_status_code, func) + return func + + return decorator + + def route( + self, + path: str, + methods: list[str] | None = None, + name: str | None = None, + include_in_schema: bool = True, + ) -> typing.Callable: # type: ignore[type-arg] + """ + We no longer document this decorator style API, and its usage is discouraged. + Instead you should use the following approach: + + >>> routes = [Route(path, endpoint=...), ...] + >>> app = Starlette(routes=routes) + """ + warnings.warn( + "The `route` decorator is deprecated, and will be removed in version 1.0.0. " + "Refer to https://www.starlette.io/routing/ for the recommended approach.", + DeprecationWarning, + ) + + def decorator(func: typing.Callable) -> typing.Callable: # type: ignore[type-arg] + self.router.add_route( + path, + func, + methods=methods, + name=name, + include_in_schema=include_in_schema, + ) + return func + + return decorator + + def websocket_route(self, path: str, name: str | None = None) -> typing.Callable: # type: ignore[type-arg] + """ + We no longer document this decorator style API, and its usage is discouraged. + Instead you should use the following approach: + + >>> routes = [WebSocketRoute(path, endpoint=...), ...] + >>> app = Starlette(routes=routes) + """ + warnings.warn( + "The `websocket_route` decorator is deprecated, and will be removed in version 1.0.0. " + "Refer to https://www.starlette.io/routing/#websocket-routing for the recommended approach.", + DeprecationWarning, + ) + + def decorator(func: typing.Callable) -> typing.Callable: # type: ignore[type-arg] + self.router.add_websocket_route(path, func, name=name) + return func + + return decorator + + def middleware(self, middleware_type: str) -> typing.Callable: # type: ignore[type-arg] + """ + We no longer document this decorator style API, and its usage is discouraged. + Instead you should use the following approach: + + >>> middleware = [Middleware(...), ...] + >>> app = Starlette(middleware=middleware) + """ + warnings.warn( + "The `middleware` decorator is deprecated, and will be removed in version 1.0.0. " + "Refer to https://www.starlette.io/middleware/#using-middleware for recommended approach.", + DeprecationWarning, + ) + assert middleware_type == "http", 'Currently only middleware("http") is supported.' + + def decorator(func: typing.Callable) -> typing.Callable: # type: ignore[type-arg] + self.add_middleware(BaseHTTPMiddleware, dispatch=func) + return func + + return decorator diff --git a/lib/python3.12/site-packages/starlette/authentication.py b/lib/python3.12/site-packages/starlette/authentication.py new file mode 100644 index 0000000000000000000000000000000000000000..4fd866412b5e32fc333866cfea8271f3a7116907 --- /dev/null +++ b/lib/python3.12/site-packages/starlette/authentication.py @@ -0,0 +1,147 @@ +from __future__ import annotations + +import functools +import inspect +import sys +import typing +from urllib.parse import urlencode + +if sys.version_info >= (3, 10): # pragma: no cover + from typing import ParamSpec +else: # pragma: no cover + from typing_extensions import ParamSpec + +from starlette._utils import is_async_callable +from starlette.exceptions import HTTPException +from starlette.requests import HTTPConnection, Request +from starlette.responses import RedirectResponse +from starlette.websockets import WebSocket + +_P = ParamSpec("_P") + + +def has_required_scope(conn: HTTPConnection, scopes: typing.Sequence[str]) -> bool: + for scope in scopes: + if scope not in conn.auth.scopes: + return False + return True + + +def requires( + scopes: str | typing.Sequence[str], + status_code: int = 403, + redirect: str | None = None, +) -> typing.Callable[[typing.Callable[_P, typing.Any]], typing.Callable[_P, typing.Any]]: + scopes_list = [scopes] if isinstance(scopes, str) else list(scopes) + + def decorator( + func: typing.Callable[_P, typing.Any], + ) -> typing.Callable[_P, typing.Any]: + sig = inspect.signature(func) + for idx, parameter in enumerate(sig.parameters.values()): + if parameter.name == "request" or parameter.name == "websocket": + type_ = parameter.name + break + else: + raise Exception(f'No "request" or "websocket" argument on function "{func}"') + + if type_ == "websocket": + # Handle websocket functions. (Always async) + @functools.wraps(func) + async def websocket_wrapper(*args: _P.args, **kwargs: _P.kwargs) -> None: + websocket = kwargs.get("websocket", args[idx] if idx < len(args) else None) + assert isinstance(websocket, WebSocket) + + if not has_required_scope(websocket, scopes_list): + await websocket.close() + else: + await func(*args, **kwargs) + + return websocket_wrapper + + elif is_async_callable(func): + # Handle async request/response functions. + @functools.wraps(func) + async def async_wrapper(*args: _P.args, **kwargs: _P.kwargs) -> typing.Any: + request = kwargs.get("request", args[idx] if idx < len(args) else None) + assert isinstance(request, Request) + + if not has_required_scope(request, scopes_list): + if redirect is not None: + orig_request_qparam = urlencode({"next": str(request.url)}) + next_url = f"{request.url_for(redirect)}?{orig_request_qparam}" + return RedirectResponse(url=next_url, status_code=303) + raise HTTPException(status_code=status_code) + return await func(*args, **kwargs) + + return async_wrapper + + else: + # Handle sync request/response functions. + @functools.wraps(func) + def sync_wrapper(*args: _P.args, **kwargs: _P.kwargs) -> typing.Any: + request = kwargs.get("request", args[idx] if idx < len(args) else None) + assert isinstance(request, Request) + + if not has_required_scope(request, scopes_list): + if redirect is not None: + orig_request_qparam = urlencode({"next": str(request.url)}) + next_url = f"{request.url_for(redirect)}?{orig_request_qparam}" + return RedirectResponse(url=next_url, status_code=303) + raise HTTPException(status_code=status_code) + return func(*args, **kwargs) + + return sync_wrapper + + return decorator + + +class AuthenticationError(Exception): + pass + + +class AuthenticationBackend: + async def authenticate(self, conn: HTTPConnection) -> tuple[AuthCredentials, BaseUser] | None: + raise NotImplementedError() # pragma: no cover + + +class AuthCredentials: + def __init__(self, scopes: typing.Sequence[str] | None = None): + self.scopes = [] if scopes is None else list(scopes) + + +class BaseUser: + @property + def is_authenticated(self) -> bool: + raise NotImplementedError() # pragma: no cover + + @property + def display_name(self) -> str: + raise NotImplementedError() # pragma: no cover + + @property + def identity(self) -> str: + raise NotImplementedError() # pragma: no cover + + +class SimpleUser(BaseUser): + def __init__(self, username: str) -> None: + self.username = username + + @property + def is_authenticated(self) -> bool: + return True + + @property + def display_name(self) -> str: + return self.username + + +class UnauthenticatedUser(BaseUser): + @property + def is_authenticated(self) -> bool: + return False + + @property + def display_name(self) -> str: + return "" diff --git a/lib/python3.12/site-packages/starlette/background.py b/lib/python3.12/site-packages/starlette/background.py new file mode 100644 index 0000000000000000000000000000000000000000..0430fc08bb6b256767b8511220e89ae9373fa53f --- /dev/null +++ b/lib/python3.12/site-packages/starlette/background.py @@ -0,0 +1,41 @@ +from __future__ import annotations + +import sys +import typing + +if sys.version_info >= (3, 10): # pragma: no cover + from typing import ParamSpec +else: # pragma: no cover + from typing_extensions import ParamSpec + +from starlette._utils import is_async_callable +from starlette.concurrency import run_in_threadpool + +P = ParamSpec("P") + + +class BackgroundTask: + def __init__(self, func: typing.Callable[P, typing.Any], *args: P.args, **kwargs: P.kwargs) -> None: + self.func = func + self.args = args + self.kwargs = kwargs + self.is_async = is_async_callable(func) + + async def __call__(self) -> None: + if self.is_async: + await self.func(*self.args, **self.kwargs) + else: + await run_in_threadpool(self.func, *self.args, **self.kwargs) + + +class BackgroundTasks(BackgroundTask): + def __init__(self, tasks: typing.Sequence[BackgroundTask] | None = None): + self.tasks = list(tasks) if tasks else [] + + def add_task(self, func: typing.Callable[P, typing.Any], *args: P.args, **kwargs: P.kwargs) -> None: + task = BackgroundTask(func, *args, **kwargs) + self.tasks.append(task) + + async def __call__(self) -> None: + for task in self.tasks: + await task() diff --git a/lib/python3.12/site-packages/starlette/concurrency.py b/lib/python3.12/site-packages/starlette/concurrency.py new file mode 100644 index 0000000000000000000000000000000000000000..494f34204fc004f2be964bd227cb0c7a303d8351 --- /dev/null +++ b/lib/python3.12/site-packages/starlette/concurrency.py @@ -0,0 +1,62 @@ +from __future__ import annotations + +import functools +import sys +import typing +import warnings + +import anyio.to_thread + +if sys.version_info >= (3, 10): # pragma: no cover + from typing import ParamSpec +else: # pragma: no cover + from typing_extensions import ParamSpec + +P = ParamSpec("P") +T = typing.TypeVar("T") + + +async def run_until_first_complete(*args: tuple[typing.Callable, dict]) -> None: # type: ignore[type-arg] + warnings.warn( + "run_until_first_complete is deprecated and will be removed in a future version.", + DeprecationWarning, + ) + + async with anyio.create_task_group() as task_group: + + async def run(func: typing.Callable[[], typing.Coroutine]) -> None: # type: ignore[type-arg] + await func() + task_group.cancel_scope.cancel() + + for func, kwargs in args: + task_group.start_soon(run, functools.partial(func, **kwargs)) + + +async def run_in_threadpool(func: typing.Callable[P, T], *args: P.args, **kwargs: P.kwargs) -> T: + func = functools.partial(func, *args, **kwargs) + return await anyio.to_thread.run_sync(func) + + +class _StopIteration(Exception): + pass + + +def _next(iterator: typing.Iterator[T]) -> T: + # We can't raise `StopIteration` from within the threadpool iterator + # and catch it outside that context, so we coerce them into a different + # exception type. + try: + return next(iterator) + except StopIteration: + raise _StopIteration + + +async def iterate_in_threadpool( + iterator: typing.Iterable[T], +) -> typing.AsyncIterator[T]: + as_iterator = iter(iterator) + while True: + try: + yield await anyio.to_thread.run_sync(_next, as_iterator) + except _StopIteration: + break diff --git a/lib/python3.12/site-packages/starlette/config.py b/lib/python3.12/site-packages/starlette/config.py new file mode 100644 index 0000000000000000000000000000000000000000..ca15c564670271e66790000a308a7d7e981e0bac --- /dev/null +++ b/lib/python3.12/site-packages/starlette/config.py @@ -0,0 +1,138 @@ +from __future__ import annotations + +import os +import typing +import warnings +from pathlib import Path + + +class undefined: + pass + + +class EnvironError(Exception): + pass + + +class Environ(typing.MutableMapping[str, str]): + def __init__(self, environ: typing.MutableMapping[str, str] = os.environ): + self._environ = environ + self._has_been_read: set[str] = set() + + def __getitem__(self, key: str) -> str: + self._has_been_read.add(key) + return self._environ.__getitem__(key) + + def __setitem__(self, key: str, value: str) -> None: + if key in self._has_been_read: + raise EnvironError(f"Attempting to set environ['{key}'], but the value has already been read.") + self._environ.__setitem__(key, value) + + def __delitem__(self, key: str) -> None: + if key in self._has_been_read: + raise EnvironError(f"Attempting to delete environ['{key}'], but the value has already been read.") + self._environ.__delitem__(key) + + def __iter__(self) -> typing.Iterator[str]: + return iter(self._environ) + + def __len__(self) -> int: + return len(self._environ) + + +environ = Environ() + +T = typing.TypeVar("T") + + +class Config: + def __init__( + self, + env_file: str | Path | None = None, + environ: typing.Mapping[str, str] = environ, + env_prefix: str = "", + ) -> None: + self.environ = environ + self.env_prefix = env_prefix + self.file_values: dict[str, str] = {} + if env_file is not None: + if not os.path.isfile(env_file): + warnings.warn(f"Config file '{env_file}' not found.") + else: + self.file_values = self._read_file(env_file) + + @typing.overload + def __call__(self, key: str, *, default: None) -> str | None: ... + + @typing.overload + def __call__(self, key: str, cast: type[T], default: T = ...) -> T: ... + + @typing.overload + def __call__(self, key: str, cast: type[str] = ..., default: str = ...) -> str: ... + + @typing.overload + def __call__( + self, + key: str, + cast: typing.Callable[[typing.Any], T] = ..., + default: typing.Any = ..., + ) -> T: ... + + @typing.overload + def __call__(self, key: str, cast: type[str] = ..., default: T = ...) -> T | str: ... + + def __call__( + self, + key: str, + cast: typing.Callable[[typing.Any], typing.Any] | None = None, + default: typing.Any = undefined, + ) -> typing.Any: + return self.get(key, cast, default) + + def get( + self, + key: str, + cast: typing.Callable[[typing.Any], typing.Any] | None = None, + default: typing.Any = undefined, + ) -> typing.Any: + key = self.env_prefix + key + if key in self.environ: + value = self.environ[key] + return self._perform_cast(key, value, cast) + if key in self.file_values: + value = self.file_values[key] + return self._perform_cast(key, value, cast) + if default is not undefined: + return self._perform_cast(key, default, cast) + raise KeyError(f"Config '{key}' is missing, and has no default.") + + def _read_file(self, file_name: str | Path) -> dict[str, str]: + file_values: dict[str, str] = {} + with open(file_name) as input_file: + for line in input_file.readlines(): + line = line.strip() + if "=" in line and not line.startswith("#"): + key, value = line.split("=", 1) + key = key.strip() + value = value.strip().strip("\"'") + file_values[key] = value + return file_values + + def _perform_cast( + self, + key: str, + value: typing.Any, + cast: typing.Callable[[typing.Any], typing.Any] | None = None, + ) -> typing.Any: + if cast is None or value is None: + return value + elif cast is bool and isinstance(value, str): + mapping = {"true": True, "1": True, "false": False, "0": False} + value = value.lower() + if value not in mapping: + raise ValueError(f"Config '{key}' has value '{value}'. Not a valid bool.") + return mapping[value] + try: + return cast(value) + except (TypeError, ValueError): + raise ValueError(f"Config '{key}' has value '{value}'. Not a valid {cast.__name__}.") diff --git a/lib/python3.12/site-packages/starlette/convertors.py b/lib/python3.12/site-packages/starlette/convertors.py new file mode 100644 index 0000000000000000000000000000000000000000..84df87a586adf44bb6135d547049b9f5385f30b7 --- /dev/null +++ b/lib/python3.12/site-packages/starlette/convertors.py @@ -0,0 +1,89 @@ +from __future__ import annotations + +import math +import typing +import uuid + +T = typing.TypeVar("T") + + +class Convertor(typing.Generic[T]): + regex: typing.ClassVar[str] = "" + + def convert(self, value: str) -> T: + raise NotImplementedError() # pragma: no cover + + def to_string(self, value: T) -> str: + raise NotImplementedError() # pragma: no cover + + +class StringConvertor(Convertor[str]): + regex = "[^/]+" + + def convert(self, value: str) -> str: + return value + + def to_string(self, value: str) -> str: + value = str(value) + assert "/" not in value, "May not contain path separators" + assert value, "Must not be empty" + return value + + +class PathConvertor(Convertor[str]): + regex = ".*" + + def convert(self, value: str) -> str: + return str(value) + + def to_string(self, value: str) -> str: + return str(value) + + +class IntegerConvertor(Convertor[int]): + regex = "[0-9]+" + + def convert(self, value: str) -> int: + return int(value) + + def to_string(self, value: int) -> str: + value = int(value) + assert value >= 0, "Negative integers are not supported" + return str(value) + + +class FloatConvertor(Convertor[float]): + regex = r"[0-9]+(\.[0-9]+)?" + + def convert(self, value: str) -> float: + return float(value) + + def to_string(self, value: float) -> str: + value = float(value) + assert value >= 0.0, "Negative floats are not supported" + assert not math.isnan(value), "NaN values are not supported" + assert not math.isinf(value), "Infinite values are not supported" + return ("%0.20f" % value).rstrip("0").rstrip(".") + + +class UUIDConvertor(Convertor[uuid.UUID]): + regex = "[0-9a-fA-F]{8}-?[0-9a-fA-F]{4}-?[0-9a-fA-F]{4}-?[0-9a-fA-F]{4}-?[0-9a-fA-F]{12}" + + def convert(self, value: str) -> uuid.UUID: + return uuid.UUID(value) + + def to_string(self, value: uuid.UUID) -> str: + return str(value) + + +CONVERTOR_TYPES: dict[str, Convertor[typing.Any]] = { + "str": StringConvertor(), + "path": PathConvertor(), + "int": IntegerConvertor(), + "float": FloatConvertor(), + "uuid": UUIDConvertor(), +} + + +def register_url_convertor(key: str, convertor: Convertor[typing.Any]) -> None: + CONVERTOR_TYPES[key] = convertor diff --git a/lib/python3.12/site-packages/starlette/datastructures.py b/lib/python3.12/site-packages/starlette/datastructures.py new file mode 100644 index 0000000000000000000000000000000000000000..f5d74d25f22b2360518229158af6a92c27248010 --- /dev/null +++ b/lib/python3.12/site-packages/starlette/datastructures.py @@ -0,0 +1,674 @@ +from __future__ import annotations + +import typing +from shlex import shlex +from urllib.parse import SplitResult, parse_qsl, urlencode, urlsplit + +from starlette.concurrency import run_in_threadpool +from starlette.types import Scope + + +class Address(typing.NamedTuple): + host: str + port: int + + +_KeyType = typing.TypeVar("_KeyType") +# Mapping keys are invariant but their values are covariant since +# you can only read them +# that is, you can't do `Mapping[str, Animal]()["fido"] = Dog()` +_CovariantValueType = typing.TypeVar("_CovariantValueType", covariant=True) + + +class URL: + def __init__( + self, + url: str = "", + scope: Scope | None = None, + **components: typing.Any, + ) -> None: + if scope is not None: + assert not url, 'Cannot set both "url" and "scope".' + assert not components, 'Cannot set both "scope" and "**components".' + scheme = scope.get("scheme", "http") + server = scope.get("server", None) + path = scope["path"] + query_string = scope.get("query_string", b"") + + host_header = None + for key, value in scope["headers"]: + if key == b"host": + host_header = value.decode("latin-1") + break + + if host_header is not None: + url = f"{scheme}://{host_header}{path}" + elif server is None: + url = path + else: + host, port = server + default_port = {"http": 80, "https": 443, "ws": 80, "wss": 443}[scheme] + if port == default_port: + url = f"{scheme}://{host}{path}" + else: + url = f"{scheme}://{host}:{port}{path}" + + if query_string: + url += "?" + query_string.decode() + elif components: + assert not url, 'Cannot set both "url" and "**components".' + url = URL("").replace(**components).components.geturl() + + self._url = url + + @property + def components(self) -> SplitResult: + if not hasattr(self, "_components"): + self._components = urlsplit(self._url) + return self._components + + @property + def scheme(self) -> str: + return self.components.scheme + + @property + def netloc(self) -> str: + return self.components.netloc + + @property + def path(self) -> str: + return self.components.path + + @property + def query(self) -> str: + return self.components.query + + @property + def fragment(self) -> str: + return self.components.fragment + + @property + def username(self) -> None | str: + return self.components.username + + @property + def password(self) -> None | str: + return self.components.password + + @property + def hostname(self) -> None | str: + return self.components.hostname + + @property + def port(self) -> int | None: + return self.components.port + + @property + def is_secure(self) -> bool: + return self.scheme in ("https", "wss") + + def replace(self, **kwargs: typing.Any) -> URL: + if "username" in kwargs or "password" in kwargs or "hostname" in kwargs or "port" in kwargs: + hostname = kwargs.pop("hostname", None) + port = kwargs.pop("port", self.port) + username = kwargs.pop("username", self.username) + password = kwargs.pop("password", self.password) + + if hostname is None: + netloc = self.netloc + _, _, hostname = netloc.rpartition("@") + + if hostname[-1] != "]": + hostname = hostname.rsplit(":", 1)[0] + + netloc = hostname + if port is not None: + netloc += f":{port}" + if username is not None: + userpass = username + if password is not None: + userpass += f":{password}" + netloc = f"{userpass}@{netloc}" + + kwargs["netloc"] = netloc + + components = self.components._replace(**kwargs) + return self.__class__(components.geturl()) + + def include_query_params(self, **kwargs: typing.Any) -> URL: + params = MultiDict(parse_qsl(self.query, keep_blank_values=True)) + params.update({str(key): str(value) for key, value in kwargs.items()}) + query = urlencode(params.multi_items()) + return self.replace(query=query) + + def replace_query_params(self, **kwargs: typing.Any) -> URL: + query = urlencode([(str(key), str(value)) for key, value in kwargs.items()]) + return self.replace(query=query) + + def remove_query_params(self, keys: str | typing.Sequence[str]) -> URL: + if isinstance(keys, str): + keys = [keys] + params = MultiDict(parse_qsl(self.query, keep_blank_values=True)) + for key in keys: + params.pop(key, None) + query = urlencode(params.multi_items()) + return self.replace(query=query) + + def __eq__(self, other: typing.Any) -> bool: + return str(self) == str(other) + + def __str__(self) -> str: + return self._url + + def __repr__(self) -> str: + url = str(self) + if self.password: + url = str(self.replace(password="********")) + return f"{self.__class__.__name__}({repr(url)})" + + +class URLPath(str): + """ + A URL path string that may also hold an associated protocol and/or host. + Used by the routing to return `url_path_for` matches. + """ + + def __new__(cls, path: str, protocol: str = "", host: str = "") -> URLPath: + assert protocol in ("http", "websocket", "") + return str.__new__(cls, path) + + def __init__(self, path: str, protocol: str = "", host: str = "") -> None: + self.protocol = protocol + self.host = host + + def make_absolute_url(self, base_url: str | URL) -> URL: + if isinstance(base_url, str): + base_url = URL(base_url) + if self.protocol: + scheme = { + "http": {True: "https", False: "http"}, + "websocket": {True: "wss", False: "ws"}, + }[self.protocol][base_url.is_secure] + else: + scheme = base_url.scheme + + netloc = self.host or base_url.netloc + path = base_url.path.rstrip("/") + str(self) + return URL(scheme=scheme, netloc=netloc, path=path) + + +class Secret: + """ + Holds a string value that should not be revealed in tracebacks etc. + You should cast the value to `str` at the point it is required. + """ + + def __init__(self, value: str): + self._value = value + + def __repr__(self) -> str: + class_name = self.__class__.__name__ + return f"{class_name}('**********')" + + def __str__(self) -> str: + return self._value + + def __bool__(self) -> bool: + return bool(self._value) + + +class CommaSeparatedStrings(typing.Sequence[str]): + def __init__(self, value: str | typing.Sequence[str]): + if isinstance(value, str): + splitter = shlex(value, posix=True) + splitter.whitespace = "," + splitter.whitespace_split = True + self._items = [item.strip() for item in splitter] + else: + self._items = list(value) + + def __len__(self) -> int: + return len(self._items) + + def __getitem__(self, index: int | slice) -> typing.Any: + return self._items[index] + + def __iter__(self) -> typing.Iterator[str]: + return iter(self._items) + + def __repr__(self) -> str: + class_name = self.__class__.__name__ + items = [item for item in self] + return f"{class_name}({items!r})" + + def __str__(self) -> str: + return ", ".join(repr(item) for item in self) + + +class ImmutableMultiDict(typing.Mapping[_KeyType, _CovariantValueType]): + _dict: dict[_KeyType, _CovariantValueType] + + def __init__( + self, + *args: ImmutableMultiDict[_KeyType, _CovariantValueType] + | typing.Mapping[_KeyType, _CovariantValueType] + | typing.Iterable[tuple[_KeyType, _CovariantValueType]], + **kwargs: typing.Any, + ) -> None: + assert len(args) < 2, "Too many arguments." + + value: typing.Any = args[0] if args else [] + if kwargs: + value = ImmutableMultiDict(value).multi_items() + ImmutableMultiDict(kwargs).multi_items() + + if not value: + _items: list[tuple[typing.Any, typing.Any]] = [] + elif hasattr(value, "multi_items"): + value = typing.cast(ImmutableMultiDict[_KeyType, _CovariantValueType], value) + _items = list(value.multi_items()) + elif hasattr(value, "items"): + value = typing.cast(typing.Mapping[_KeyType, _CovariantValueType], value) + _items = list(value.items()) + else: + value = typing.cast("list[tuple[typing.Any, typing.Any]]", value) + _items = list(value) + + self._dict = {k: v for k, v in _items} + self._list = _items + + def getlist(self, key: typing.Any) -> list[_CovariantValueType]: + return [item_value for item_key, item_value in self._list if item_key == key] + + def keys(self) -> typing.KeysView[_KeyType]: + return self._dict.keys() + + def values(self) -> typing.ValuesView[_CovariantValueType]: + return self._dict.values() + + def items(self) -> typing.ItemsView[_KeyType, _CovariantValueType]: + return self._dict.items() + + def multi_items(self) -> list[tuple[_KeyType, _CovariantValueType]]: + return list(self._list) + + def __getitem__(self, key: _KeyType) -> _CovariantValueType: + return self._dict[key] + + def __contains__(self, key: typing.Any) -> bool: + return key in self._dict + + def __iter__(self) -> typing.Iterator[_KeyType]: + return iter(self.keys()) + + def __len__(self) -> int: + return len(self._dict) + + def __eq__(self, other: typing.Any) -> bool: + if not isinstance(other, self.__class__): + return False + return sorted(self._list) == sorted(other._list) + + def __repr__(self) -> str: + class_name = self.__class__.__name__ + items = self.multi_items() + return f"{class_name}({items!r})" + + +class MultiDict(ImmutableMultiDict[typing.Any, typing.Any]): + def __setitem__(self, key: typing.Any, value: typing.Any) -> None: + self.setlist(key, [value]) + + def __delitem__(self, key: typing.Any) -> None: + self._list = [(k, v) for k, v in self._list if k != key] + del self._dict[key] + + def pop(self, key: typing.Any, default: typing.Any = None) -> typing.Any: + self._list = [(k, v) for k, v in self._list if k != key] + return self._dict.pop(key, default) + + def popitem(self) -> tuple[typing.Any, typing.Any]: + key, value = self._dict.popitem() + self._list = [(k, v) for k, v in self._list if k != key] + return key, value + + def poplist(self, key: typing.Any) -> list[typing.Any]: + values = [v for k, v in self._list if k == key] + self.pop(key) + return values + + def clear(self) -> None: + self._dict.clear() + self._list.clear() + + def setdefault(self, key: typing.Any, default: typing.Any = None) -> typing.Any: + if key not in self: + self._dict[key] = default + self._list.append((key, default)) + + return self[key] + + def setlist(self, key: typing.Any, values: list[typing.Any]) -> None: + if not values: + self.pop(key, None) + else: + existing_items = [(k, v) for (k, v) in self._list if k != key] + self._list = existing_items + [(key, value) for value in values] + self._dict[key] = values[-1] + + def append(self, key: typing.Any, value: typing.Any) -> None: + self._list.append((key, value)) + self._dict[key] = value + + def update( + self, + *args: MultiDict | typing.Mapping[typing.Any, typing.Any] | list[tuple[typing.Any, typing.Any]], + **kwargs: typing.Any, + ) -> None: + value = MultiDict(*args, **kwargs) + existing_items = [(k, v) for (k, v) in self._list if k not in value.keys()] + self._list = existing_items + value.multi_items() + self._dict.update(value) + + +class QueryParams(ImmutableMultiDict[str, str]): + """ + An immutable multidict. + """ + + def __init__( + self, + *args: ImmutableMultiDict[typing.Any, typing.Any] + | typing.Mapping[typing.Any, typing.Any] + | list[tuple[typing.Any, typing.Any]] + | str + | bytes, + **kwargs: typing.Any, + ) -> None: + assert len(args) < 2, "Too many arguments." + + value = args[0] if args else [] + + if isinstance(value, str): + super().__init__(parse_qsl(value, keep_blank_values=True), **kwargs) + elif isinstance(value, bytes): + super().__init__(parse_qsl(value.decode("latin-1"), keep_blank_values=True), **kwargs) + else: + super().__init__(*args, **kwargs) # type: ignore[arg-type] + self._list = [(str(k), str(v)) for k, v in self._list] + self._dict = {str(k): str(v) for k, v in self._dict.items()} + + def __str__(self) -> str: + return urlencode(self._list) + + def __repr__(self) -> str: + class_name = self.__class__.__name__ + query_string = str(self) + return f"{class_name}({query_string!r})" + + +class UploadFile: + """ + An uploaded file included as part of the request data. + """ + + def __init__( + self, + file: typing.BinaryIO, + *, + size: int | None = None, + filename: str | None = None, + headers: Headers | None = None, + ) -> None: + self.filename = filename + self.file = file + self.size = size + self.headers = headers or Headers() + + @property + def content_type(self) -> str | None: + return self.headers.get("content-type", None) + + @property + def _in_memory(self) -> bool: + # check for SpooledTemporaryFile._rolled + rolled_to_disk = getattr(self.file, "_rolled", True) + return not rolled_to_disk + + async def write(self, data: bytes) -> None: + if self.size is not None: + self.size += len(data) + + if self._in_memory: + self.file.write(data) + else: + await run_in_threadpool(self.file.write, data) + + async def read(self, size: int = -1) -> bytes: + if self._in_memory: + return self.file.read(size) + return await run_in_threadpool(self.file.read, size) + + async def seek(self, offset: int) -> None: + if self._in_memory: + self.file.seek(offset) + else: + await run_in_threadpool(self.file.seek, offset) + + async def close(self) -> None: + if self._in_memory: + self.file.close() + else: + await run_in_threadpool(self.file.close) + + def __repr__(self) -> str: + return f"{self.__class__.__name__}(filename={self.filename!r}, size={self.size!r}, headers={self.headers!r})" + + +class FormData(ImmutableMultiDict[str, typing.Union[UploadFile, str]]): + """ + An immutable multidict, containing both file uploads and text input. + """ + + def __init__( + self, + *args: FormData | typing.Mapping[str, str | UploadFile] | list[tuple[str, str | UploadFile]], + **kwargs: str | UploadFile, + ) -> None: + super().__init__(*args, **kwargs) + + async def close(self) -> None: + for key, value in self.multi_items(): + if isinstance(value, UploadFile): + await value.close() + + +class Headers(typing.Mapping[str, str]): + """ + An immutable, case-insensitive multidict. + """ + + def __init__( + self, + headers: typing.Mapping[str, str] | None = None, + raw: list[tuple[bytes, bytes]] | None = None, + scope: typing.MutableMapping[str, typing.Any] | None = None, + ) -> None: + self._list: list[tuple[bytes, bytes]] = [] + if headers is not None: + assert raw is None, 'Cannot set both "headers" and "raw".' + assert scope is None, 'Cannot set both "headers" and "scope".' + self._list = [(key.lower().encode("latin-1"), value.encode("latin-1")) for key, value in headers.items()] + elif raw is not None: + assert scope is None, 'Cannot set both "raw" and "scope".' + self._list = raw + elif scope is not None: + # scope["headers"] isn't necessarily a list + # it might be a tuple or other iterable + self._list = scope["headers"] = list(scope["headers"]) + + @property + def raw(self) -> list[tuple[bytes, bytes]]: + return list(self._list) + + def keys(self) -> list[str]: # type: ignore[override] + return [key.decode("latin-1") for key, value in self._list] + + def values(self) -> list[str]: # type: ignore[override] + return [value.decode("latin-1") for key, value in self._list] + + def items(self) -> list[tuple[str, str]]: # type: ignore[override] + return [(key.decode("latin-1"), value.decode("latin-1")) for key, value in self._list] + + def getlist(self, key: str) -> list[str]: + get_header_key = key.lower().encode("latin-1") + return [item_value.decode("latin-1") for item_key, item_value in self._list if item_key == get_header_key] + + def mutablecopy(self) -> MutableHeaders: + return MutableHeaders(raw=self._list[:]) + + def __getitem__(self, key: str) -> str: + get_header_key = key.lower().encode("latin-1") + for header_key, header_value in self._list: + if header_key == get_header_key: + return header_value.decode("latin-1") + raise KeyError(key) + + def __contains__(self, key: typing.Any) -> bool: + get_header_key = key.lower().encode("latin-1") + for header_key, header_value in self._list: + if header_key == get_header_key: + return True + return False + + def __iter__(self) -> typing.Iterator[typing.Any]: + return iter(self.keys()) + + def __len__(self) -> int: + return len(self._list) + + def __eq__(self, other: typing.Any) -> bool: + if not isinstance(other, Headers): + return False + return sorted(self._list) == sorted(other._list) + + def __repr__(self) -> str: + class_name = self.__class__.__name__ + as_dict = dict(self.items()) + if len(as_dict) == len(self): + return f"{class_name}({as_dict!r})" + return f"{class_name}(raw={self.raw!r})" + + +class MutableHeaders(Headers): + def __setitem__(self, key: str, value: str) -> None: + """ + Set the header `key` to `value`, removing any duplicate entries. + Retains insertion order. + """ + set_key = key.lower().encode("latin-1") + set_value = value.encode("latin-1") + + found_indexes: list[int] = [] + for idx, (item_key, item_value) in enumerate(self._list): + if item_key == set_key: + found_indexes.append(idx) + + for idx in reversed(found_indexes[1:]): + del self._list[idx] + + if found_indexes: + idx = found_indexes[0] + self._list[idx] = (set_key, set_value) + else: + self._list.append((set_key, set_value)) + + def __delitem__(self, key: str) -> None: + """ + Remove the header `key`. + """ + del_key = key.lower().encode("latin-1") + + pop_indexes: list[int] = [] + for idx, (item_key, item_value) in enumerate(self._list): + if item_key == del_key: + pop_indexes.append(idx) + + for idx in reversed(pop_indexes): + del self._list[idx] + + def __ior__(self, other: typing.Mapping[str, str]) -> MutableHeaders: + if not isinstance(other, typing.Mapping): + raise TypeError(f"Expected a mapping but got {other.__class__.__name__}") + self.update(other) + return self + + def __or__(self, other: typing.Mapping[str, str]) -> MutableHeaders: + if not isinstance(other, typing.Mapping): + raise TypeError(f"Expected a mapping but got {other.__class__.__name__}") + new = self.mutablecopy() + new.update(other) + return new + + @property + def raw(self) -> list[tuple[bytes, bytes]]: + return self._list + + def setdefault(self, key: str, value: str) -> str: + """ + If the header `key` does not exist, then set it to `value`. + Returns the header value. + """ + set_key = key.lower().encode("latin-1") + set_value = value.encode("latin-1") + + for idx, (item_key, item_value) in enumerate(self._list): + if item_key == set_key: + return item_value.decode("latin-1") + self._list.append((set_key, set_value)) + return value + + def update(self, other: typing.Mapping[str, str]) -> None: + for key, val in other.items(): + self[key] = val + + def append(self, key: str, value: str) -> None: + """ + Append a header, preserving any duplicate entries. + """ + append_key = key.lower().encode("latin-1") + append_value = value.encode("latin-1") + self._list.append((append_key, append_value)) + + def add_vary_header(self, vary: str) -> None: + existing = self.get("vary") + if existing is not None: + vary = ", ".join([existing, vary]) + self["vary"] = vary + + +class State: + """ + An object that can be used to store arbitrary state. + + Used for `request.state` and `app.state`. + """ + + _state: dict[str, typing.Any] + + def __init__(self, state: dict[str, typing.Any] | None = None): + if state is None: + state = {} + super().__setattr__("_state", state) + + def __setattr__(self, key: typing.Any, value: typing.Any) -> None: + self._state[key] = value + + def __getattr__(self, key: typing.Any) -> typing.Any: + try: + return self._state[key] + except KeyError: + message = "'{}' object has no attribute '{}'" + raise AttributeError(message.format(self.__class__.__name__, key)) + + def __delattr__(self, key: typing.Any) -> None: + del self._state[key] diff --git a/lib/python3.12/site-packages/starlette/endpoints.py b/lib/python3.12/site-packages/starlette/endpoints.py new file mode 100644 index 0000000000000000000000000000000000000000..107690266e16e85add5b7960e1915d153ceb8ccc --- /dev/null +++ b/lib/python3.12/site-packages/starlette/endpoints.py @@ -0,0 +1,122 @@ +from __future__ import annotations + +import json +import typing + +from starlette import status +from starlette._utils import is_async_callable +from starlette.concurrency import run_in_threadpool +from starlette.exceptions import HTTPException +from starlette.requests import Request +from starlette.responses import PlainTextResponse, Response +from starlette.types import Message, Receive, Scope, Send +from starlette.websockets import WebSocket + + +class HTTPEndpoint: + def __init__(self, scope: Scope, receive: Receive, send: Send) -> None: + assert scope["type"] == "http" + self.scope = scope + self.receive = receive + self.send = send + self._allowed_methods = [ + method + for method in ("GET", "HEAD", "POST", "PUT", "PATCH", "DELETE", "OPTIONS") + if getattr(self, method.lower(), None) is not None + ] + + def __await__(self) -> typing.Generator[typing.Any, None, None]: + return self.dispatch().__await__() + + async def dispatch(self) -> None: + request = Request(self.scope, receive=self.receive) + handler_name = "get" if request.method == "HEAD" and not hasattr(self, "head") else request.method.lower() + + handler: typing.Callable[[Request], typing.Any] = getattr(self, handler_name, self.method_not_allowed) + is_async = is_async_callable(handler) + if is_async: + response = await handler(request) + else: + response = await run_in_threadpool(handler, request) + await response(self.scope, self.receive, self.send) + + async def method_not_allowed(self, request: Request) -> Response: + # If we're running inside a starlette application then raise an + # exception, so that the configurable exception handler can deal with + # returning the response. For plain ASGI apps, just return the response. + headers = {"Allow": ", ".join(self._allowed_methods)} + if "app" in self.scope: + raise HTTPException(status_code=405, headers=headers) + return PlainTextResponse("Method Not Allowed", status_code=405, headers=headers) + + +class WebSocketEndpoint: + encoding: str | None = None # May be "text", "bytes", or "json". + + def __init__(self, scope: Scope, receive: Receive, send: Send) -> None: + assert scope["type"] == "websocket" + self.scope = scope + self.receive = receive + self.send = send + + def __await__(self) -> typing.Generator[typing.Any, None, None]: + return self.dispatch().__await__() + + async def dispatch(self) -> None: + websocket = WebSocket(self.scope, receive=self.receive, send=self.send) + await self.on_connect(websocket) + + close_code = status.WS_1000_NORMAL_CLOSURE + + try: + while True: + message = await websocket.receive() + if message["type"] == "websocket.receive": + data = await self.decode(websocket, message) + await self.on_receive(websocket, data) + elif message["type"] == "websocket.disconnect": # pragma: no branch + close_code = int(message.get("code") or status.WS_1000_NORMAL_CLOSURE) + break + except Exception as exc: + close_code = status.WS_1011_INTERNAL_ERROR + raise exc + finally: + await self.on_disconnect(websocket, close_code) + + async def decode(self, websocket: WebSocket, message: Message) -> typing.Any: + if self.encoding == "text": + if "text" not in message: + await websocket.close(code=status.WS_1003_UNSUPPORTED_DATA) + raise RuntimeError("Expected text websocket messages, but got bytes") + return message["text"] + + elif self.encoding == "bytes": + if "bytes" not in message: + await websocket.close(code=status.WS_1003_UNSUPPORTED_DATA) + raise RuntimeError("Expected bytes websocket messages, but got text") + return message["bytes"] + + elif self.encoding == "json": + if message.get("text") is not None: + text = message["text"] + else: + text = message["bytes"].decode("utf-8") + + try: + return json.loads(text) + except json.decoder.JSONDecodeError: + await websocket.close(code=status.WS_1003_UNSUPPORTED_DATA) + raise RuntimeError("Malformed JSON data received.") + + assert self.encoding is None, f"Unsupported 'encoding' attribute {self.encoding}" + return message["text"] if message.get("text") else message["bytes"] + + async def on_connect(self, websocket: WebSocket) -> None: + """Override to handle an incoming websocket connection""" + await websocket.accept() + + async def on_receive(self, websocket: WebSocket, data: typing.Any) -> None: + """Override to handle an incoming websocket message""" + + async def on_disconnect(self, websocket: WebSocket, close_code: int) -> None: + """Override to handle a disconnecting websocket""" diff --git a/lib/python3.12/site-packages/starlette/exceptions.py b/lib/python3.12/site-packages/starlette/exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..9ad3527b94ce034bb3967b3bf0969403118d4284 --- /dev/null +++ b/lib/python3.12/site-packages/starlette/exceptions.py @@ -0,0 +1,33 @@ +from __future__ import annotations + +import http +from collections.abc import Mapping + + +class HTTPException(Exception): + def __init__(self, status_code: int, detail: str | None = None, headers: Mapping[str, str] | None = None) -> None: + if detail is None: + detail = http.HTTPStatus(status_code).phrase + self.status_code = status_code + self.detail = detail + self.headers = headers + + def __str__(self) -> str: + return f"{self.status_code}: {self.detail}" + + def __repr__(self) -> str: + class_name = self.__class__.__name__ + return f"{class_name}(status_code={self.status_code!r}, detail={self.detail!r})" + + +class WebSocketException(Exception): + def __init__(self, code: int, reason: str | None = None) -> None: + self.code = code + self.reason = reason or "" + + def __str__(self) -> str: + return f"{self.code}: {self.reason}" + + def __repr__(self) -> str: + class_name = self.__class__.__name__ + return f"{class_name}(code={self.code!r}, reason={self.reason!r})" diff --git a/lib/python3.12/site-packages/starlette/formparsers.py b/lib/python3.12/site-packages/starlette/formparsers.py new file mode 100644 index 0000000000000000000000000000000000000000..4551d6887221990e5b6b613a9502d727b6755c34 --- /dev/null +++ b/lib/python3.12/site-packages/starlette/formparsers.py @@ -0,0 +1,275 @@ +from __future__ import annotations + +import typing +from dataclasses import dataclass, field +from enum import Enum +from tempfile import SpooledTemporaryFile +from urllib.parse import unquote_plus + +from starlette.datastructures import FormData, Headers, UploadFile + +if typing.TYPE_CHECKING: + import python_multipart as multipart + from python_multipart.multipart import MultipartCallbacks, QuerystringCallbacks, parse_options_header +else: + try: + try: + import python_multipart as multipart + from python_multipart.multipart import parse_options_header + except ModuleNotFoundError: # pragma: no cover + import multipart + from multipart.multipart import parse_options_header + except ModuleNotFoundError: # pragma: no cover + multipart = None + parse_options_header = None + + +class FormMessage(Enum): + FIELD_START = 1 + FIELD_NAME = 2 + FIELD_DATA = 3 + FIELD_END = 4 + END = 5 + + +@dataclass +class MultipartPart: + content_disposition: bytes | None = None + field_name: str = "" + data: bytearray = field(default_factory=bytearray) + file: UploadFile | None = None + item_headers: list[tuple[bytes, bytes]] = field(default_factory=list) + + +def _user_safe_decode(src: bytes | bytearray, codec: str) -> str: + try: + return src.decode(codec) + except (UnicodeDecodeError, LookupError): + return src.decode("latin-1") + + +class MultiPartException(Exception): + def __init__(self, message: str) -> None: + self.message = message + + +class FormParser: + def __init__(self, headers: Headers, stream: typing.AsyncGenerator[bytes, None]) -> None: + assert multipart is not None, "The `python-multipart` library must be installed to use form parsing." + self.headers = headers + self.stream = stream + self.messages: list[tuple[FormMessage, bytes]] = [] + + def on_field_start(self) -> None: + message = (FormMessage.FIELD_START, b"") + self.messages.append(message) + + def on_field_name(self, data: bytes, start: int, end: int) -> None: + message = (FormMessage.FIELD_NAME, data[start:end]) + self.messages.append(message) + + def on_field_data(self, data: bytes, start: int, end: int) -> None: + message = (FormMessage.FIELD_DATA, data[start:end]) + self.messages.append(message) + + def on_field_end(self) -> None: + message = (FormMessage.FIELD_END, b"") + self.messages.append(message) + + def on_end(self) -> None: + message = (FormMessage.END, b"") + self.messages.append(message) + + async def parse(self) -> FormData: + # Callbacks dictionary. + callbacks: QuerystringCallbacks = { + "on_field_start": self.on_field_start, + "on_field_name": self.on_field_name, + "on_field_data": self.on_field_data, + "on_field_end": self.on_field_end, + "on_end": self.on_end, + } + + # Create the parser. + parser = multipart.QuerystringParser(callbacks) + field_name = b"" + field_value = b"" + + items: list[tuple[str, str | UploadFile]] = [] + + # Feed the parser with data from the request. + async for chunk in self.stream: + if chunk: + parser.write(chunk) + else: + parser.finalize() + messages = list(self.messages) + self.messages.clear() + for message_type, message_bytes in messages: + if message_type == FormMessage.FIELD_START: + field_name = b"" + field_value = b"" + elif message_type == FormMessage.FIELD_NAME: + field_name += message_bytes + elif message_type == FormMessage.FIELD_DATA: + field_value += message_bytes + elif message_type == FormMessage.FIELD_END: + name = unquote_plus(field_name.decode("latin-1")) + value = unquote_plus(field_value.decode("latin-1")) + items.append((name, value)) + + return FormData(items) + + +class MultiPartParser: + spool_max_size = 1024 * 1024 # 1MB + """The maximum size of the spooled temporary file used to store file data.""" + max_part_size = 1024 * 1024 # 1MB + """The maximum size of a part in the multipart request.""" + + def __init__( + self, + headers: Headers, + stream: typing.AsyncGenerator[bytes, None], + *, + max_files: int | float = 1000, + max_fields: int | float = 1000, + max_part_size: int = 1024 * 1024, # 1MB + ) -> None: + assert multipart is not None, "The `python-multipart` library must be installed to use form parsing." + self.headers = headers + self.stream = stream + self.max_files = max_files + self.max_fields = max_fields + self.items: list[tuple[str, str | UploadFile]] = [] + self._current_files = 0 + self._current_fields = 0 + self._current_partial_header_name: bytes = b"" + self._current_partial_header_value: bytes = b"" + self._current_part = MultipartPart() + self._charset = "" + self._file_parts_to_write: list[tuple[MultipartPart, bytes]] = [] + self._file_parts_to_finish: list[MultipartPart] = [] + self._files_to_close_on_error: list[SpooledTemporaryFile[bytes]] = [] + self.max_part_size = max_part_size + + def on_part_begin(self) -> None: + self._current_part = MultipartPart() + + def on_part_data(self, data: bytes, start: int, end: int) -> None: + message_bytes = data[start:end] + if self._current_part.file is None: + if len(self._current_part.data) + len(message_bytes) > self.max_part_size: + raise MultiPartException(f"Part exceeded maximum size of {int(self.max_part_size / 1024)}KB.") + self._current_part.data.extend(message_bytes) + else: + self._file_parts_to_write.append((self._current_part, message_bytes)) + + def on_part_end(self) -> None: + if self._current_part.file is None: + self.items.append( + ( + self._current_part.field_name, + _user_safe_decode(self._current_part.data, self._charset), + ) + ) + else: + self._file_parts_to_finish.append(self._current_part) + # The file can be added to the items right now even though it's not + # finished yet, because it will be finished in the `parse()` method, before + # self.items is used in the return value. + self.items.append((self._current_part.field_name, self._current_part.file)) + + def on_header_field(self, data: bytes, start: int, end: int) -> None: + self._current_partial_header_name += data[start:end] + + def on_header_value(self, data: bytes, start: int, end: int) -> None: + self._current_partial_header_value += data[start:end] + + def on_header_end(self) -> None: + field = self._current_partial_header_name.lower() + if field == b"content-disposition": + self._current_part.content_disposition = self._current_partial_header_value + self._current_part.item_headers.append((field, self._current_partial_header_value)) + self._current_partial_header_name = b"" + self._current_partial_header_value = b"" + + def on_headers_finished(self) -> None: + disposition, options = parse_options_header(self._current_part.content_disposition) + try: + self._current_part.field_name = _user_safe_decode(options[b"name"], self._charset) + except KeyError: + raise MultiPartException('The Content-Disposition header field "name" must be provided.') + if b"filename" in options: + self._current_files += 1 + if self._current_files > self.max_files: + raise MultiPartException(f"Too many files. Maximum number of files is {self.max_files}.") + filename = _user_safe_decode(options[b"filename"], self._charset) + tempfile = SpooledTemporaryFile(max_size=self.spool_max_size) + self._files_to_close_on_error.append(tempfile) + self._current_part.file = UploadFile( + file=tempfile, # type: ignore[arg-type] + size=0, + filename=filename, + headers=Headers(raw=self._current_part.item_headers), + ) + else: + self._current_fields += 1 + if self._current_fields > self.max_fields: + raise MultiPartException(f"Too many fields. Maximum number of fields is {self.max_fields}.") + self._current_part.file = None + + def on_end(self) -> None: + pass + + async def parse(self) -> FormData: + # Parse the Content-Type header to get the multipart boundary. + _, params = parse_options_header(self.headers["Content-Type"]) + charset = params.get(b"charset", "utf-8") + if isinstance(charset, bytes): + charset = charset.decode("latin-1") + self._charset = charset + try: + boundary = params[b"boundary"] + except KeyError: + raise MultiPartException("Missing boundary in multipart.") + + # Callbacks dictionary. + callbacks: MultipartCallbacks = { + "on_part_begin": self.on_part_begin, + "on_part_data": self.on_part_data, + "on_part_end": self.on_part_end, + "on_header_field": self.on_header_field, + "on_header_value": self.on_header_value, + "on_header_end": self.on_header_end, + "on_headers_finished": self.on_headers_finished, + "on_end": self.on_end, + } + + # Create the parser. + parser = multipart.MultipartParser(boundary, callbacks) + try: + # Feed the parser with data from the request. + async for chunk in self.stream: + parser.write(chunk) + # Write file data, it needs to use await with the UploadFile methods + # that call the corresponding file methods *in a threadpool*, + # otherwise, if they were called directly in the callback methods above + # (regular, non-async functions), that would block the event loop in + # the main thread. + for part, data in self._file_parts_to_write: + assert part.file # for type checkers + await part.file.write(data) + for part in self._file_parts_to_finish: + assert part.file # for type checkers + await part.file.seek(0) + self._file_parts_to_write.clear() + self._file_parts_to_finish.clear() + except MultiPartException as exc: + # Close all the files if there was an error. + for file in self._files_to_close_on_error: + file.close() + raise exc + + parser.finalize() + return FormData(self.items) diff --git a/lib/python3.12/site-packages/starlette/middleware/__init__.py b/lib/python3.12/site-packages/starlette/middleware/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..b99538a272d985e5288faff04d2e7971b938d613 --- /dev/null +++ b/lib/python3.12/site-packages/starlette/middleware/__init__.py @@ -0,0 +1,42 @@ +from __future__ import annotations + +import sys +from collections.abc import Iterator +from typing import Any, Protocol + +if sys.version_info >= (3, 10): # pragma: no cover + from typing import ParamSpec +else: # pragma: no cover + from typing_extensions import ParamSpec + +from starlette.types import ASGIApp + +P = ParamSpec("P") + + +class _MiddlewareFactory(Protocol[P]): + def __call__(self, app: ASGIApp, /, *args: P.args, **kwargs: P.kwargs) -> ASGIApp: ... # pragma: no cover + + +class Middleware: + def __init__( + self, + cls: _MiddlewareFactory[P], + *args: P.args, + **kwargs: P.kwargs, + ) -> None: + self.cls = cls + self.args = args + self.kwargs = kwargs + + def __iter__(self) -> Iterator[Any]: + as_tuple = (self.cls, self.args, self.kwargs) + return iter(as_tuple) + + def __repr__(self) -> str: + class_name = self.__class__.__name__ + args_strings = [f"{value!r}" for value in self.args] + option_strings = [f"{key}={value!r}" for key, value in self.kwargs.items()] + name = getattr(self.cls, "__name__", "") + args_repr = ", ".join([name] + args_strings + option_strings) + return f"{class_name}({args_repr})" diff --git a/lib/python3.12/site-packages/starlette/middleware/__pycache__/__init__.cpython-312.pyc b/lib/python3.12/site-packages/starlette/middleware/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..07eddfcf80530d7e72097ecef526dfae51898285 Binary files /dev/null and b/lib/python3.12/site-packages/starlette/middleware/__pycache__/__init__.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/starlette/middleware/__pycache__/authentication.cpython-312.pyc b/lib/python3.12/site-packages/starlette/middleware/__pycache__/authentication.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5de7d881aec00f6b91bdc2157f75166702af8e65 Binary files /dev/null and b/lib/python3.12/site-packages/starlette/middleware/__pycache__/authentication.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/starlette/middleware/__pycache__/base.cpython-312.pyc b/lib/python3.12/site-packages/starlette/middleware/__pycache__/base.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2547fc8e32cdfff194cb8368c91e3e516ec44061 Binary files /dev/null and b/lib/python3.12/site-packages/starlette/middleware/__pycache__/base.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/starlette/middleware/__pycache__/cors.cpython-312.pyc b/lib/python3.12/site-packages/starlette/middleware/__pycache__/cors.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bd839eb8438a42eb0ce28cf98d5f542024c0b696 Binary files /dev/null and b/lib/python3.12/site-packages/starlette/middleware/__pycache__/cors.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/starlette/middleware/__pycache__/errors.cpython-312.pyc b/lib/python3.12/site-packages/starlette/middleware/__pycache__/errors.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4f73621725e67856bfe37beced7487a1665b4cbb Binary files /dev/null and b/lib/python3.12/site-packages/starlette/middleware/__pycache__/errors.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/starlette/middleware/__pycache__/exceptions.cpython-312.pyc b/lib/python3.12/site-packages/starlette/middleware/__pycache__/exceptions.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a0ed3988b36dbfc36b55b9f999f0eee406d7e2ab Binary files /dev/null and b/lib/python3.12/site-packages/starlette/middleware/__pycache__/exceptions.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/starlette/middleware/__pycache__/gzip.cpython-312.pyc b/lib/python3.12/site-packages/starlette/middleware/__pycache__/gzip.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b1cc7823f23128889ce94fc967ff1ab7cce9ecf9 Binary files /dev/null and b/lib/python3.12/site-packages/starlette/middleware/__pycache__/gzip.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/starlette/middleware/__pycache__/httpsredirect.cpython-312.pyc b/lib/python3.12/site-packages/starlette/middleware/__pycache__/httpsredirect.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e2e589a7e6afe28e6e2ce8f65fb93714076d5322 Binary files /dev/null and b/lib/python3.12/site-packages/starlette/middleware/__pycache__/httpsredirect.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/starlette/middleware/__pycache__/sessions.cpython-312.pyc b/lib/python3.12/site-packages/starlette/middleware/__pycache__/sessions.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7c1f29dc11de4c6b874ab5433a8f556422601dce Binary files /dev/null and b/lib/python3.12/site-packages/starlette/middleware/__pycache__/sessions.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/starlette/middleware/__pycache__/trustedhost.cpython-312.pyc b/lib/python3.12/site-packages/starlette/middleware/__pycache__/trustedhost.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1dc2f4d95f5ed4265c347249784fcb89fb89f51f Binary files /dev/null and b/lib/python3.12/site-packages/starlette/middleware/__pycache__/trustedhost.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/starlette/middleware/__pycache__/wsgi.cpython-312.pyc b/lib/python3.12/site-packages/starlette/middleware/__pycache__/wsgi.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3ee14a515ef70c1783db077b59b4aa742b67066f Binary files /dev/null and b/lib/python3.12/site-packages/starlette/middleware/__pycache__/wsgi.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/starlette/middleware/authentication.py b/lib/python3.12/site-packages/starlette/middleware/authentication.py new file mode 100644 index 0000000000000000000000000000000000000000..8555ee0780e98b052eb463d55a1c18e39b257762 --- /dev/null +++ b/lib/python3.12/site-packages/starlette/middleware/authentication.py @@ -0,0 +1,52 @@ +from __future__ import annotations + +import typing + +from starlette.authentication import ( + AuthCredentials, + AuthenticationBackend, + AuthenticationError, + UnauthenticatedUser, +) +from starlette.requests import HTTPConnection +from starlette.responses import PlainTextResponse, Response +from starlette.types import ASGIApp, Receive, Scope, Send + + +class AuthenticationMiddleware: + def __init__( + self, + app: ASGIApp, + backend: AuthenticationBackend, + on_error: typing.Callable[[HTTPConnection, AuthenticationError], Response] | None = None, + ) -> None: + self.app = app + self.backend = backend + self.on_error: typing.Callable[[HTTPConnection, AuthenticationError], Response] = ( + on_error if on_error is not None else self.default_on_error + ) + + async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None: + if scope["type"] not in ["http", "websocket"]: + await self.app(scope, receive, send) + return + + conn = HTTPConnection(scope) + try: + auth_result = await self.backend.authenticate(conn) + except AuthenticationError as exc: + response = self.on_error(conn, exc) + if scope["type"] == "websocket": + await send({"type": "websocket.close", "code": 1000}) + else: + await response(scope, receive, send) + return + + if auth_result is None: + auth_result = AuthCredentials(), UnauthenticatedUser() + scope["auth"], scope["user"] = auth_result + await self.app(scope, receive, send) + + @staticmethod + def default_on_error(conn: HTTPConnection, exc: Exception) -> Response: + return PlainTextResponse(str(exc), status_code=400) diff --git a/lib/python3.12/site-packages/starlette/middleware/base.py b/lib/python3.12/site-packages/starlette/middleware/base.py new file mode 100644 index 0000000000000000000000000000000000000000..b49ab611f4d25e217e11ee7a53013082b182afce --- /dev/null +++ b/lib/python3.12/site-packages/starlette/middleware/base.py @@ -0,0 +1,222 @@ +from __future__ import annotations + +import typing + +import anyio + +from starlette._utils import collapse_excgroups +from starlette.requests import ClientDisconnect, Request +from starlette.responses import AsyncContentStream, Response +from starlette.types import ASGIApp, Message, Receive, Scope, Send + +RequestResponseEndpoint = typing.Callable[[Request], typing.Awaitable[Response]] +DispatchFunction = typing.Callable[[Request, RequestResponseEndpoint], typing.Awaitable[Response]] +T = typing.TypeVar("T") + + +class _CachedRequest(Request): + """ + If the user calls Request.body() from their dispatch function + we cache the entire request body in memory and pass that to downstream middlewares, + but if they call Request.stream() then all we do is send an + empty body so that downstream things don't hang forever. + """ + + def __init__(self, scope: Scope, receive: Receive): + super().__init__(scope, receive) + self._wrapped_rcv_disconnected = False + self._wrapped_rcv_consumed = False + self._wrapped_rc_stream = self.stream() + + async def wrapped_receive(self) -> Message: + # wrapped_rcv state 1: disconnected + if self._wrapped_rcv_disconnected: + # we've already sent a disconnect to the downstream app + # we don't need to wait to get another one + # (although most ASGI servers will just keep sending it) + return {"type": "http.disconnect"} + # wrapped_rcv state 1: consumed but not yet disconnected + if self._wrapped_rcv_consumed: + # since the downstream app has consumed us all that is left + # is to send it a disconnect + if self._is_disconnected: + # the middleware has already seen the disconnect + # since we know the client is disconnected no need to wait + # for the message + self._wrapped_rcv_disconnected = True + return {"type": "http.disconnect"} + # we don't know yet if the client is disconnected or not + # so we'll wait until we get that message + msg = await self.receive() + if msg["type"] != "http.disconnect": # pragma: no cover + # at this point a disconnect is all that we should be receiving + # if we get something else, things went wrong somewhere + raise RuntimeError(f"Unexpected message received: {msg['type']}") + self._wrapped_rcv_disconnected = True + return msg + + # wrapped_rcv state 3: not yet consumed + if getattr(self, "_body", None) is not None: + # body() was called, we return it even if the client disconnected + self._wrapped_rcv_consumed = True + return { + "type": "http.request", + "body": self._body, + "more_body": False, + } + elif self._stream_consumed: + # stream() was called to completion + # return an empty body so that downstream apps don't hang + # waiting for a disconnect + self._wrapped_rcv_consumed = True + return { + "type": "http.request", + "body": b"", + "more_body": False, + } + else: + # body() was never called and stream() wasn't consumed + try: + stream = self.stream() + chunk = await stream.__anext__() + self._wrapped_rcv_consumed = self._stream_consumed + return { + "type": "http.request", + "body": chunk, + "more_body": not self._stream_consumed, + } + except ClientDisconnect: + self._wrapped_rcv_disconnected = True + return {"type": "http.disconnect"} + + +class BaseHTTPMiddleware: + def __init__(self, app: ASGIApp, dispatch: DispatchFunction | None = None) -> None: + self.app = app + self.dispatch_func = self.dispatch if dispatch is None else dispatch + + async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None: + if scope["type"] != "http": + await self.app(scope, receive, send) + return + + request = _CachedRequest(scope, receive) + wrapped_receive = request.wrapped_receive + response_sent = anyio.Event() + app_exc: Exception | None = None + exception_already_raised = False + + async def call_next(request: Request) -> Response: + async def receive_or_disconnect() -> Message: + if response_sent.is_set(): + return {"type": "http.disconnect"} + + async with anyio.create_task_group() as task_group: + + async def wrap(func: typing.Callable[[], typing.Awaitable[T]]) -> T: + result = await func() + task_group.cancel_scope.cancel() + return result + + task_group.start_soon(wrap, response_sent.wait) + message = await wrap(wrapped_receive) + + if response_sent.is_set(): + return {"type": "http.disconnect"} + + return message + + async def send_no_error(message: Message) -> None: + try: + await send_stream.send(message) + except anyio.BrokenResourceError: + # recv_stream has been closed, i.e. response_sent has been set. + return + + async def coro() -> None: + nonlocal app_exc + + with send_stream: + try: + await self.app(scope, receive_or_disconnect, send_no_error) + except Exception as exc: + app_exc = exc + + task_group.start_soon(coro) + + try: + message = await recv_stream.receive() + info = message.get("info", None) + if message["type"] == "http.response.debug" and info is not None: + message = await recv_stream.receive() + except anyio.EndOfStream: + if app_exc is not None: + nonlocal exception_already_raised + exception_already_raised = True + raise app_exc + raise RuntimeError("No response returned.") + + assert message["type"] == "http.response.start" + + async def body_stream() -> typing.AsyncGenerator[bytes, None]: + async for message in recv_stream: + assert message["type"] == "http.response.body" + body = message.get("body", b"") + if body: + yield body + if not message.get("more_body", False): + break + + response = _StreamingResponse(status_code=message["status"], content=body_stream(), info=info) + response.raw_headers = message["headers"] + return response + + streams: anyio.create_memory_object_stream[Message] = anyio.create_memory_object_stream() + send_stream, recv_stream = streams + with recv_stream, send_stream, collapse_excgroups(): + async with anyio.create_task_group() as task_group: + response = await self.dispatch_func(request, call_next) + await response(scope, wrapped_receive, send) + response_sent.set() + recv_stream.close() + if app_exc is not None and not exception_already_raised: + raise app_exc + + async def dispatch(self, request: Request, call_next: RequestResponseEndpoint) -> Response: + raise NotImplementedError() # pragma: no cover + + +class _StreamingResponse(Response): + def __init__( + self, + content: AsyncContentStream, + status_code: int = 200, + headers: typing.Mapping[str, str] | None = None, + media_type: str | None = None, + info: typing.Mapping[str, typing.Any] | None = None, + ) -> None: + self.info = info + self.body_iterator = content + self.status_code = status_code + self.media_type = media_type + self.init_headers(headers) + self.background = None + + async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None: + if self.info is not None: + await send({"type": "http.response.debug", "info": self.info}) + await send( + { + "type": "http.response.start", + "status": self.status_code, + "headers": self.raw_headers, + } + ) + + async for chunk in self.body_iterator: + await send({"type": "http.response.body", "body": chunk, "more_body": True}) + + await send({"type": "http.response.body", "body": b"", "more_body": False}) + + if self.background: + await self.background() diff --git a/lib/python3.12/site-packages/starlette/middleware/cors.py b/lib/python3.12/site-packages/starlette/middleware/cors.py new file mode 100644 index 0000000000000000000000000000000000000000..61502691abdcde4ce790cb16c4d28002a6241311 --- /dev/null +++ b/lib/python3.12/site-packages/starlette/middleware/cors.py @@ -0,0 +1,172 @@ +from __future__ import annotations + +import functools +import re +import typing + +from starlette.datastructures import Headers, MutableHeaders +from starlette.responses import PlainTextResponse, Response +from starlette.types import ASGIApp, Message, Receive, Scope, Send + +ALL_METHODS = ("DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT") +SAFELISTED_HEADERS = {"Accept", "Accept-Language", "Content-Language", "Content-Type"} + + +class CORSMiddleware: + def __init__( + self, + app: ASGIApp, + allow_origins: typing.Sequence[str] = (), + allow_methods: typing.Sequence[str] = ("GET",), + allow_headers: typing.Sequence[str] = (), + allow_credentials: bool = False, + allow_origin_regex: str | None = None, + expose_headers: typing.Sequence[str] = (), + max_age: int = 600, + ) -> None: + if "*" in allow_methods: + allow_methods = ALL_METHODS + + compiled_allow_origin_regex = None + if allow_origin_regex is not None: + compiled_allow_origin_regex = re.compile(allow_origin_regex) + + allow_all_origins = "*" in allow_origins + allow_all_headers = "*" in allow_headers + preflight_explicit_allow_origin = not allow_all_origins or allow_credentials + + simple_headers = {} + if allow_all_origins: + simple_headers["Access-Control-Allow-Origin"] = "*" + if allow_credentials: + simple_headers["Access-Control-Allow-Credentials"] = "true" + if expose_headers: + simple_headers["Access-Control-Expose-Headers"] = ", ".join(expose_headers) + + preflight_headers = {} + if preflight_explicit_allow_origin: + # The origin value will be set in preflight_response() if it is allowed. + preflight_headers["Vary"] = "Origin" + else: + preflight_headers["Access-Control-Allow-Origin"] = "*" + preflight_headers.update( + { + "Access-Control-Allow-Methods": ", ".join(allow_methods), + "Access-Control-Max-Age": str(max_age), + } + ) + allow_headers = sorted(SAFELISTED_HEADERS | set(allow_headers)) + if allow_headers and not allow_all_headers: + preflight_headers["Access-Control-Allow-Headers"] = ", ".join(allow_headers) + if allow_credentials: + preflight_headers["Access-Control-Allow-Credentials"] = "true" + + self.app = app + self.allow_origins = allow_origins + self.allow_methods = allow_methods + self.allow_headers = [h.lower() for h in allow_headers] + self.allow_all_origins = allow_all_origins + self.allow_all_headers = allow_all_headers + self.preflight_explicit_allow_origin = preflight_explicit_allow_origin + self.allow_origin_regex = compiled_allow_origin_regex + self.simple_headers = simple_headers + self.preflight_headers = preflight_headers + + async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None: + if scope["type"] != "http": # pragma: no cover + await self.app(scope, receive, send) + return + + method = scope["method"] + headers = Headers(scope=scope) + origin = headers.get("origin") + + if origin is None: + await self.app(scope, receive, send) + return + + if method == "OPTIONS" and "access-control-request-method" in headers: + response = self.preflight_response(request_headers=headers) + await response(scope, receive, send) + return + + await self.simple_response(scope, receive, send, request_headers=headers) + + def is_allowed_origin(self, origin: str) -> bool: + if self.allow_all_origins: + return True + + if self.allow_origin_regex is not None and self.allow_origin_regex.fullmatch(origin): + return True + + return origin in self.allow_origins + + def preflight_response(self, request_headers: Headers) -> Response: + requested_origin = request_headers["origin"] + requested_method = request_headers["access-control-request-method"] + requested_headers = request_headers.get("access-control-request-headers") + + headers = dict(self.preflight_headers) + failures = [] + + if self.is_allowed_origin(origin=requested_origin): + if self.preflight_explicit_allow_origin: + # The "else" case is already accounted for in self.preflight_headers + # and the value would be "*". + headers["Access-Control-Allow-Origin"] = requested_origin + else: + failures.append("origin") + + if requested_method not in self.allow_methods: + failures.append("method") + + # If we allow all headers, then we have to mirror back any requested + # headers in the response. + if self.allow_all_headers and requested_headers is not None: + headers["Access-Control-Allow-Headers"] = requested_headers + elif requested_headers is not None: + for header in [h.lower() for h in requested_headers.split(",")]: + if header.strip() not in self.allow_headers: + failures.append("headers") + break + + # We don't strictly need to use 400 responses here, since its up to + # the browser to enforce the CORS policy, but its more informative + # if we do. + if failures: + failure_text = "Disallowed CORS " + ", ".join(failures) + return PlainTextResponse(failure_text, status_code=400, headers=headers) + + return PlainTextResponse("OK", status_code=200, headers=headers) + + async def simple_response(self, scope: Scope, receive: Receive, send: Send, request_headers: Headers) -> None: + send = functools.partial(self.send, send=send, request_headers=request_headers) + await self.app(scope, receive, send) + + async def send(self, message: Message, send: Send, request_headers: Headers) -> None: + if message["type"] != "http.response.start": + await send(message) + return + + message.setdefault("headers", []) + headers = MutableHeaders(scope=message) + headers.update(self.simple_headers) + origin = request_headers["Origin"] + has_cookie = "cookie" in request_headers + + # If request includes any cookie headers, then we must respond + # with the specific origin instead of '*'. + if self.allow_all_origins and has_cookie: + self.allow_explicit_origin(headers, origin) + + # If we only allow specific origins, then we have to mirror back + # the Origin header in the response. + elif not self.allow_all_origins and self.is_allowed_origin(origin=origin): + self.allow_explicit_origin(headers, origin) + + await send(message) + + @staticmethod + def allow_explicit_origin(headers: MutableHeaders, origin: str) -> None: + headers["Access-Control-Allow-Origin"] = origin + headers.add_vary_header("Origin") diff --git a/lib/python3.12/site-packages/starlette/middleware/errors.py b/lib/python3.12/site-packages/starlette/middleware/errors.py new file mode 100644 index 0000000000000000000000000000000000000000..76ad776be2272b6ee6ad1e5cd948762465bf0dcc --- /dev/null +++ b/lib/python3.12/site-packages/starlette/middleware/errors.py @@ -0,0 +1,260 @@ +from __future__ import annotations + +import html +import inspect +import sys +import traceback +import typing + +from starlette._utils import is_async_callable +from starlette.concurrency import run_in_threadpool +from starlette.requests import Request +from starlette.responses import HTMLResponse, PlainTextResponse, Response +from starlette.types import ASGIApp, Message, Receive, Scope, Send + +STYLES = """ +p { + color: #211c1c; +} +.traceback-container { + border: 1px solid #038BB8; +} +.traceback-title { + background-color: #038BB8; + color: lemonchiffon; + padding: 12px; + font-size: 20px; + margin-top: 0px; +} +.frame-line { + padding-left: 10px; + font-family: monospace; +} +.frame-filename { + font-family: monospace; +} +.center-line { + background-color: #038BB8; + color: #f9f6e1; + padding: 5px 0px 5px 5px; +} +.lineno { + margin-right: 5px; +} +.frame-title { + font-weight: unset; + padding: 10px 10px 10px 10px; + background-color: #E4F4FD; + margin-right: 10px; + color: #191f21; + font-size: 17px; + border: 1px solid #c7dce8; +} +.collapse-btn { + float: right; + padding: 0px 5px 1px 5px; + border: solid 1px #96aebb; + cursor: pointer; +} +.collapsed { + display: none; +} +.source-code { + font-family: courier; + font-size: small; + padding-bottom: 10px; +} +""" + +JS = """ + +""" + +TEMPLATE = """ + + + + Starlette Debugger + + +

500 Server Error

+

{error}

+
+

Traceback

+
{exc_html}
+
+ {js} + + +""" + +FRAME_TEMPLATE = """ +
+

File {frame_filename}, + line {frame_lineno}, + in {frame_name} + {collapse_button} +

+
{code_context}
+
+""" # noqa: E501 + +LINE = """ +

+{lineno}. {line}

+""" + +CENTER_LINE = """ +

+{lineno}. {line}

+""" + + +class ServerErrorMiddleware: + """ + Handles returning 500 responses when a server error occurs. + + If 'debug' is set, then traceback responses will be returned, + otherwise the designated 'handler' will be called. + + This middleware class should generally be used to wrap *everything* + else up, so that unhandled exceptions anywhere in the stack + always result in an appropriate 500 response. + """ + + def __init__( + self, + app: ASGIApp, + handler: typing.Callable[[Request, Exception], typing.Any] | None = None, + debug: bool = False, + ) -> None: + self.app = app + self.handler = handler + self.debug = debug + + async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None: + if scope["type"] != "http": + await self.app(scope, receive, send) + return + + response_started = False + + async def _send(message: Message) -> None: + nonlocal response_started, send + + if message["type"] == "http.response.start": + response_started = True + await send(message) + + try: + await self.app(scope, receive, _send) + except Exception as exc: + request = Request(scope) + if self.debug: + # In debug mode, return traceback responses. + response = self.debug_response(request, exc) + elif self.handler is None: + # Use our default 500 error handler. + response = self.error_response(request, exc) + else: + # Use an installed 500 error handler. + if is_async_callable(self.handler): + response = await self.handler(request, exc) + else: + response = await run_in_threadpool(self.handler, request, exc) + + if not response_started: + await response(scope, receive, send) + + # We always continue to raise the exception. + # This allows servers to log the error, or allows test clients + # to optionally raise the error within the test case. + raise exc + + def format_line(self, index: int, line: str, frame_lineno: int, frame_index: int) -> str: + values = { + # HTML escape - line could contain < or > + "line": html.escape(line).replace(" ", " "), + "lineno": (frame_lineno - frame_index) + index, + } + + if index != frame_index: + return LINE.format(**values) + return CENTER_LINE.format(**values) + + def generate_frame_html(self, frame: inspect.FrameInfo, is_collapsed: bool) -> str: + code_context = "".join( + self.format_line( + index, + line, + frame.lineno, + frame.index, # type: ignore[arg-type] + ) + for index, line in enumerate(frame.code_context or []) + ) + + values = { + # HTML escape - filename could contain < or >, especially if it's a virtual + # file e.g. in the REPL + "frame_filename": html.escape(frame.filename), + "frame_lineno": frame.lineno, + # HTML escape - if you try very hard it's possible to name a function with < + # or > + "frame_name": html.escape(frame.function), + "code_context": code_context, + "collapsed": "collapsed" if is_collapsed else "", + "collapse_button": "+" if is_collapsed else "‒", + } + return FRAME_TEMPLATE.format(**values) + + def generate_html(self, exc: Exception, limit: int = 7) -> str: + traceback_obj = traceback.TracebackException.from_exception(exc, capture_locals=True) + + exc_html = "" + is_collapsed = False + exc_traceback = exc.__traceback__ + if exc_traceback is not None: + frames = inspect.getinnerframes(exc_traceback, limit) + for frame in reversed(frames): + exc_html += self.generate_frame_html(frame, is_collapsed) + is_collapsed = True + + if sys.version_info >= (3, 13): # pragma: no cover + exc_type_str = traceback_obj.exc_type_str + else: # pragma: no cover + exc_type_str = traceback_obj.exc_type.__name__ + + # escape error class and text + error = f"{html.escape(exc_type_str)}: {html.escape(str(traceback_obj))}" + + return TEMPLATE.format(styles=STYLES, js=JS, error=error, exc_html=exc_html) + + def generate_plain_text(self, exc: Exception) -> str: + return "".join(traceback.format_exception(type(exc), exc, exc.__traceback__)) + + def debug_response(self, request: Request, exc: Exception) -> Response: + accept = request.headers.get("accept", "") + + if "text/html" in accept: + content = self.generate_html(exc) + return HTMLResponse(content, status_code=500) + content = self.generate_plain_text(exc) + return PlainTextResponse(content, status_code=500) + + def error_response(self, request: Request, exc: Exception) -> Response: + return PlainTextResponse("Internal Server Error", status_code=500) diff --git a/lib/python3.12/site-packages/starlette/middleware/exceptions.py b/lib/python3.12/site-packages/starlette/middleware/exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..981d2fcaef87c2d3fadb54c83afeb88ccdbaf561 --- /dev/null +++ b/lib/python3.12/site-packages/starlette/middleware/exceptions.py @@ -0,0 +1,72 @@ +from __future__ import annotations + +import typing + +from starlette._exception_handler import ( + ExceptionHandlers, + StatusHandlers, + wrap_app_handling_exceptions, +) +from starlette.exceptions import HTTPException, WebSocketException +from starlette.requests import Request +from starlette.responses import PlainTextResponse, Response +from starlette.types import ASGIApp, Receive, Scope, Send +from starlette.websockets import WebSocket + + +class ExceptionMiddleware: + def __init__( + self, + app: ASGIApp, + handlers: typing.Mapping[typing.Any, typing.Callable[[Request, Exception], Response]] | None = None, + debug: bool = False, + ) -> None: + self.app = app + self.debug = debug # TODO: We ought to handle 404 cases if debug is set. + self._status_handlers: StatusHandlers = {} + self._exception_handlers: ExceptionHandlers = { + HTTPException: self.http_exception, + WebSocketException: self.websocket_exception, + } + if handlers is not None: # pragma: no branch + for key, value in handlers.items(): + self.add_exception_handler(key, value) + + def add_exception_handler( + self, + exc_class_or_status_code: int | type[Exception], + handler: typing.Callable[[Request, Exception], Response], + ) -> None: + if isinstance(exc_class_or_status_code, int): + self._status_handlers[exc_class_or_status_code] = handler + else: + assert issubclass(exc_class_or_status_code, Exception) + self._exception_handlers[exc_class_or_status_code] = handler + + async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None: + if scope["type"] not in ("http", "websocket"): + await self.app(scope, receive, send) + return + + scope["starlette.exception_handlers"] = ( + self._exception_handlers, + self._status_handlers, + ) + + conn: Request | WebSocket + if scope["type"] == "http": + conn = Request(scope, receive, send) + else: + conn = WebSocket(scope, receive, send) + + await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send) + + def http_exception(self, request: Request, exc: Exception) -> Response: + assert isinstance(exc, HTTPException) + if exc.status_code in {204, 304}: + return Response(status_code=exc.status_code, headers=exc.headers) + return PlainTextResponse(exc.detail, status_code=exc.status_code, headers=exc.headers) + + async def websocket_exception(self, websocket: WebSocket, exc: Exception) -> None: + assert isinstance(exc, WebSocketException) + await websocket.close(code=exc.code, reason=exc.reason) # pragma: no cover diff --git a/lib/python3.12/site-packages/starlette/middleware/gzip.py b/lib/python3.12/site-packages/starlette/middleware/gzip.py new file mode 100644 index 0000000000000000000000000000000000000000..c7fd5b77e6dcd00aa1dee733647fa9223c878ad1 --- /dev/null +++ b/lib/python3.12/site-packages/starlette/middleware/gzip.py @@ -0,0 +1,141 @@ +import gzip +import io +import typing + +from starlette.datastructures import Headers, MutableHeaders +from starlette.types import ASGIApp, Message, Receive, Scope, Send + +DEFAULT_EXCLUDED_CONTENT_TYPES = ("text/event-stream",) + + +class GZipMiddleware: + def __init__(self, app: ASGIApp, minimum_size: int = 500, compresslevel: int = 9) -> None: + self.app = app + self.minimum_size = minimum_size + self.compresslevel = compresslevel + + async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None: + if scope["type"] != "http": # pragma: no cover + await self.app(scope, receive, send) + return + + headers = Headers(scope=scope) + responder: ASGIApp + if "gzip" in headers.get("Accept-Encoding", ""): + responder = GZipResponder(self.app, self.minimum_size, compresslevel=self.compresslevel) + else: + responder = IdentityResponder(self.app, self.minimum_size) + + await responder(scope, receive, send) + + +class IdentityResponder: + content_encoding: str + + def __init__(self, app: ASGIApp, minimum_size: int) -> None: + self.app = app + self.minimum_size = minimum_size + self.send: Send = unattached_send + self.initial_message: Message = {} + self.started = False + self.content_encoding_set = False + self.content_type_is_excluded = False + + async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None: + self.send = send + await self.app(scope, receive, self.send_with_compression) + + async def send_with_compression(self, message: Message) -> None: + message_type = message["type"] + if message_type == "http.response.start": + # Don't send the initial message until we've determined how to + # modify the outgoing headers correctly. + self.initial_message = message + headers = Headers(raw=self.initial_message["headers"]) + self.content_encoding_set = "content-encoding" in headers + self.content_type_is_excluded = headers.get("content-type", "").startswith(DEFAULT_EXCLUDED_CONTENT_TYPES) + elif message_type == "http.response.body" and (self.content_encoding_set or self.content_type_is_excluded): + if not self.started: + self.started = True + await self.send(self.initial_message) + await self.send(message) + elif message_type == "http.response.body" and not self.started: + self.started = True + body = message.get("body", b"") + more_body = message.get("more_body", False) + if len(body) < self.minimum_size and not more_body: + # Don't apply compression to small outgoing responses. + await self.send(self.initial_message) + await self.send(message) + elif not more_body: + # Standard response. + body = self.apply_compression(body, more_body=False) + + headers = MutableHeaders(raw=self.initial_message["headers"]) + headers.add_vary_header("Accept-Encoding") + if body != message["body"]: + headers["Content-Encoding"] = self.content_encoding + headers["Content-Length"] = str(len(body)) + message["body"] = body + + await self.send(self.initial_message) + await self.send(message) + else: + # Initial body in streaming response. + body = self.apply_compression(body, more_body=True) + + headers = MutableHeaders(raw=self.initial_message["headers"]) + headers.add_vary_header("Accept-Encoding") + if body != message["body"]: + headers["Content-Encoding"] = self.content_encoding + del headers["Content-Length"] + message["body"] = body + + await self.send(self.initial_message) + await self.send(message) + elif message_type == "http.response.body": # pragma: no branch + # Remaining body in streaming response. + body = message.get("body", b"") + more_body = message.get("more_body", False) + + message["body"] = self.apply_compression(body, more_body=more_body) + + await self.send(message) + + def apply_compression(self, body: bytes, *, more_body: bool) -> bytes: + """Apply compression on the response body. + + If more_body is False, any compression file should be closed. If it + isn't, it won't be closed automatically until all background tasks + complete. + """ + return body + + +class GZipResponder(IdentityResponder): + content_encoding = "gzip" + + def __init__(self, app: ASGIApp, minimum_size: int, compresslevel: int = 9) -> None: + super().__init__(app, minimum_size) + + self.gzip_buffer = io.BytesIO() + self.gzip_file = gzip.GzipFile(mode="wb", fileobj=self.gzip_buffer, compresslevel=compresslevel) + + async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None: + with self.gzip_buffer, self.gzip_file: + await super().__call__(scope, receive, send) + + def apply_compression(self, body: bytes, *, more_body: bool) -> bytes: + self.gzip_file.write(body) + if not more_body: + self.gzip_file.close() + + body = self.gzip_buffer.getvalue() + self.gzip_buffer.seek(0) + self.gzip_buffer.truncate() + + return body + + +async def unattached_send(message: Message) -> typing.NoReturn: + raise RuntimeError("send awaitable not set") # pragma: no cover diff --git a/lib/python3.12/site-packages/starlette/middleware/httpsredirect.py b/lib/python3.12/site-packages/starlette/middleware/httpsredirect.py new file mode 100644 index 0000000000000000000000000000000000000000..a8359067ff7afb80e979042077d5fa0fff119ddf --- /dev/null +++ b/lib/python3.12/site-packages/starlette/middleware/httpsredirect.py @@ -0,0 +1,19 @@ +from starlette.datastructures import URL +from starlette.responses import RedirectResponse +from starlette.types import ASGIApp, Receive, Scope, Send + + +class HTTPSRedirectMiddleware: + def __init__(self, app: ASGIApp) -> None: + self.app = app + + async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None: + if scope["type"] in ("http", "websocket") and scope["scheme"] in ("http", "ws"): + url = URL(scope=scope) + redirect_scheme = {"http": "https", "ws": "wss"}[url.scheme] + netloc = url.hostname if url.port in (80, 443) else url.netloc + url = url.replace(scheme=redirect_scheme, netloc=netloc) + response = RedirectResponse(url, status_code=307) + await response(scope, receive, send) + else: + await self.app(scope, receive, send) diff --git a/lib/python3.12/site-packages/starlette/middleware/sessions.py b/lib/python3.12/site-packages/starlette/middleware/sessions.py new file mode 100644 index 0000000000000000000000000000000000000000..5f9fcd883b69fb960b067f316d49401aba425b4c --- /dev/null +++ b/lib/python3.12/site-packages/starlette/middleware/sessions.py @@ -0,0 +1,85 @@ +from __future__ import annotations + +import json +import typing +from base64 import b64decode, b64encode + +import itsdangerous +from itsdangerous.exc import BadSignature + +from starlette.datastructures import MutableHeaders, Secret +from starlette.requests import HTTPConnection +from starlette.types import ASGIApp, Message, Receive, Scope, Send + + +class SessionMiddleware: + def __init__( + self, + app: ASGIApp, + secret_key: str | Secret, + session_cookie: str = "session", + max_age: int | None = 14 * 24 * 60 * 60, # 14 days, in seconds + path: str = "/", + same_site: typing.Literal["lax", "strict", "none"] = "lax", + https_only: bool = False, + domain: str | None = None, + ) -> None: + self.app = app + self.signer = itsdangerous.TimestampSigner(str(secret_key)) + self.session_cookie = session_cookie + self.max_age = max_age + self.path = path + self.security_flags = "httponly; samesite=" + same_site + if https_only: # Secure flag can be used with HTTPS only + self.security_flags += "; secure" + if domain is not None: + self.security_flags += f"; domain={domain}" + + async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None: + if scope["type"] not in ("http", "websocket"): # pragma: no cover + await self.app(scope, receive, send) + return + + connection = HTTPConnection(scope) + initial_session_was_empty = True + + if self.session_cookie in connection.cookies: + data = connection.cookies[self.session_cookie].encode("utf-8") + try: + data = self.signer.unsign(data, max_age=self.max_age) + scope["session"] = json.loads(b64decode(data)) + initial_session_was_empty = False + except BadSignature: + scope["session"] = {} + else: + scope["session"] = {} + + async def send_wrapper(message: Message) -> None: + if message["type"] == "http.response.start": + if scope["session"]: + # We have session data to persist. + data = b64encode(json.dumps(scope["session"]).encode("utf-8")) + data = self.signer.sign(data) + headers = MutableHeaders(scope=message) + header_value = "{session_cookie}={data}; path={path}; {max_age}{security_flags}".format( + session_cookie=self.session_cookie, + data=data.decode("utf-8"), + path=self.path, + max_age=f"Max-Age={self.max_age}; " if self.max_age else "", + security_flags=self.security_flags, + ) + headers.append("Set-Cookie", header_value) + elif not initial_session_was_empty: + # The session has been cleared. + headers = MutableHeaders(scope=message) + header_value = "{session_cookie}={data}; path={path}; {expires}{security_flags}".format( + session_cookie=self.session_cookie, + data="null", + path=self.path, + expires="expires=Thu, 01 Jan 1970 00:00:00 GMT; ", + security_flags=self.security_flags, + ) + headers.append("Set-Cookie", header_value) + await send(message) + + await self.app(scope, receive, send_wrapper) diff --git a/lib/python3.12/site-packages/starlette/middleware/trustedhost.py b/lib/python3.12/site-packages/starlette/middleware/trustedhost.py new file mode 100644 index 0000000000000000000000000000000000000000..2d1c999e25f40929aacd526ad8b737bbd6394e1f --- /dev/null +++ b/lib/python3.12/site-packages/starlette/middleware/trustedhost.py @@ -0,0 +1,60 @@ +from __future__ import annotations + +import typing + +from starlette.datastructures import URL, Headers +from starlette.responses import PlainTextResponse, RedirectResponse, Response +from starlette.types import ASGIApp, Receive, Scope, Send + +ENFORCE_DOMAIN_WILDCARD = "Domain wildcard patterns must be like '*.example.com'." + + +class TrustedHostMiddleware: + def __init__( + self, + app: ASGIApp, + allowed_hosts: typing.Sequence[str] | None = None, + www_redirect: bool = True, + ) -> None: + if allowed_hosts is None: + allowed_hosts = ["*"] + + for pattern in allowed_hosts: + assert "*" not in pattern[1:], ENFORCE_DOMAIN_WILDCARD + if pattern.startswith("*") and pattern != "*": + assert pattern.startswith("*."), ENFORCE_DOMAIN_WILDCARD + self.app = app + self.allowed_hosts = list(allowed_hosts) + self.allow_any = "*" in allowed_hosts + self.www_redirect = www_redirect + + async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None: + if self.allow_any or scope["type"] not in ( + "http", + "websocket", + ): # pragma: no cover + await self.app(scope, receive, send) + return + + headers = Headers(scope=scope) + host = headers.get("host", "").split(":")[0] + is_valid_host = False + found_www_redirect = False + for pattern in self.allowed_hosts: + if host == pattern or (pattern.startswith("*") and host.endswith(pattern[1:])): + is_valid_host = True + break + elif "www." + host == pattern: + found_www_redirect = True + + if is_valid_host: + await self.app(scope, receive, send) + else: + response: Response + if found_www_redirect and self.www_redirect: + url = URL(scope=scope) + redirect_url = url.replace(netloc="www." + url.netloc) + response = RedirectResponse(url=str(redirect_url)) + else: + response = PlainTextResponse("Invalid host header", status_code=400) + await response(scope, receive, send) diff --git a/lib/python3.12/site-packages/starlette/middleware/wsgi.py b/lib/python3.12/site-packages/starlette/middleware/wsgi.py new file mode 100644 index 0000000000000000000000000000000000000000..6e0a3fae6c176485e506b498200ac22c499c6c50 --- /dev/null +++ b/lib/python3.12/site-packages/starlette/middleware/wsgi.py @@ -0,0 +1,152 @@ +from __future__ import annotations + +import io +import math +import sys +import typing +import warnings + +import anyio +from anyio.abc import ObjectReceiveStream, ObjectSendStream + +from starlette.types import Receive, Scope, Send + +warnings.warn( + "starlette.middleware.wsgi is deprecated and will be removed in a future release. " + "Please refer to https://github.com/abersheeran/a2wsgi as a replacement.", + DeprecationWarning, +) + + +def build_environ(scope: Scope, body: bytes) -> dict[str, typing.Any]: + """ + Builds a scope and request body into a WSGI environ object. + """ + + script_name = scope.get("root_path", "").encode("utf8").decode("latin1") + path_info = scope["path"].encode("utf8").decode("latin1") + if path_info.startswith(script_name): + path_info = path_info[len(script_name) :] + + environ = { + "REQUEST_METHOD": scope["method"], + "SCRIPT_NAME": script_name, + "PATH_INFO": path_info, + "QUERY_STRING": scope["query_string"].decode("ascii"), + "SERVER_PROTOCOL": f"HTTP/{scope['http_version']}", + "wsgi.version": (1, 0), + "wsgi.url_scheme": scope.get("scheme", "http"), + "wsgi.input": io.BytesIO(body), + "wsgi.errors": sys.stdout, + "wsgi.multithread": True, + "wsgi.multiprocess": True, + "wsgi.run_once": False, + } + + # Get server name and port - required in WSGI, not in ASGI + server = scope.get("server") or ("localhost", 80) + environ["SERVER_NAME"] = server[0] + environ["SERVER_PORT"] = server[1] + + # Get client IP address + if scope.get("client"): + environ["REMOTE_ADDR"] = scope["client"][0] + + # Go through headers and make them into environ entries + for name, value in scope.get("headers", []): + name = name.decode("latin1") + if name == "content-length": + corrected_name = "CONTENT_LENGTH" + elif name == "content-type": + corrected_name = "CONTENT_TYPE" + else: + corrected_name = f"HTTP_{name}".upper().replace("-", "_") + # HTTPbis say only ASCII chars are allowed in headers, but we latin1 just in + # case + value = value.decode("latin1") + if corrected_name in environ: + value = environ[corrected_name] + "," + value + environ[corrected_name] = value + return environ + + +class WSGIMiddleware: + def __init__(self, app: typing.Callable[..., typing.Any]) -> None: + self.app = app + + async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None: + assert scope["type"] == "http" + responder = WSGIResponder(self.app, scope) + await responder(receive, send) + + +class WSGIResponder: + stream_send: ObjectSendStream[typing.MutableMapping[str, typing.Any]] + stream_receive: ObjectReceiveStream[typing.MutableMapping[str, typing.Any]] + + def __init__(self, app: typing.Callable[..., typing.Any], scope: Scope) -> None: + self.app = app + self.scope = scope + self.status = None + self.response_headers = None + self.stream_send, self.stream_receive = anyio.create_memory_object_stream(math.inf) + self.response_started = False + self.exc_info: typing.Any = None + + async def __call__(self, receive: Receive, send: Send) -> None: + body = b"" + more_body = True + while more_body: + message = await receive() + body += message.get("body", b"") + more_body = message.get("more_body", False) + environ = build_environ(self.scope, body) + + async with anyio.create_task_group() as task_group: + task_group.start_soon(self.sender, send) + async with self.stream_send: + await anyio.to_thread.run_sync(self.wsgi, environ, self.start_response) + if self.exc_info is not None: + raise self.exc_info[0].with_traceback(self.exc_info[1], self.exc_info[2]) + + async def sender(self, send: Send) -> None: + async with self.stream_receive: + async for message in self.stream_receive: + await send(message) + + def start_response( + self, + status: str, + response_headers: list[tuple[str, str]], + exc_info: typing.Any = None, + ) -> None: + self.exc_info = exc_info + if not self.response_started: # pragma: no branch + self.response_started = True + status_code_string, _ = status.split(" ", 1) + status_code = int(status_code_string) + headers = [ + (name.strip().encode("ascii").lower(), value.strip().encode("ascii")) + for name, value in response_headers + ] + anyio.from_thread.run( + self.stream_send.send, + { + "type": "http.response.start", + "status": status_code, + "headers": headers, + }, + ) + + def wsgi( + self, + environ: dict[str, typing.Any], + start_response: typing.Callable[..., typing.Any], + ) -> None: + for chunk in self.app(environ, start_response): + anyio.from_thread.run( + self.stream_send.send, + {"type": "http.response.body", "body": chunk, "more_body": True}, + ) + + anyio.from_thread.run(self.stream_send.send, {"type": "http.response.body", "body": b""}) diff --git a/lib/python3.12/site-packages/starlette/py.typed b/lib/python3.12/site-packages/starlette/py.typed new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/lib/python3.12/site-packages/starlette/requests.py b/lib/python3.12/site-packages/starlette/requests.py new file mode 100644 index 0000000000000000000000000000000000000000..7dc04a746064897e5448a6625ae3cfe71ef425e3 --- /dev/null +++ b/lib/python3.12/site-packages/starlette/requests.py @@ -0,0 +1,322 @@ +from __future__ import annotations + +import json +import typing +from http import cookies as http_cookies + +import anyio + +from starlette._utils import AwaitableOrContextManager, AwaitableOrContextManagerWrapper +from starlette.datastructures import URL, Address, FormData, Headers, QueryParams, State +from starlette.exceptions import HTTPException +from starlette.formparsers import FormParser, MultiPartException, MultiPartParser +from starlette.types import Message, Receive, Scope, Send + +if typing.TYPE_CHECKING: + from python_multipart.multipart import parse_options_header + + from starlette.applications import Starlette + from starlette.routing import Router +else: + try: + try: + from python_multipart.multipart import parse_options_header + except ModuleNotFoundError: # pragma: no cover + from multipart.multipart import parse_options_header + except ModuleNotFoundError: # pragma: no cover + parse_options_header = None + + +SERVER_PUSH_HEADERS_TO_COPY = { + "accept", + "accept-encoding", + "accept-language", + "cache-control", + "user-agent", +} + + +def cookie_parser(cookie_string: str) -> dict[str, str]: + """ + This function parses a ``Cookie`` HTTP header into a dict of key/value pairs. + + It attempts to mimic browser cookie parsing behavior: browsers and web servers + frequently disregard the spec (RFC 6265) when setting and reading cookies, + so we attempt to suit the common scenarios here. + + This function has been adapted from Django 3.1.0. + Note: we are explicitly _NOT_ using `SimpleCookie.load` because it is based + on an outdated spec and will fail on lots of input we want to support + """ + cookie_dict: dict[str, str] = {} + for chunk in cookie_string.split(";"): + if "=" in chunk: + key, val = chunk.split("=", 1) + else: + # Assume an empty name per + # https://bugzilla.mozilla.org/show_bug.cgi?id=169091 + key, val = "", chunk + key, val = key.strip(), val.strip() + if key or val: + # unquote using Python's algorithm. + cookie_dict[key] = http_cookies._unquote(val) + return cookie_dict + + +class ClientDisconnect(Exception): + pass + + +class HTTPConnection(typing.Mapping[str, typing.Any]): + """ + A base class for incoming HTTP connections, that is used to provide + any functionality that is common to both `Request` and `WebSocket`. + """ + + def __init__(self, scope: Scope, receive: Receive | None = None) -> None: + assert scope["type"] in ("http", "websocket") + self.scope = scope + + def __getitem__(self, key: str) -> typing.Any: + return self.scope[key] + + def __iter__(self) -> typing.Iterator[str]: + return iter(self.scope) + + def __len__(self) -> int: + return len(self.scope) + + # Don't use the `abc.Mapping.__eq__` implementation. + # Connection instances should never be considered equal + # unless `self is other`. + __eq__ = object.__eq__ + __hash__ = object.__hash__ + + @property + def app(self) -> typing.Any: + return self.scope["app"] + + @property + def url(self) -> URL: + if not hasattr(self, "_url"): # pragma: no branch + self._url = URL(scope=self.scope) + return self._url + + @property + def base_url(self) -> URL: + if not hasattr(self, "_base_url"): + base_url_scope = dict(self.scope) + # This is used by request.url_for, it might be used inside a Mount which + # would have its own child scope with its own root_path, but the base URL + # for url_for should still be the top level app root path. + app_root_path = base_url_scope.get("app_root_path", base_url_scope.get("root_path", "")) + path = app_root_path + if not path.endswith("/"): + path += "/" + base_url_scope["path"] = path + base_url_scope["query_string"] = b"" + base_url_scope["root_path"] = app_root_path + self._base_url = URL(scope=base_url_scope) + return self._base_url + + @property + def headers(self) -> Headers: + if not hasattr(self, "_headers"): + self._headers = Headers(scope=self.scope) + return self._headers + + @property + def query_params(self) -> QueryParams: + if not hasattr(self, "_query_params"): # pragma: no branch + self._query_params = QueryParams(self.scope["query_string"]) + return self._query_params + + @property + def path_params(self) -> dict[str, typing.Any]: + return self.scope.get("path_params", {}) + + @property + def cookies(self) -> dict[str, str]: + if not hasattr(self, "_cookies"): + cookies: dict[str, str] = {} + cookie_header = self.headers.get("cookie") + + if cookie_header: + cookies = cookie_parser(cookie_header) + self._cookies = cookies + return self._cookies + + @property + def client(self) -> Address | None: + # client is a 2 item tuple of (host, port), None if missing + host_port = self.scope.get("client") + if host_port is not None: + return Address(*host_port) + return None + + @property + def session(self) -> dict[str, typing.Any]: + assert "session" in self.scope, "SessionMiddleware must be installed to access request.session" + return self.scope["session"] # type: ignore[no-any-return] + + @property + def auth(self) -> typing.Any: + assert "auth" in self.scope, "AuthenticationMiddleware must be installed to access request.auth" + return self.scope["auth"] + + @property + def user(self) -> typing.Any: + assert "user" in self.scope, "AuthenticationMiddleware must be installed to access request.user" + return self.scope["user"] + + @property + def state(self) -> State: + if not hasattr(self, "_state"): + # Ensure 'state' has an empty dict if it's not already populated. + self.scope.setdefault("state", {}) + # Create a state instance with a reference to the dict in which it should + # store info + self._state = State(self.scope["state"]) + return self._state + + def url_for(self, name: str, /, **path_params: typing.Any) -> URL: + url_path_provider: Router | Starlette | None = self.scope.get("router") or self.scope.get("app") + if url_path_provider is None: + raise RuntimeError("The `url_for` method can only be used inside a Starlette application or with a router.") + url_path = url_path_provider.url_path_for(name, **path_params) + return url_path.make_absolute_url(base_url=self.base_url) + + +async def empty_receive() -> typing.NoReturn: + raise RuntimeError("Receive channel has not been made available") + + +async def empty_send(message: Message) -> typing.NoReturn: + raise RuntimeError("Send channel has not been made available") + + +class Request(HTTPConnection): + _form: FormData | None + + def __init__(self, scope: Scope, receive: Receive = empty_receive, send: Send = empty_send): + super().__init__(scope) + assert scope["type"] == "http" + self._receive = receive + self._send = send + self._stream_consumed = False + self._is_disconnected = False + self._form = None + + @property + def method(self) -> str: + return typing.cast(str, self.scope["method"]) + + @property + def receive(self) -> Receive: + return self._receive + + async def stream(self) -> typing.AsyncGenerator[bytes, None]: + if hasattr(self, "_body"): + yield self._body + yield b"" + return + if self._stream_consumed: + raise RuntimeError("Stream consumed") + while not self._stream_consumed: + message = await self._receive() + if message["type"] == "http.request": + body = message.get("body", b"") + if not message.get("more_body", False): + self._stream_consumed = True + if body: + yield body + elif message["type"] == "http.disconnect": # pragma: no branch + self._is_disconnected = True + raise ClientDisconnect() + yield b"" + + async def body(self) -> bytes: + if not hasattr(self, "_body"): + chunks: list[bytes] = [] + async for chunk in self.stream(): + chunks.append(chunk) + self._body = b"".join(chunks) + return self._body + + async def json(self) -> typing.Any: + if not hasattr(self, "_json"): # pragma: no branch + body = await self.body() + self._json = json.loads(body) + return self._json + + async def _get_form( + self, + *, + max_files: int | float = 1000, + max_fields: int | float = 1000, + max_part_size: int = 1024 * 1024, + ) -> FormData: + if self._form is None: # pragma: no branch + assert parse_options_header is not None, ( + "The `python-multipart` library must be installed to use form parsing." + ) + content_type_header = self.headers.get("Content-Type") + content_type: bytes + content_type, _ = parse_options_header(content_type_header) + if content_type == b"multipart/form-data": + try: + multipart_parser = MultiPartParser( + self.headers, + self.stream(), + max_files=max_files, + max_fields=max_fields, + max_part_size=max_part_size, + ) + self._form = await multipart_parser.parse() + except MultiPartException as exc: + if "app" in self.scope: + raise HTTPException(status_code=400, detail=exc.message) + raise exc + elif content_type == b"application/x-www-form-urlencoded": + form_parser = FormParser(self.headers, self.stream()) + self._form = await form_parser.parse() + else: + self._form = FormData() + return self._form + + def form( + self, + *, + max_files: int | float = 1000, + max_fields: int | float = 1000, + max_part_size: int = 1024 * 1024, + ) -> AwaitableOrContextManager[FormData]: + return AwaitableOrContextManagerWrapper( + self._get_form(max_files=max_files, max_fields=max_fields, max_part_size=max_part_size) + ) + + async def close(self) -> None: + if self._form is not None: # pragma: no branch + await self._form.close() + + async def is_disconnected(self) -> bool: + if not self._is_disconnected: + message: Message = {} + + # If message isn't immediately available, move on + with anyio.CancelScope() as cs: + cs.cancel() + message = await self._receive() + + if message.get("type") == "http.disconnect": + self._is_disconnected = True + + return self._is_disconnected + + async def send_push_promise(self, path: str) -> None: + if "http.response.push" in self.scope.get("extensions", {}): + raw_headers: list[tuple[bytes, bytes]] = [] + for name in SERVER_PUSH_HEADERS_TO_COPY: + for value in self.headers.getlist(name): + raw_headers.append((name.encode("latin-1"), value.encode("latin-1"))) + await self._send({"type": "http.response.push", "path": path, "headers": raw_headers}) diff --git a/lib/python3.12/site-packages/starlette/responses.py b/lib/python3.12/site-packages/starlette/responses.py new file mode 100644 index 0000000000000000000000000000000000000000..81e89faefed9050e59177af5d91872355410e97a --- /dev/null +++ b/lib/python3.12/site-packages/starlette/responses.py @@ -0,0 +1,536 @@ +from __future__ import annotations + +import hashlib +import http.cookies +import json +import os +import re +import stat +import typing +import warnings +from datetime import datetime +from email.utils import format_datetime, formatdate +from functools import partial +from mimetypes import guess_type +from secrets import token_hex +from urllib.parse import quote + +import anyio +import anyio.to_thread + +from starlette._utils import collapse_excgroups +from starlette.background import BackgroundTask +from starlette.concurrency import iterate_in_threadpool +from starlette.datastructures import URL, Headers, MutableHeaders +from starlette.requests import ClientDisconnect +from starlette.types import Receive, Scope, Send + + +class Response: + media_type = None + charset = "utf-8" + + def __init__( + self, + content: typing.Any = None, + status_code: int = 200, + headers: typing.Mapping[str, str] | None = None, + media_type: str | None = None, + background: BackgroundTask | None = None, + ) -> None: + self.status_code = status_code + if media_type is not None: + self.media_type = media_type + self.background = background + self.body = self.render(content) + self.init_headers(headers) + + def render(self, content: typing.Any) -> bytes | memoryview: + if content is None: + return b"" + if isinstance(content, (bytes, memoryview)): + return content + return content.encode(self.charset) # type: ignore + + def init_headers(self, headers: typing.Mapping[str, str] | None = None) -> None: + if headers is None: + raw_headers: list[tuple[bytes, bytes]] = [] + populate_content_length = True + populate_content_type = True + else: + raw_headers = [(k.lower().encode("latin-1"), v.encode("latin-1")) for k, v in headers.items()] + keys = [h[0] for h in raw_headers] + populate_content_length = b"content-length" not in keys + populate_content_type = b"content-type" not in keys + + body = getattr(self, "body", None) + if ( + body is not None + and populate_content_length + and not (self.status_code < 200 or self.status_code in (204, 304)) + ): + content_length = str(len(body)) + raw_headers.append((b"content-length", content_length.encode("latin-1"))) + + content_type = self.media_type + if content_type is not None and populate_content_type: + if content_type.startswith("text/") and "charset=" not in content_type.lower(): + content_type += "; charset=" + self.charset + raw_headers.append((b"content-type", content_type.encode("latin-1"))) + + self.raw_headers = raw_headers + + @property + def headers(self) -> MutableHeaders: + if not hasattr(self, "_headers"): + self._headers = MutableHeaders(raw=self.raw_headers) + return self._headers + + def set_cookie( + self, + key: str, + value: str = "", + max_age: int | None = None, + expires: datetime | str | int | None = None, + path: str | None = "/", + domain: str | None = None, + secure: bool = False, + httponly: bool = False, + samesite: typing.Literal["lax", "strict", "none"] | None = "lax", + ) -> None: + cookie: http.cookies.BaseCookie[str] = http.cookies.SimpleCookie() + cookie[key] = value + if max_age is not None: + cookie[key]["max-age"] = max_age + if expires is not None: + if isinstance(expires, datetime): + cookie[key]["expires"] = format_datetime(expires, usegmt=True) + else: + cookie[key]["expires"] = expires + if path is not None: + cookie[key]["path"] = path + if domain is not None: + cookie[key]["domain"] = domain + if secure: + cookie[key]["secure"] = True + if httponly: + cookie[key]["httponly"] = True + if samesite is not None: + assert samesite.lower() in [ + "strict", + "lax", + "none", + ], "samesite must be either 'strict', 'lax' or 'none'" + cookie[key]["samesite"] = samesite + cookie_val = cookie.output(header="").strip() + self.raw_headers.append((b"set-cookie", cookie_val.encode("latin-1"))) + + def delete_cookie( + self, + key: str, + path: str = "/", + domain: str | None = None, + secure: bool = False, + httponly: bool = False, + samesite: typing.Literal["lax", "strict", "none"] | None = "lax", + ) -> None: + self.set_cookie( + key, + max_age=0, + expires=0, + path=path, + domain=domain, + secure=secure, + httponly=httponly, + samesite=samesite, + ) + + async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None: + prefix = "websocket." if scope["type"] == "websocket" else "" + await send( + { + "type": prefix + "http.response.start", + "status": self.status_code, + "headers": self.raw_headers, + } + ) + await send({"type": prefix + "http.response.body", "body": self.body}) + + if self.background is not None: + await self.background() + + +class HTMLResponse(Response): + media_type = "text/html" + + +class PlainTextResponse(Response): + media_type = "text/plain" + + +class JSONResponse(Response): + media_type = "application/json" + + def __init__( + self, + content: typing.Any, + status_code: int = 200, + headers: typing.Mapping[str, str] | None = None, + media_type: str | None = None, + background: BackgroundTask | None = None, + ) -> None: + super().__init__(content, status_code, headers, media_type, background) + + def render(self, content: typing.Any) -> bytes: + return json.dumps( + content, + ensure_ascii=False, + allow_nan=False, + indent=None, + separators=(",", ":"), + ).encode("utf-8") + + +class RedirectResponse(Response): + def __init__( + self, + url: str | URL, + status_code: int = 307, + headers: typing.Mapping[str, str] | None = None, + background: BackgroundTask | None = None, + ) -> None: + super().__init__(content=b"", status_code=status_code, headers=headers, background=background) + self.headers["location"] = quote(str(url), safe=":/%#?=@[]!$&'()*+,;") + + +Content = typing.Union[str, bytes, memoryview] +SyncContentStream = typing.Iterable[Content] +AsyncContentStream = typing.AsyncIterable[Content] +ContentStream = typing.Union[AsyncContentStream, SyncContentStream] + + +class StreamingResponse(Response): + body_iterator: AsyncContentStream + + def __init__( + self, + content: ContentStream, + status_code: int = 200, + headers: typing.Mapping[str, str] | None = None, + media_type: str | None = None, + background: BackgroundTask | None = None, + ) -> None: + if isinstance(content, typing.AsyncIterable): + self.body_iterator = content + else: + self.body_iterator = iterate_in_threadpool(content) + self.status_code = status_code + self.media_type = self.media_type if media_type is None else media_type + self.background = background + self.init_headers(headers) + + async def listen_for_disconnect(self, receive: Receive) -> None: + while True: + message = await receive() + if message["type"] == "http.disconnect": + break + + async def stream_response(self, send: Send) -> None: + await send( + { + "type": "http.response.start", + "status": self.status_code, + "headers": self.raw_headers, + } + ) + async for chunk in self.body_iterator: + if not isinstance(chunk, (bytes, memoryview)): + chunk = chunk.encode(self.charset) + await send({"type": "http.response.body", "body": chunk, "more_body": True}) + + await send({"type": "http.response.body", "body": b"", "more_body": False}) + + async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None: + spec_version = tuple(map(int, scope.get("asgi", {}).get("spec_version", "2.0").split("."))) + + if spec_version >= (2, 4): + try: + await self.stream_response(send) + except OSError: + raise ClientDisconnect() + else: + with collapse_excgroups(): + async with anyio.create_task_group() as task_group: + + async def wrap(func: typing.Callable[[], typing.Awaitable[None]]) -> None: + await func() + task_group.cancel_scope.cancel() + + task_group.start_soon(wrap, partial(self.stream_response, send)) + await wrap(partial(self.listen_for_disconnect, receive)) + + if self.background is not None: + await self.background() + + +class MalformedRangeHeader(Exception): + def __init__(self, content: str = "Malformed range header.") -> None: + self.content = content + + +class RangeNotSatisfiable(Exception): + def __init__(self, max_size: int) -> None: + self.max_size = max_size + + +_RANGE_PATTERN = re.compile(r"(\d*)-(\d*)") + + +class FileResponse(Response): + chunk_size = 64 * 1024 + + def __init__( + self, + path: str | os.PathLike[str], + status_code: int = 200, + headers: typing.Mapping[str, str] | None = None, + media_type: str | None = None, + background: BackgroundTask | None = None, + filename: str | None = None, + stat_result: os.stat_result | None = None, + method: str | None = None, + content_disposition_type: str = "attachment", + ) -> None: + self.path = path + self.status_code = status_code + self.filename = filename + if method is not None: + warnings.warn( + "The 'method' parameter is not used, and it will be removed.", + DeprecationWarning, + ) + if media_type is None: + media_type = guess_type(filename or path)[0] or "text/plain" + self.media_type = media_type + self.background = background + self.init_headers(headers) + self.headers.setdefault("accept-ranges", "bytes") + if self.filename is not None: + content_disposition_filename = quote(self.filename) + if content_disposition_filename != self.filename: + content_disposition = f"{content_disposition_type}; filename*=utf-8''{content_disposition_filename}" + else: + content_disposition = f'{content_disposition_type}; filename="{self.filename}"' + self.headers.setdefault("content-disposition", content_disposition) + self.stat_result = stat_result + if stat_result is not None: + self.set_stat_headers(stat_result) + + def set_stat_headers(self, stat_result: os.stat_result) -> None: + content_length = str(stat_result.st_size) + last_modified = formatdate(stat_result.st_mtime, usegmt=True) + etag_base = str(stat_result.st_mtime) + "-" + str(stat_result.st_size) + etag = f'"{hashlib.md5(etag_base.encode(), usedforsecurity=False).hexdigest()}"' + + self.headers.setdefault("content-length", content_length) + self.headers.setdefault("last-modified", last_modified) + self.headers.setdefault("etag", etag) + + async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None: + send_header_only: bool = scope["method"].upper() == "HEAD" + if self.stat_result is None: + try: + stat_result = await anyio.to_thread.run_sync(os.stat, self.path) + self.set_stat_headers(stat_result) + except FileNotFoundError: + raise RuntimeError(f"File at path {self.path} does not exist.") + else: + mode = stat_result.st_mode + if not stat.S_ISREG(mode): + raise RuntimeError(f"File at path {self.path} is not a file.") + else: + stat_result = self.stat_result + + headers = Headers(scope=scope) + http_range = headers.get("range") + http_if_range = headers.get("if-range") + + if http_range is None or (http_if_range is not None and not self._should_use_range(http_if_range)): + await self._handle_simple(send, send_header_only) + else: + try: + ranges = self._parse_range_header(http_range, stat_result.st_size) + except MalformedRangeHeader as exc: + return await PlainTextResponse(exc.content, status_code=400)(scope, receive, send) + except RangeNotSatisfiable as exc: + response = PlainTextResponse(status_code=416, headers={"Content-Range": f"*/{exc.max_size}"}) + return await response(scope, receive, send) + + if len(ranges) == 1: + start, end = ranges[0] + await self._handle_single_range(send, start, end, stat_result.st_size, send_header_only) + else: + await self._handle_multiple_ranges(send, ranges, stat_result.st_size, send_header_only) + + if self.background is not None: + await self.background() + + async def _handle_simple(self, send: Send, send_header_only: bool) -> None: + await send({"type": "http.response.start", "status": self.status_code, "headers": self.raw_headers}) + if send_header_only: + await send({"type": "http.response.body", "body": b"", "more_body": False}) + else: + async with await anyio.open_file(self.path, mode="rb") as file: + more_body = True + while more_body: + chunk = await file.read(self.chunk_size) + more_body = len(chunk) == self.chunk_size + await send({"type": "http.response.body", "body": chunk, "more_body": more_body}) + + async def _handle_single_range( + self, send: Send, start: int, end: int, file_size: int, send_header_only: bool + ) -> None: + self.headers["content-range"] = f"bytes {start}-{end - 1}/{file_size}" + self.headers["content-length"] = str(end - start) + await send({"type": "http.response.start", "status": 206, "headers": self.raw_headers}) + if send_header_only: + await send({"type": "http.response.body", "body": b"", "more_body": False}) + else: + async with await anyio.open_file(self.path, mode="rb") as file: + await file.seek(start) + more_body = True + while more_body: + chunk = await file.read(min(self.chunk_size, end - start)) + start += len(chunk) + more_body = len(chunk) == self.chunk_size and start < end + await send({"type": "http.response.body", "body": chunk, "more_body": more_body}) + + async def _handle_multiple_ranges( + self, + send: Send, + ranges: list[tuple[int, int]], + file_size: int, + send_header_only: bool, + ) -> None: + # In firefox and chrome, they use boundary with 95-96 bits entropy (that's roughly 13 bytes). + boundary = token_hex(13) + content_length, header_generator = self.generate_multipart( + ranges, boundary, file_size, self.headers["content-type"] + ) + self.headers["content-range"] = f"multipart/byteranges; boundary={boundary}" + self.headers["content-length"] = str(content_length) + await send({"type": "http.response.start", "status": 206, "headers": self.raw_headers}) + if send_header_only: + await send({"type": "http.response.body", "body": b"", "more_body": False}) + else: + async with await anyio.open_file(self.path, mode="rb") as file: + for start, end in ranges: + await send({"type": "http.response.body", "body": header_generator(start, end), "more_body": True}) + await file.seek(start) + while start < end: + chunk = await file.read(min(self.chunk_size, end - start)) + start += len(chunk) + await send({"type": "http.response.body", "body": chunk, "more_body": True}) + await send({"type": "http.response.body", "body": b"\n", "more_body": True}) + await send( + { + "type": "http.response.body", + "body": f"\n--{boundary}--\n".encode("latin-1"), + "more_body": False, + } + ) + + def _should_use_range(self, http_if_range: str) -> bool: + return http_if_range == self.headers["last-modified"] or http_if_range == self.headers["etag"] + + @staticmethod + def _parse_range_header(http_range: str, file_size: int) -> list[tuple[int, int]]: + ranges: list[tuple[int, int]] = [] + try: + units, range_ = http_range.split("=", 1) + except ValueError: + raise MalformedRangeHeader() + + units = units.strip().lower() + + if units != "bytes": + raise MalformedRangeHeader("Only support bytes range") + + ranges = [ + ( + int(_[0]) if _[0] else file_size - int(_[1]), + int(_[1]) + 1 if _[0] and _[1] and int(_[1]) < file_size else file_size, + ) + for _ in _RANGE_PATTERN.findall(range_) + if _ != ("", "") + ] + + if len(ranges) == 0: + raise MalformedRangeHeader("Range header: range must be requested") + + if any(not (0 <= start < file_size) for start, _ in ranges): + raise RangeNotSatisfiable(file_size) + + if any(start > end for start, end in ranges): + raise MalformedRangeHeader("Range header: start must be less than end") + + if len(ranges) == 1: + return ranges + + # Merge ranges + result: list[tuple[int, int]] = [] + for start, end in ranges: + for p in range(len(result)): + p_start, p_end = result[p] + if start > p_end: + continue + elif end < p_start: + result.insert(p, (start, end)) # THIS IS NOT REACHED! + break + else: + result[p] = (min(start, p_start), max(end, p_end)) + break + else: + result.append((start, end)) + + return result + + def generate_multipart( + self, + ranges: typing.Sequence[tuple[int, int]], + boundary: str, + max_size: int, + content_type: str, + ) -> tuple[int, typing.Callable[[int, int], bytes]]: + r""" + Multipart response headers generator. + + ``` + --{boundary}\n + Content-Type: {content_type}\n + Content-Range: bytes {start}-{end-1}/{max_size}\n + \n + ..........content...........\n + --{boundary}\n + Content-Type: {content_type}\n + Content-Range: bytes {start}-{end-1}/{max_size}\n + \n + ..........content...........\n + --{boundary}--\n + ``` + """ + boundary_len = len(boundary) + static_header_part_len = 44 + boundary_len + len(content_type) + len(str(max_size)) + content_length = sum( + (len(str(start)) + len(str(end - 1)) + static_header_part_len) # Headers + + (end - start) # Content + for start, end in ranges + ) + ( + 5 + boundary_len # --boundary--\n + ) + return ( + content_length, + lambda start, end: ( + f"--{boundary}\nContent-Type: {content_type}\nContent-Range: bytes {start}-{end - 1}/{max_size}\n\n" + ).encode("latin-1"), + ) diff --git a/lib/python3.12/site-packages/starlette/routing.py b/lib/python3.12/site-packages/starlette/routing.py new file mode 100644 index 0000000000000000000000000000000000000000..add7df0c210f95af8310250d3ce7820aaeb170e3 --- /dev/null +++ b/lib/python3.12/site-packages/starlette/routing.py @@ -0,0 +1,874 @@ +from __future__ import annotations + +import contextlib +import functools +import inspect +import re +import traceback +import types +import typing +import warnings +from contextlib import asynccontextmanager +from enum import Enum + +from starlette._exception_handler import wrap_app_handling_exceptions +from starlette._utils import get_route_path, is_async_callable +from starlette.concurrency import run_in_threadpool +from starlette.convertors import CONVERTOR_TYPES, Convertor +from starlette.datastructures import URL, Headers, URLPath +from starlette.exceptions import HTTPException +from starlette.middleware import Middleware +from starlette.requests import Request +from starlette.responses import PlainTextResponse, RedirectResponse, Response +from starlette.types import ASGIApp, Lifespan, Receive, Scope, Send +from starlette.websockets import WebSocket, WebSocketClose + + +class NoMatchFound(Exception): + """ + Raised by `.url_for(name, **path_params)` and `.url_path_for(name, **path_params)` + if no matching route exists. + """ + + def __init__(self, name: str, path_params: dict[str, typing.Any]) -> None: + params = ", ".join(list(path_params.keys())) + super().__init__(f'No route exists for name "{name}" and params "{params}".') + + +class Match(Enum): + NONE = 0 + PARTIAL = 1 + FULL = 2 + + +def iscoroutinefunction_or_partial(obj: typing.Any) -> bool: # pragma: no cover + """ + Correctly determines if an object is a coroutine function, + including those wrapped in functools.partial objects. + """ + warnings.warn( + "iscoroutinefunction_or_partial is deprecated, and will be removed in a future release.", + DeprecationWarning, + ) + while isinstance(obj, functools.partial): + obj = obj.func + return inspect.iscoroutinefunction(obj) + + +def request_response( + func: typing.Callable[[Request], typing.Awaitable[Response] | Response], +) -> ASGIApp: + """ + Takes a function or coroutine `func(request) -> response`, + and returns an ASGI application. + """ + f: typing.Callable[[Request], typing.Awaitable[Response]] = ( + func if is_async_callable(func) else functools.partial(run_in_threadpool, func) # type:ignore + ) + + async def app(scope: Scope, receive: Receive, send: Send) -> None: + request = Request(scope, receive, send) + + async def app(scope: Scope, receive: Receive, send: Send) -> None: + response = await f(request) + await response(scope, receive, send) + + await wrap_app_handling_exceptions(app, request)(scope, receive, send) + + return app + + +def websocket_session( + func: typing.Callable[[WebSocket], typing.Awaitable[None]], +) -> ASGIApp: + """ + Takes a coroutine `func(session)`, and returns an ASGI application. + """ + # assert asyncio.iscoroutinefunction(func), "WebSocket endpoints must be async" + + async def app(scope: Scope, receive: Receive, send: Send) -> None: + session = WebSocket(scope, receive=receive, send=send) + + async def app(scope: Scope, receive: Receive, send: Send) -> None: + await func(session) + + await wrap_app_handling_exceptions(app, session)(scope, receive, send) + + return app + + +def get_name(endpoint: typing.Callable[..., typing.Any]) -> str: + return getattr(endpoint, "__name__", endpoint.__class__.__name__) + + +def replace_params( + path: str, + param_convertors: dict[str, Convertor[typing.Any]], + path_params: dict[str, str], +) -> tuple[str, dict[str, str]]: + for key, value in list(path_params.items()): + if "{" + key + "}" in path: + convertor = param_convertors[key] + value = convertor.to_string(value) + path = path.replace("{" + key + "}", value) + path_params.pop(key) + return path, path_params + + +# Match parameters in URL paths, eg. '{param}', and '{param:int}' +PARAM_REGEX = re.compile("{([a-zA-Z_][a-zA-Z0-9_]*)(:[a-zA-Z_][a-zA-Z0-9_]*)?}") + + +def compile_path( + path: str, +) -> tuple[typing.Pattern[str], str, dict[str, Convertor[typing.Any]]]: + """ + Given a path string, like: "/{username:str}", + or a host string, like: "{subdomain}.mydomain.org", return a three-tuple + of (regex, format, {param_name:convertor}). + + regex: "/(?P[^/]+)" + format: "/{username}" + convertors: {"username": StringConvertor()} + """ + is_host = not path.startswith("/") + + path_regex = "^" + path_format = "" + duplicated_params = set() + + idx = 0 + param_convertors = {} + for match in PARAM_REGEX.finditer(path): + param_name, convertor_type = match.groups("str") + convertor_type = convertor_type.lstrip(":") + assert convertor_type in CONVERTOR_TYPES, f"Unknown path convertor '{convertor_type}'" + convertor = CONVERTOR_TYPES[convertor_type] + + path_regex += re.escape(path[idx : match.start()]) + path_regex += f"(?P<{param_name}>{convertor.regex})" + + path_format += path[idx : match.start()] + path_format += "{%s}" % param_name + + if param_name in param_convertors: + duplicated_params.add(param_name) + + param_convertors[param_name] = convertor + + idx = match.end() + + if duplicated_params: + names = ", ".join(sorted(duplicated_params)) + ending = "s" if len(duplicated_params) > 1 else "" + raise ValueError(f"Duplicated param name{ending} {names} at path {path}") + + if is_host: + # Align with `Host.matches()` behavior, which ignores port. + hostname = path[idx:].split(":")[0] + path_regex += re.escape(hostname) + "$" + else: + path_regex += re.escape(path[idx:]) + "$" + + path_format += path[idx:] + + return re.compile(path_regex), path_format, param_convertors + + +class BaseRoute: + def matches(self, scope: Scope) -> tuple[Match, Scope]: + raise NotImplementedError() # pragma: no cover + + def url_path_for(self, name: str, /, **path_params: typing.Any) -> URLPath: + raise NotImplementedError() # pragma: no cover + + async def handle(self, scope: Scope, receive: Receive, send: Send) -> None: + raise NotImplementedError() # pragma: no cover + + async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None: + """ + A route may be used in isolation as a stand-alone ASGI app. + This is a somewhat contrived case, as they'll almost always be used + within a Router, but could be useful for some tooling and minimal apps. + """ + match, child_scope = self.matches(scope) + if match == Match.NONE: + if scope["type"] == "http": + response = PlainTextResponse("Not Found", status_code=404) + await response(scope, receive, send) + elif scope["type"] == "websocket": # pragma: no branch + websocket_close = WebSocketClose() + await websocket_close(scope, receive, send) + return + + scope.update(child_scope) + await self.handle(scope, receive, send) + + +class Route(BaseRoute): + def __init__( + self, + path: str, + endpoint: typing.Callable[..., typing.Any], + *, + methods: list[str] | None = None, + name: str | None = None, + include_in_schema: bool = True, + middleware: typing.Sequence[Middleware] | None = None, + ) -> None: + assert path.startswith("/"), "Routed paths must start with '/'" + self.path = path + self.endpoint = endpoint + self.name = get_name(endpoint) if name is None else name + self.include_in_schema = include_in_schema + + endpoint_handler = endpoint + while isinstance(endpoint_handler, functools.partial): + endpoint_handler = endpoint_handler.func + if inspect.isfunction(endpoint_handler) or inspect.ismethod(endpoint_handler): + # Endpoint is function or method. Treat it as `func(request) -> response`. + self.app = request_response(endpoint) + if methods is None: + methods = ["GET"] + else: + # Endpoint is a class. Treat it as ASGI. + self.app = endpoint + + if middleware is not None: + for cls, args, kwargs in reversed(middleware): + self.app = cls(self.app, *args, **kwargs) + + if methods is None: + self.methods = None + else: + self.methods = {method.upper() for method in methods} + if "GET" in self.methods: + self.methods.add("HEAD") + + self.path_regex, self.path_format, self.param_convertors = compile_path(path) + + def matches(self, scope: Scope) -> tuple[Match, Scope]: + path_params: dict[str, typing.Any] + if scope["type"] == "http": + route_path = get_route_path(scope) + match = self.path_regex.match(route_path) + if match: + matched_params = match.groupdict() + for key, value in matched_params.items(): + matched_params[key] = self.param_convertors[key].convert(value) + path_params = dict(scope.get("path_params", {})) + path_params.update(matched_params) + child_scope = {"endpoint": self.endpoint, "path_params": path_params} + if self.methods and scope["method"] not in self.methods: + return Match.PARTIAL, child_scope + else: + return Match.FULL, child_scope + return Match.NONE, {} + + def url_path_for(self, name: str, /, **path_params: typing.Any) -> URLPath: + seen_params = set(path_params.keys()) + expected_params = set(self.param_convertors.keys()) + + if name != self.name or seen_params != expected_params: + raise NoMatchFound(name, path_params) + + path, remaining_params = replace_params(self.path_format, self.param_convertors, path_params) + assert not remaining_params + return URLPath(path=path, protocol="http") + + async def handle(self, scope: Scope, receive: Receive, send: Send) -> None: + if self.methods and scope["method"] not in self.methods: + headers = {"Allow": ", ".join(self.methods)} + if "app" in scope: + raise HTTPException(status_code=405, headers=headers) + else: + response = PlainTextResponse("Method Not Allowed", status_code=405, headers=headers) + await response(scope, receive, send) + else: + await self.app(scope, receive, send) + + def __eq__(self, other: typing.Any) -> bool: + return ( + isinstance(other, Route) + and self.path == other.path + and self.endpoint == other.endpoint + and self.methods == other.methods + ) + + def __repr__(self) -> str: + class_name = self.__class__.__name__ + methods = sorted(self.methods or []) + path, name = self.path, self.name + return f"{class_name}(path={path!r}, name={name!r}, methods={methods!r})" + + +class WebSocketRoute(BaseRoute): + def __init__( + self, + path: str, + endpoint: typing.Callable[..., typing.Any], + *, + name: str | None = None, + middleware: typing.Sequence[Middleware] | None = None, + ) -> None: + assert path.startswith("/"), "Routed paths must start with '/'" + self.path = path + self.endpoint = endpoint + self.name = get_name(endpoint) if name is None else name + + endpoint_handler = endpoint + while isinstance(endpoint_handler, functools.partial): + endpoint_handler = endpoint_handler.func + if inspect.isfunction(endpoint_handler) or inspect.ismethod(endpoint_handler): + # Endpoint is function or method. Treat it as `func(websocket)`. + self.app = websocket_session(endpoint) + else: + # Endpoint is a class. Treat it as ASGI. + self.app = endpoint + + if middleware is not None: + for cls, args, kwargs in reversed(middleware): + self.app = cls(self.app, *args, **kwargs) + + self.path_regex, self.path_format, self.param_convertors = compile_path(path) + + def matches(self, scope: Scope) -> tuple[Match, Scope]: + path_params: dict[str, typing.Any] + if scope["type"] == "websocket": + route_path = get_route_path(scope) + match = self.path_regex.match(route_path) + if match: + matched_params = match.groupdict() + for key, value in matched_params.items(): + matched_params[key] = self.param_convertors[key].convert(value) + path_params = dict(scope.get("path_params", {})) + path_params.update(matched_params) + child_scope = {"endpoint": self.endpoint, "path_params": path_params} + return Match.FULL, child_scope + return Match.NONE, {} + + def url_path_for(self, name: str, /, **path_params: typing.Any) -> URLPath: + seen_params = set(path_params.keys()) + expected_params = set(self.param_convertors.keys()) + + if name != self.name or seen_params != expected_params: + raise NoMatchFound(name, path_params) + + path, remaining_params = replace_params(self.path_format, self.param_convertors, path_params) + assert not remaining_params + return URLPath(path=path, protocol="websocket") + + async def handle(self, scope: Scope, receive: Receive, send: Send) -> None: + await self.app(scope, receive, send) + + def __eq__(self, other: typing.Any) -> bool: + return isinstance(other, WebSocketRoute) and self.path == other.path and self.endpoint == other.endpoint + + def __repr__(self) -> str: + return f"{self.__class__.__name__}(path={self.path!r}, name={self.name!r})" + + +class Mount(BaseRoute): + def __init__( + self, + path: str, + app: ASGIApp | None = None, + routes: typing.Sequence[BaseRoute] | None = None, + name: str | None = None, + *, + middleware: typing.Sequence[Middleware] | None = None, + ) -> None: + assert path == "" or path.startswith("/"), "Routed paths must start with '/'" + assert app is not None or routes is not None, "Either 'app=...', or 'routes=' must be specified" + self.path = path.rstrip("/") + if app is not None: + self._base_app: ASGIApp = app + else: + self._base_app = Router(routes=routes) + self.app = self._base_app + if middleware is not None: + for cls, args, kwargs in reversed(middleware): + self.app = cls(self.app, *args, **kwargs) + self.name = name + self.path_regex, self.path_format, self.param_convertors = compile_path(self.path + "/{path:path}") + + @property + def routes(self) -> list[BaseRoute]: + return getattr(self._base_app, "routes", []) + + def matches(self, scope: Scope) -> tuple[Match, Scope]: + path_params: dict[str, typing.Any] + if scope["type"] in ("http", "websocket"): # pragma: no branch + root_path = scope.get("root_path", "") + route_path = get_route_path(scope) + match = self.path_regex.match(route_path) + if match: + matched_params = match.groupdict() + for key, value in matched_params.items(): + matched_params[key] = self.param_convertors[key].convert(value) + remaining_path = "/" + matched_params.pop("path") + matched_path = route_path[: -len(remaining_path)] + path_params = dict(scope.get("path_params", {})) + path_params.update(matched_params) + child_scope = { + "path_params": path_params, + # app_root_path will only be set at the top level scope, + # initialized with the (optional) value of a root_path + # set above/before Starlette. And even though any + # mount will have its own child scope with its own respective + # root_path, the app_root_path will always be available in all + # the child scopes with the same top level value because it's + # set only once here with a default, any other child scope will + # just inherit that app_root_path default value stored in the + # scope. All this is needed to support Request.url_for(), as it + # uses the app_root_path to build the URL path. + "app_root_path": scope.get("app_root_path", root_path), + "root_path": root_path + matched_path, + "endpoint": self.app, + } + return Match.FULL, child_scope + return Match.NONE, {} + + def url_path_for(self, name: str, /, **path_params: typing.Any) -> URLPath: + if self.name is not None and name == self.name and "path" in path_params: + # 'name' matches "". + path_params["path"] = path_params["path"].lstrip("/") + path, remaining_params = replace_params(self.path_format, self.param_convertors, path_params) + if not remaining_params: + return URLPath(path=path) + elif self.name is None or name.startswith(self.name + ":"): + if self.name is None: + # No mount name. + remaining_name = name + else: + # 'name' matches ":". + remaining_name = name[len(self.name) + 1 :] + path_kwarg = path_params.get("path") + path_params["path"] = "" + path_prefix, remaining_params = replace_params(self.path_format, self.param_convertors, path_params) + if path_kwarg is not None: + remaining_params["path"] = path_kwarg + for route in self.routes or []: + try: + url = route.url_path_for(remaining_name, **remaining_params) + return URLPath(path=path_prefix.rstrip("/") + str(url), protocol=url.protocol) + except NoMatchFound: + pass + raise NoMatchFound(name, path_params) + + async def handle(self, scope: Scope, receive: Receive, send: Send) -> None: + await self.app(scope, receive, send) + + def __eq__(self, other: typing.Any) -> bool: + return isinstance(other, Mount) and self.path == other.path and self.app == other.app + + def __repr__(self) -> str: + class_name = self.__class__.__name__ + name = self.name or "" + return f"{class_name}(path={self.path!r}, name={name!r}, app={self.app!r})" + + +class Host(BaseRoute): + def __init__(self, host: str, app: ASGIApp, name: str | None = None) -> None: + assert not host.startswith("/"), "Host must not start with '/'" + self.host = host + self.app = app + self.name = name + self.host_regex, self.host_format, self.param_convertors = compile_path(host) + + @property + def routes(self) -> list[BaseRoute]: + return getattr(self.app, "routes", []) + + def matches(self, scope: Scope) -> tuple[Match, Scope]: + if scope["type"] in ("http", "websocket"): # pragma:no branch + headers = Headers(scope=scope) + host = headers.get("host", "").split(":")[0] + match = self.host_regex.match(host) + if match: + matched_params = match.groupdict() + for key, value in matched_params.items(): + matched_params[key] = self.param_convertors[key].convert(value) + path_params = dict(scope.get("path_params", {})) + path_params.update(matched_params) + child_scope = {"path_params": path_params, "endpoint": self.app} + return Match.FULL, child_scope + return Match.NONE, {} + + def url_path_for(self, name: str, /, **path_params: typing.Any) -> URLPath: + if self.name is not None and name == self.name and "path" in path_params: + # 'name' matches "". + path = path_params.pop("path") + host, remaining_params = replace_params(self.host_format, self.param_convertors, path_params) + if not remaining_params: + return URLPath(path=path, host=host) + elif self.name is None or name.startswith(self.name + ":"): + if self.name is None: + # No mount name. + remaining_name = name + else: + # 'name' matches ":". + remaining_name = name[len(self.name) + 1 :] + host, remaining_params = replace_params(self.host_format, self.param_convertors, path_params) + for route in self.routes or []: + try: + url = route.url_path_for(remaining_name, **remaining_params) + return URLPath(path=str(url), protocol=url.protocol, host=host) + except NoMatchFound: + pass + raise NoMatchFound(name, path_params) + + async def handle(self, scope: Scope, receive: Receive, send: Send) -> None: + await self.app(scope, receive, send) + + def __eq__(self, other: typing.Any) -> bool: + return isinstance(other, Host) and self.host == other.host and self.app == other.app + + def __repr__(self) -> str: + class_name = self.__class__.__name__ + name = self.name or "" + return f"{class_name}(host={self.host!r}, name={name!r}, app={self.app!r})" + + +_T = typing.TypeVar("_T") + + +class _AsyncLiftContextManager(typing.AsyncContextManager[_T]): + def __init__(self, cm: typing.ContextManager[_T]): + self._cm = cm + + async def __aenter__(self) -> _T: + return self._cm.__enter__() + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_value: BaseException | None, + traceback: types.TracebackType | None, + ) -> bool | None: + return self._cm.__exit__(exc_type, exc_value, traceback) + + +def _wrap_gen_lifespan_context( + lifespan_context: typing.Callable[[typing.Any], typing.Generator[typing.Any, typing.Any, typing.Any]], +) -> typing.Callable[[typing.Any], typing.AsyncContextManager[typing.Any]]: + cmgr = contextlib.contextmanager(lifespan_context) + + @functools.wraps(cmgr) + def wrapper(app: typing.Any) -> _AsyncLiftContextManager[typing.Any]: + return _AsyncLiftContextManager(cmgr(app)) + + return wrapper + + +class _DefaultLifespan: + def __init__(self, router: Router): + self._router = router + + async def __aenter__(self) -> None: + await self._router.startup() + + async def __aexit__(self, *exc_info: object) -> None: + await self._router.shutdown() + + def __call__(self: _T, app: object) -> _T: + return self + + +class Router: + def __init__( + self, + routes: typing.Sequence[BaseRoute] | None = None, + redirect_slashes: bool = True, + default: ASGIApp | None = None, + on_startup: typing.Sequence[typing.Callable[[], typing.Any]] | None = None, + on_shutdown: typing.Sequence[typing.Callable[[], typing.Any]] | None = None, + # the generic to Lifespan[AppType] is the type of the top level application + # which the router cannot know statically, so we use typing.Any + lifespan: Lifespan[typing.Any] | None = None, + *, + middleware: typing.Sequence[Middleware] | None = None, + ) -> None: + self.routes = [] if routes is None else list(routes) + self.redirect_slashes = redirect_slashes + self.default = self.not_found if default is None else default + self.on_startup = [] if on_startup is None else list(on_startup) + self.on_shutdown = [] if on_shutdown is None else list(on_shutdown) + + if on_startup or on_shutdown: + warnings.warn( + "The on_startup and on_shutdown parameters are deprecated, and they " + "will be removed on version 1.0. Use the lifespan parameter instead. " + "See more about it on https://www.starlette.io/lifespan/.", + DeprecationWarning, + ) + if lifespan: + warnings.warn( + "The `lifespan` parameter cannot be used with `on_startup` or " + "`on_shutdown`. Both `on_startup` and `on_shutdown` will be " + "ignored." + ) + + if lifespan is None: + self.lifespan_context: Lifespan[typing.Any] = _DefaultLifespan(self) + + elif inspect.isasyncgenfunction(lifespan): + warnings.warn( + "async generator function lifespans are deprecated, " + "use an @contextlib.asynccontextmanager function instead", + DeprecationWarning, + ) + self.lifespan_context = asynccontextmanager( + lifespan, + ) + elif inspect.isgeneratorfunction(lifespan): + warnings.warn( + "generator function lifespans are deprecated, use an @contextlib.asynccontextmanager function instead", + DeprecationWarning, + ) + self.lifespan_context = _wrap_gen_lifespan_context( + lifespan, + ) + else: + self.lifespan_context = lifespan + + self.middleware_stack = self.app + if middleware: + for cls, args, kwargs in reversed(middleware): + self.middleware_stack = cls(self.middleware_stack, *args, **kwargs) + + async def not_found(self, scope: Scope, receive: Receive, send: Send) -> None: + if scope["type"] == "websocket": + websocket_close = WebSocketClose() + await websocket_close(scope, receive, send) + return + + # If we're running inside a starlette application then raise an + # exception, so that the configurable exception handler can deal with + # returning the response. For plain ASGI apps, just return the response. + if "app" in scope: + raise HTTPException(status_code=404) + else: + response = PlainTextResponse("Not Found", status_code=404) + await response(scope, receive, send) + + def url_path_for(self, name: str, /, **path_params: typing.Any) -> URLPath: + for route in self.routes: + try: + return route.url_path_for(name, **path_params) + except NoMatchFound: + pass + raise NoMatchFound(name, path_params) + + async def startup(self) -> None: + """ + Run any `.on_startup` event handlers. + """ + for handler in self.on_startup: + if is_async_callable(handler): + await handler() + else: + handler() + + async def shutdown(self) -> None: + """ + Run any `.on_shutdown` event handlers. + """ + for handler in self.on_shutdown: + if is_async_callable(handler): + await handler() + else: + handler() + + async def lifespan(self, scope: Scope, receive: Receive, send: Send) -> None: + """ + Handle ASGI lifespan messages, which allows us to manage application + startup and shutdown events. + """ + started = False + app: typing.Any = scope.get("app") + await receive() + try: + async with self.lifespan_context(app) as maybe_state: + if maybe_state is not None: + if "state" not in scope: + raise RuntimeError('The server does not support "state" in the lifespan scope.') + scope["state"].update(maybe_state) + await send({"type": "lifespan.startup.complete"}) + started = True + await receive() + except BaseException: + exc_text = traceback.format_exc() + if started: + await send({"type": "lifespan.shutdown.failed", "message": exc_text}) + else: + await send({"type": "lifespan.startup.failed", "message": exc_text}) + raise + else: + await send({"type": "lifespan.shutdown.complete"}) + + async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None: + """ + The main entry point to the Router class. + """ + await self.middleware_stack(scope, receive, send) + + async def app(self, scope: Scope, receive: Receive, send: Send) -> None: + assert scope["type"] in ("http", "websocket", "lifespan") + + if "router" not in scope: + scope["router"] = self + + if scope["type"] == "lifespan": + await self.lifespan(scope, receive, send) + return + + partial = None + + for route in self.routes: + # Determine if any route matches the incoming scope, + # and hand over to the matching route if found. + match, child_scope = route.matches(scope) + if match == Match.FULL: + scope.update(child_scope) + await route.handle(scope, receive, send) + return + elif match == Match.PARTIAL and partial is None: + partial = route + partial_scope = child_scope + + if partial is not None: + #  Handle partial matches. These are cases where an endpoint is + # able to handle the request, but is not a preferred option. + # We use this in particular to deal with "405 Method Not Allowed". + scope.update(partial_scope) + await partial.handle(scope, receive, send) + return + + route_path = get_route_path(scope) + if scope["type"] == "http" and self.redirect_slashes and route_path != "/": + redirect_scope = dict(scope) + if route_path.endswith("/"): + redirect_scope["path"] = redirect_scope["path"].rstrip("/") + else: + redirect_scope["path"] = redirect_scope["path"] + "/" + + for route in self.routes: + match, child_scope = route.matches(redirect_scope) + if match != Match.NONE: + redirect_url = URL(scope=redirect_scope) + response = RedirectResponse(url=str(redirect_url)) + await response(scope, receive, send) + return + + await self.default(scope, receive, send) + + def __eq__(self, other: typing.Any) -> bool: + return isinstance(other, Router) and self.routes == other.routes + + def mount(self, path: str, app: ASGIApp, name: str | None = None) -> None: # pragma: no cover + route = Mount(path, app=app, name=name) + self.routes.append(route) + + def host(self, host: str, app: ASGIApp, name: str | None = None) -> None: # pragma: no cover + route = Host(host, app=app, name=name) + self.routes.append(route) + + def add_route( + self, + path: str, + endpoint: typing.Callable[[Request], typing.Awaitable[Response] | Response], + methods: list[str] | None = None, + name: str | None = None, + include_in_schema: bool = True, + ) -> None: # pragma: no cover + route = Route( + path, + endpoint=endpoint, + methods=methods, + name=name, + include_in_schema=include_in_schema, + ) + self.routes.append(route) + + def add_websocket_route( + self, + path: str, + endpoint: typing.Callable[[WebSocket], typing.Awaitable[None]], + name: str | None = None, + ) -> None: # pragma: no cover + route = WebSocketRoute(path, endpoint=endpoint, name=name) + self.routes.append(route) + + def route( + self, + path: str, + methods: list[str] | None = None, + name: str | None = None, + include_in_schema: bool = True, + ) -> typing.Callable: # type: ignore[type-arg] + """ + We no longer document this decorator style API, and its usage is discouraged. + Instead you should use the following approach: + + >>> routes = [Route(path, endpoint=...), ...] + >>> app = Starlette(routes=routes) + """ + warnings.warn( + "The `route` decorator is deprecated, and will be removed in version 1.0.0." + "Refer to https://www.starlette.io/routing/#http-routing for the recommended approach.", + DeprecationWarning, + ) + + def decorator(func: typing.Callable) -> typing.Callable: # type: ignore[type-arg] + self.add_route( + path, + func, + methods=methods, + name=name, + include_in_schema=include_in_schema, + ) + return func + + return decorator + + def websocket_route(self, path: str, name: str | None = None) -> typing.Callable: # type: ignore[type-arg] + """ + We no longer document this decorator style API, and its usage is discouraged. + Instead you should use the following approach: + + >>> routes = [WebSocketRoute(path, endpoint=...), ...] + >>> app = Starlette(routes=routes) + """ + warnings.warn( + "The `websocket_route` decorator is deprecated, and will be removed in version 1.0.0. Refer to " + "https://www.starlette.io/routing/#websocket-routing for the recommended approach.", + DeprecationWarning, + ) + + def decorator(func: typing.Callable) -> typing.Callable: # type: ignore[type-arg] + self.add_websocket_route(path, func, name=name) + return func + + return decorator + + def add_event_handler(self, event_type: str, func: typing.Callable[[], typing.Any]) -> None: # pragma: no cover + assert event_type in ("startup", "shutdown") + + if event_type == "startup": + self.on_startup.append(func) + else: + self.on_shutdown.append(func) + + def on_event(self, event_type: str) -> typing.Callable: # type: ignore[type-arg] + warnings.warn( + "The `on_event` decorator is deprecated, and will be removed in version 1.0.0. " + "Refer to https://www.starlette.io/lifespan/ for recommended approach.", + DeprecationWarning, + ) + + def decorator(func: typing.Callable) -> typing.Callable: # type: ignore[type-arg] + self.add_event_handler(event_type, func) + return func + + return decorator diff --git a/lib/python3.12/site-packages/starlette/schemas.py b/lib/python3.12/site-packages/starlette/schemas.py new file mode 100644 index 0000000000000000000000000000000000000000..bfc40e2ae0efddee75d0328cbe83720b8d80d886 --- /dev/null +++ b/lib/python3.12/site-packages/starlette/schemas.py @@ -0,0 +1,147 @@ +from __future__ import annotations + +import inspect +import re +import typing + +from starlette.requests import Request +from starlette.responses import Response +from starlette.routing import BaseRoute, Host, Mount, Route + +try: + import yaml +except ModuleNotFoundError: # pragma: no cover + yaml = None # type: ignore[assignment] + + +class OpenAPIResponse(Response): + media_type = "application/vnd.oai.openapi" + + def render(self, content: typing.Any) -> bytes: + assert yaml is not None, "`pyyaml` must be installed to use OpenAPIResponse." + assert isinstance(content, dict), "The schema passed to OpenAPIResponse should be a dictionary." + return yaml.dump(content, default_flow_style=False).encode("utf-8") + + +class EndpointInfo(typing.NamedTuple): + path: str + http_method: str + func: typing.Callable[..., typing.Any] + + +_remove_converter_pattern = re.compile(r":\w+}") + + +class BaseSchemaGenerator: + def get_schema(self, routes: list[BaseRoute]) -> dict[str, typing.Any]: + raise NotImplementedError() # pragma: no cover + + def get_endpoints(self, routes: list[BaseRoute]) -> list[EndpointInfo]: + """ + Given the routes, yields the following information: + + - path + eg: /users/ + - http_method + one of 'get', 'post', 'put', 'patch', 'delete', 'options' + - func + method ready to extract the docstring + """ + endpoints_info: list[EndpointInfo] = [] + + for route in routes: + if isinstance(route, (Mount, Host)): + routes = route.routes or [] + if isinstance(route, Mount): + path = self._remove_converter(route.path) + else: + path = "" + sub_endpoints = [ + EndpointInfo( + path="".join((path, sub_endpoint.path)), + http_method=sub_endpoint.http_method, + func=sub_endpoint.func, + ) + for sub_endpoint in self.get_endpoints(routes) + ] + endpoints_info.extend(sub_endpoints) + + elif not isinstance(route, Route) or not route.include_in_schema: + continue + + elif inspect.isfunction(route.endpoint) or inspect.ismethod(route.endpoint): + path = self._remove_converter(route.path) + for method in route.methods or ["GET"]: + if method == "HEAD": + continue + endpoints_info.append(EndpointInfo(path, method.lower(), route.endpoint)) + else: + path = self._remove_converter(route.path) + for method in ["get", "post", "put", "patch", "delete", "options"]: + if not hasattr(route.endpoint, method): + continue + func = getattr(route.endpoint, method) + endpoints_info.append(EndpointInfo(path, method.lower(), func)) + + return endpoints_info + + def _remove_converter(self, path: str) -> str: + """ + Remove the converter from the path. + For example, a route like this: + Route("/users/{id:int}", endpoint=get_user, methods=["GET"]) + Should be represented as `/users/{id}` in the OpenAPI schema. + """ + return _remove_converter_pattern.sub("}", path) + + def parse_docstring(self, func_or_method: typing.Callable[..., typing.Any]) -> dict[str, typing.Any]: + """ + Given a function, parse the docstring as YAML and return a dictionary of info. + """ + docstring = func_or_method.__doc__ + if not docstring: + return {} + + assert yaml is not None, "`pyyaml` must be installed to use parse_docstring." + + # We support having regular docstrings before the schema + # definition. Here we return just the schema part from + # the docstring. + docstring = docstring.split("---")[-1] + + parsed = yaml.safe_load(docstring) + + if not isinstance(parsed, dict): + # A regular docstring (not yaml formatted) can return + # a simple string here, which wouldn't follow the schema. + return {} + + return parsed + + def OpenAPIResponse(self, request: Request) -> Response: + routes = request.app.routes + schema = self.get_schema(routes=routes) + return OpenAPIResponse(schema) + + +class SchemaGenerator(BaseSchemaGenerator): + def __init__(self, base_schema: dict[str, typing.Any]) -> None: + self.base_schema = base_schema + + def get_schema(self, routes: list[BaseRoute]) -> dict[str, typing.Any]: + schema = dict(self.base_schema) + schema.setdefault("paths", {}) + endpoints_info = self.get_endpoints(routes) + + for endpoint in endpoints_info: + parsed = self.parse_docstring(endpoint.func) + + if not parsed: + continue + + if endpoint.path not in schema["paths"]: + schema["paths"][endpoint.path] = {} + + schema["paths"][endpoint.path][endpoint.http_method] = parsed + + return schema diff --git a/lib/python3.12/site-packages/starlette/staticfiles.py b/lib/python3.12/site-packages/starlette/staticfiles.py new file mode 100644 index 0000000000000000000000000000000000000000..637da64802d0360b901ef369c0a121b1b75ef773 --- /dev/null +++ b/lib/python3.12/site-packages/starlette/staticfiles.py @@ -0,0 +1,220 @@ +from __future__ import annotations + +import errno +import importlib.util +import os +import stat +import typing +from email.utils import parsedate + +import anyio +import anyio.to_thread + +from starlette._utils import get_route_path +from starlette.datastructures import URL, Headers +from starlette.exceptions import HTTPException +from starlette.responses import FileResponse, RedirectResponse, Response +from starlette.types import Receive, Scope, Send + +PathLike = typing.Union[str, "os.PathLike[str]"] + + +class NotModifiedResponse(Response): + NOT_MODIFIED_HEADERS = ( + "cache-control", + "content-location", + "date", + "etag", + "expires", + "vary", + ) + + def __init__(self, headers: Headers): + super().__init__( + status_code=304, + headers={name: value for name, value in headers.items() if name in self.NOT_MODIFIED_HEADERS}, + ) + + +class StaticFiles: + def __init__( + self, + *, + directory: PathLike | None = None, + packages: list[str | tuple[str, str]] | None = None, + html: bool = False, + check_dir: bool = True, + follow_symlink: bool = False, + ) -> None: + self.directory = directory + self.packages = packages + self.all_directories = self.get_directories(directory, packages) + self.html = html + self.config_checked = False + self.follow_symlink = follow_symlink + if check_dir and directory is not None and not os.path.isdir(directory): + raise RuntimeError(f"Directory '{directory}' does not exist") + + def get_directories( + self, + directory: PathLike | None = None, + packages: list[str | tuple[str, str]] | None = None, + ) -> list[PathLike]: + """ + Given `directory` and `packages` arguments, return a list of all the + directories that should be used for serving static files from. + """ + directories = [] + if directory is not None: + directories.append(directory) + + for package in packages or []: + if isinstance(package, tuple): + package, statics_dir = package + else: + statics_dir = "statics" + spec = importlib.util.find_spec(package) + assert spec is not None, f"Package {package!r} could not be found." + assert spec.origin is not None, f"Package {package!r} could not be found." + package_directory = os.path.normpath(os.path.join(spec.origin, "..", statics_dir)) + assert os.path.isdir(package_directory), ( + f"Directory '{statics_dir!r}' in package {package!r} could not be found." + ) + directories.append(package_directory) + + return directories + + async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None: + """ + The ASGI entry point. + """ + assert scope["type"] == "http" + + if not self.config_checked: + await self.check_config() + self.config_checked = True + + path = self.get_path(scope) + response = await self.get_response(path, scope) + await response(scope, receive, send) + + def get_path(self, scope: Scope) -> str: + """ + Given the ASGI scope, return the `path` string to serve up, + with OS specific path separators, and any '..', '.' components removed. + """ + route_path = get_route_path(scope) + return os.path.normpath(os.path.join(*route_path.split("/"))) + + async def get_response(self, path: str, scope: Scope) -> Response: + """ + Returns an HTTP response, given the incoming path, method and request headers. + """ + if scope["method"] not in ("GET", "HEAD"): + raise HTTPException(status_code=405) + + try: + full_path, stat_result = await anyio.to_thread.run_sync(self.lookup_path, path) + except PermissionError: + raise HTTPException(status_code=401) + except OSError as exc: + # Filename is too long, so it can't be a valid static file. + if exc.errno == errno.ENAMETOOLONG: + raise HTTPException(status_code=404) + + raise exc + + if stat_result and stat.S_ISREG(stat_result.st_mode): + # We have a static file to serve. + return self.file_response(full_path, stat_result, scope) + + elif stat_result and stat.S_ISDIR(stat_result.st_mode) and self.html: + # We're in HTML mode, and have got a directory URL. + # Check if we have 'index.html' file to serve. + index_path = os.path.join(path, "index.html") + full_path, stat_result = await anyio.to_thread.run_sync(self.lookup_path, index_path) + if stat_result is not None and stat.S_ISREG(stat_result.st_mode): + if not scope["path"].endswith("/"): + # Directory URLs should redirect to always end in "/". + url = URL(scope=scope) + url = url.replace(path=url.path + "/") + return RedirectResponse(url=url) + return self.file_response(full_path, stat_result, scope) + + if self.html: + # Check for '404.html' if we're in HTML mode. + full_path, stat_result = await anyio.to_thread.run_sync(self.lookup_path, "404.html") + if stat_result and stat.S_ISREG(stat_result.st_mode): + return FileResponse(full_path, stat_result=stat_result, status_code=404) + raise HTTPException(status_code=404) + + def lookup_path(self, path: str) -> tuple[str, os.stat_result | None]: + for directory in self.all_directories: + joined_path = os.path.join(directory, path) + if self.follow_symlink: + full_path = os.path.abspath(joined_path) + directory = os.path.abspath(directory) + else: + full_path = os.path.realpath(joined_path) + directory = os.path.realpath(directory) + if os.path.commonpath([full_path, directory]) != str(directory): + # Don't allow misbehaving clients to break out of the static files directory. + continue + try: + return full_path, os.stat(full_path) + except (FileNotFoundError, NotADirectoryError): + continue + return "", None + + def file_response( + self, + full_path: PathLike, + stat_result: os.stat_result, + scope: Scope, + status_code: int = 200, + ) -> Response: + request_headers = Headers(scope=scope) + + response = FileResponse(full_path, status_code=status_code, stat_result=stat_result) + if self.is_not_modified(response.headers, request_headers): + return NotModifiedResponse(response.headers) + return response + + async def check_config(self) -> None: + """ + Perform a one-off configuration check that StaticFiles is actually + pointed at a directory, so that we can raise loud errors rather than + just returning 404 responses. + """ + if self.directory is None: + return + + try: + stat_result = await anyio.to_thread.run_sync(os.stat, self.directory) + except FileNotFoundError: + raise RuntimeError(f"StaticFiles directory '{self.directory}' does not exist.") + if not (stat.S_ISDIR(stat_result.st_mode) or stat.S_ISLNK(stat_result.st_mode)): + raise RuntimeError(f"StaticFiles path '{self.directory}' is not a directory.") + + def is_not_modified(self, response_headers: Headers, request_headers: Headers) -> bool: + """ + Given the request and response headers, return `True` if an HTTP + "Not Modified" response could be returned instead. + """ + try: + if_none_match = request_headers["if-none-match"] + etag = response_headers["etag"] + if etag in [tag.strip(" W/") for tag in if_none_match.split(",")]: + return True + except KeyError: + pass + + try: + if_modified_since = parsedate(request_headers["if-modified-since"]) + last_modified = parsedate(response_headers["last-modified"]) + if if_modified_since is not None and last_modified is not None and if_modified_since >= last_modified: + return True + except KeyError: + pass + + return False diff --git a/lib/python3.12/site-packages/starlette/status.py b/lib/python3.12/site-packages/starlette/status.py new file mode 100644 index 0000000000000000000000000000000000000000..54c1fb7d0df128bfe16fb4cb03cdf23b8225af94 --- /dev/null +++ b/lib/python3.12/site-packages/starlette/status.py @@ -0,0 +1,95 @@ +""" +HTTP codes +See HTTP Status Code Registry: +https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml + +And RFC 2324 - https://tools.ietf.org/html/rfc2324 +""" + +from __future__ import annotations + +HTTP_100_CONTINUE = 100 +HTTP_101_SWITCHING_PROTOCOLS = 101 +HTTP_102_PROCESSING = 102 +HTTP_103_EARLY_HINTS = 103 +HTTP_200_OK = 200 +HTTP_201_CREATED = 201 +HTTP_202_ACCEPTED = 202 +HTTP_203_NON_AUTHORITATIVE_INFORMATION = 203 +HTTP_204_NO_CONTENT = 204 +HTTP_205_RESET_CONTENT = 205 +HTTP_206_PARTIAL_CONTENT = 206 +HTTP_207_MULTI_STATUS = 207 +HTTP_208_ALREADY_REPORTED = 208 +HTTP_226_IM_USED = 226 +HTTP_300_MULTIPLE_CHOICES = 300 +HTTP_301_MOVED_PERMANENTLY = 301 +HTTP_302_FOUND = 302 +HTTP_303_SEE_OTHER = 303 +HTTP_304_NOT_MODIFIED = 304 +HTTP_305_USE_PROXY = 305 +HTTP_306_RESERVED = 306 +HTTP_307_TEMPORARY_REDIRECT = 307 +HTTP_308_PERMANENT_REDIRECT = 308 +HTTP_400_BAD_REQUEST = 400 +HTTP_401_UNAUTHORIZED = 401 +HTTP_402_PAYMENT_REQUIRED = 402 +HTTP_403_FORBIDDEN = 403 +HTTP_404_NOT_FOUND = 404 +HTTP_405_METHOD_NOT_ALLOWED = 405 +HTTP_406_NOT_ACCEPTABLE = 406 +HTTP_407_PROXY_AUTHENTICATION_REQUIRED = 407 +HTTP_408_REQUEST_TIMEOUT = 408 +HTTP_409_CONFLICT = 409 +HTTP_410_GONE = 410 +HTTP_411_LENGTH_REQUIRED = 411 +HTTP_412_PRECONDITION_FAILED = 412 +HTTP_413_REQUEST_ENTITY_TOO_LARGE = 413 +HTTP_414_REQUEST_URI_TOO_LONG = 414 +HTTP_415_UNSUPPORTED_MEDIA_TYPE = 415 +HTTP_416_REQUESTED_RANGE_NOT_SATISFIABLE = 416 +HTTP_417_EXPECTATION_FAILED = 417 +HTTP_418_IM_A_TEAPOT = 418 +HTTP_421_MISDIRECTED_REQUEST = 421 +HTTP_422_UNPROCESSABLE_ENTITY = 422 +HTTP_423_LOCKED = 423 +HTTP_424_FAILED_DEPENDENCY = 424 +HTTP_425_TOO_EARLY = 425 +HTTP_426_UPGRADE_REQUIRED = 426 +HTTP_428_PRECONDITION_REQUIRED = 428 +HTTP_429_TOO_MANY_REQUESTS = 429 +HTTP_431_REQUEST_HEADER_FIELDS_TOO_LARGE = 431 +HTTP_451_UNAVAILABLE_FOR_LEGAL_REASONS = 451 +HTTP_500_INTERNAL_SERVER_ERROR = 500 +HTTP_501_NOT_IMPLEMENTED = 501 +HTTP_502_BAD_GATEWAY = 502 +HTTP_503_SERVICE_UNAVAILABLE = 503 +HTTP_504_GATEWAY_TIMEOUT = 504 +HTTP_505_HTTP_VERSION_NOT_SUPPORTED = 505 +HTTP_506_VARIANT_ALSO_NEGOTIATES = 506 +HTTP_507_INSUFFICIENT_STORAGE = 507 +HTTP_508_LOOP_DETECTED = 508 +HTTP_510_NOT_EXTENDED = 510 +HTTP_511_NETWORK_AUTHENTICATION_REQUIRED = 511 + + +""" +WebSocket codes +https://www.iana.org/assignments/websocket/websocket.xml#close-code-number +https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent +""" +WS_1000_NORMAL_CLOSURE = 1000 +WS_1001_GOING_AWAY = 1001 +WS_1002_PROTOCOL_ERROR = 1002 +WS_1003_UNSUPPORTED_DATA = 1003 +WS_1005_NO_STATUS_RCVD = 1005 +WS_1006_ABNORMAL_CLOSURE = 1006 +WS_1007_INVALID_FRAME_PAYLOAD_DATA = 1007 +WS_1008_POLICY_VIOLATION = 1008 +WS_1009_MESSAGE_TOO_BIG = 1009 +WS_1010_MANDATORY_EXT = 1010 +WS_1011_INTERNAL_ERROR = 1011 +WS_1012_SERVICE_RESTART = 1012 +WS_1013_TRY_AGAIN_LATER = 1013 +WS_1014_BAD_GATEWAY = 1014 +WS_1015_TLS_HANDSHAKE = 1015 diff --git a/lib/python3.12/site-packages/starlette/templating.py b/lib/python3.12/site-packages/starlette/templating.py new file mode 100644 index 0000000000000000000000000000000000000000..f764858b88365c0485828cbf2d3efda62b9d3fb3 --- /dev/null +++ b/lib/python3.12/site-packages/starlette/templating.py @@ -0,0 +1,216 @@ +from __future__ import annotations + +import typing +import warnings +from os import PathLike + +from starlette.background import BackgroundTask +from starlette.datastructures import URL +from starlette.requests import Request +from starlette.responses import HTMLResponse +from starlette.types import Receive, Scope, Send + +try: + import jinja2 + + # @contextfunction was renamed to @pass_context in Jinja 3.0, and was removed in 3.1 + # hence we try to get pass_context (most installs will be >=3.1) + # and fall back to contextfunction, + # adding a type ignore for mypy to let us access an attribute that may not exist + if hasattr(jinja2, "pass_context"): + pass_context = jinja2.pass_context + else: # pragma: no cover + pass_context = jinja2.contextfunction # type: ignore[attr-defined] +except ModuleNotFoundError: # pragma: no cover + jinja2 = None # type: ignore[assignment] + + +class _TemplateResponse(HTMLResponse): + def __init__( + self, + template: typing.Any, + context: dict[str, typing.Any], + status_code: int = 200, + headers: typing.Mapping[str, str] | None = None, + media_type: str | None = None, + background: BackgroundTask | None = None, + ): + self.template = template + self.context = context + content = template.render(context) + super().__init__(content, status_code, headers, media_type, background) + + async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None: + request = self.context.get("request", {}) + extensions = request.get("extensions", {}) + if "http.response.debug" in extensions: # pragma: no branch + await send( + { + "type": "http.response.debug", + "info": { + "template": self.template, + "context": self.context, + }, + } + ) + await super().__call__(scope, receive, send) + + +class Jinja2Templates: + """ + templates = Jinja2Templates("templates") + + return templates.TemplateResponse("index.html", {"request": request}) + """ + + @typing.overload + def __init__( + self, + directory: str | PathLike[str] | typing.Sequence[str | PathLike[str]], + *, + context_processors: list[typing.Callable[[Request], dict[str, typing.Any]]] | None = None, + **env_options: typing.Any, + ) -> None: ... + + @typing.overload + def __init__( + self, + *, + env: jinja2.Environment, + context_processors: list[typing.Callable[[Request], dict[str, typing.Any]]] | None = None, + ) -> None: ... + + def __init__( + self, + directory: str | PathLike[str] | typing.Sequence[str | PathLike[str]] | None = None, + *, + context_processors: list[typing.Callable[[Request], dict[str, typing.Any]]] | None = None, + env: jinja2.Environment | None = None, + **env_options: typing.Any, + ) -> None: + if env_options: + warnings.warn( + "Extra environment options are deprecated. Use a preconfigured jinja2.Environment instead.", + DeprecationWarning, + ) + assert jinja2 is not None, "jinja2 must be installed to use Jinja2Templates" + assert bool(directory) ^ bool(env), "either 'directory' or 'env' arguments must be passed" + self.context_processors = context_processors or [] + if directory is not None: + self.env = self._create_env(directory, **env_options) + elif env is not None: # pragma: no branch + self.env = env + + self._setup_env_defaults(self.env) + + def _create_env( + self, + directory: str | PathLike[str] | typing.Sequence[str | PathLike[str]], + **env_options: typing.Any, + ) -> jinja2.Environment: + loader = jinja2.FileSystemLoader(directory) + env_options.setdefault("loader", loader) + env_options.setdefault("autoescape", True) + + return jinja2.Environment(**env_options) + + def _setup_env_defaults(self, env: jinja2.Environment) -> None: + @pass_context + def url_for( + context: dict[str, typing.Any], + name: str, + /, + **path_params: typing.Any, + ) -> URL: + request: Request = context["request"] + return request.url_for(name, **path_params) + + env.globals.setdefault("url_for", url_for) + + def get_template(self, name: str) -> jinja2.Template: + return self.env.get_template(name) + + @typing.overload + def TemplateResponse( + self, + request: Request, + name: str, + context: dict[str, typing.Any] | None = None, + status_code: int = 200, + headers: typing.Mapping[str, str] | None = None, + media_type: str | None = None, + background: BackgroundTask | None = None, + ) -> _TemplateResponse: ... + + @typing.overload + def TemplateResponse( + self, + name: str, + context: dict[str, typing.Any] | None = None, + status_code: int = 200, + headers: typing.Mapping[str, str] | None = None, + media_type: str | None = None, + background: BackgroundTask | None = None, + ) -> _TemplateResponse: + # Deprecated usage + ... + + def TemplateResponse(self, *args: typing.Any, **kwargs: typing.Any) -> _TemplateResponse: + if args: + if isinstance(args[0], str): # the first argument is template name (old style) + warnings.warn( + "The `name` is not the first parameter anymore. " + "The first parameter should be the `Request` instance.\n" + 'Replace `TemplateResponse(name, {"request": request})` by `TemplateResponse(request, name)`.', + DeprecationWarning, + ) + + name = args[0] + context = args[1] if len(args) > 1 else kwargs.get("context", {}) + status_code = args[2] if len(args) > 2 else kwargs.get("status_code", 200) + headers = args[3] if len(args) > 3 else kwargs.get("headers") + media_type = args[4] if len(args) > 4 else kwargs.get("media_type") + background = args[5] if len(args) > 5 else kwargs.get("background") + + if "request" not in context: + raise ValueError('context must include a "request" key') + request = context["request"] + else: # the first argument is a request instance (new style) + request = args[0] + name = args[1] if len(args) > 1 else kwargs["name"] + context = args[2] if len(args) > 2 else kwargs.get("context", {}) + status_code = args[3] if len(args) > 3 else kwargs.get("status_code", 200) + headers = args[4] if len(args) > 4 else kwargs.get("headers") + media_type = args[5] if len(args) > 5 else kwargs.get("media_type") + background = args[6] if len(args) > 6 else kwargs.get("background") + else: # all arguments are kwargs + if "request" not in kwargs: + warnings.warn( + "The `TemplateResponse` now requires the `request` argument.\n" + 'Replace `TemplateResponse(name, {"context": context})` by `TemplateResponse(request, name)`.', + DeprecationWarning, + ) + if "request" not in kwargs.get("context", {}): + raise ValueError('context must include a "request" key') + + context = kwargs.get("context", {}) + request = kwargs.get("request", context.get("request")) + name = typing.cast(str, kwargs["name"]) + status_code = kwargs.get("status_code", 200) + headers = kwargs.get("headers") + media_type = kwargs.get("media_type") + background = kwargs.get("background") + + context.setdefault("request", request) + for context_processor in self.context_processors: + context.update(context_processor(request)) + + template = self.get_template(name) + return _TemplateResponse( + template, + context, + status_code=status_code, + headers=headers, + media_type=media_type, + background=background, + ) diff --git a/lib/python3.12/site-packages/starlette/testclient.py b/lib/python3.12/site-packages/starlette/testclient.py new file mode 100644 index 0000000000000000000000000000000000000000..d54025e52d7ec2996d3a6fbdea9c390291067063 --- /dev/null +++ b/lib/python3.12/site-packages/starlette/testclient.py @@ -0,0 +1,731 @@ +from __future__ import annotations + +import contextlib +import inspect +import io +import json +import math +import sys +import typing +import warnings +from concurrent.futures import Future +from types import GeneratorType +from urllib.parse import unquote, urljoin + +import anyio +import anyio.abc +import anyio.from_thread +from anyio.streams.stapled import StapledObjectStream + +from starlette._utils import is_async_callable +from starlette.types import ASGIApp, Message, Receive, Scope, Send +from starlette.websockets import WebSocketDisconnect + +if sys.version_info >= (3, 10): # pragma: no cover + from typing import TypeGuard +else: # pragma: no cover + from typing_extensions import TypeGuard + +try: + import httpx +except ModuleNotFoundError: # pragma: no cover + raise RuntimeError( + "The starlette.testclient module requires the httpx package to be installed.\n" + "You can install this with:\n" + " $ pip install httpx\n" + ) +_PortalFactoryType = typing.Callable[[], typing.ContextManager[anyio.abc.BlockingPortal]] + +ASGIInstance = typing.Callable[[Receive, Send], typing.Awaitable[None]] +ASGI2App = typing.Callable[[Scope], ASGIInstance] +ASGI3App = typing.Callable[[Scope, Receive, Send], typing.Awaitable[None]] + + +_RequestData = typing.Mapping[str, typing.Union[str, typing.Iterable[str], bytes]] + + +def _is_asgi3(app: ASGI2App | ASGI3App) -> TypeGuard[ASGI3App]: + if inspect.isclass(app): + return hasattr(app, "__await__") + return is_async_callable(app) + + +class _WrapASGI2: + """ + Provide an ASGI3 interface onto an ASGI2 app. + """ + + def __init__(self, app: ASGI2App) -> None: + self.app = app + + async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None: + instance = self.app(scope) + await instance(receive, send) + + +class _AsyncBackend(typing.TypedDict): + backend: str + backend_options: dict[str, typing.Any] + + +class _Upgrade(Exception): + def __init__(self, session: WebSocketTestSession) -> None: + self.session = session + + +class WebSocketDenialResponse( # type: ignore[misc] + httpx.Response, + WebSocketDisconnect, +): + """ + A special case of `WebSocketDisconnect`, raised in the `TestClient` if the + `WebSocket` is closed before being accepted with a `send_denial_response()`. + """ + + +class WebSocketTestSession: + def __init__( + self, + app: ASGI3App, + scope: Scope, + portal_factory: _PortalFactoryType, + ) -> None: + self.app = app + self.scope = scope + self.accepted_subprotocol = None + self.portal_factory = portal_factory + self.extra_headers = None + + def __enter__(self) -> WebSocketTestSession: + with contextlib.ExitStack() as stack: + self.portal = portal = stack.enter_context(self.portal_factory()) + fut, cs = portal.start_task(self._run) + stack.callback(fut.result) + stack.callback(portal.call, cs.cancel) + self.send({"type": "websocket.connect"}) + message = self.receive() + self._raise_on_close(message) + self.accepted_subprotocol = message.get("subprotocol", None) + self.extra_headers = message.get("headers", None) + stack.callback(self.close, 1000) + self.exit_stack = stack.pop_all() + return self + + def __exit__(self, *args: typing.Any) -> bool | None: + return self.exit_stack.__exit__(*args) + + async def _run(self, *, task_status: anyio.abc.TaskStatus[anyio.CancelScope]) -> None: + """ + The sub-thread in which the websocket session runs. + """ + send: anyio.create_memory_object_stream[Message] = anyio.create_memory_object_stream(math.inf) + send_tx, send_rx = send + receive: anyio.create_memory_object_stream[Message] = anyio.create_memory_object_stream(math.inf) + receive_tx, receive_rx = receive + with send_tx, send_rx, receive_tx, receive_rx, anyio.CancelScope() as cs: + self._receive_tx = receive_tx + self._send_rx = send_rx + task_status.started(cs) + await self.app(self.scope, receive_rx.receive, send_tx.send) + + # wait for cs.cancel to be called before closing streams + await anyio.sleep_forever() + + def _raise_on_close(self, message: Message) -> None: + if message["type"] == "websocket.close": + raise WebSocketDisconnect(code=message.get("code", 1000), reason=message.get("reason", "")) + elif message["type"] == "websocket.http.response.start": + status_code: int = message["status"] + headers: list[tuple[bytes, bytes]] = message["headers"] + body: list[bytes] = [] + while True: + message = self.receive() + assert message["type"] == "websocket.http.response.body" + body.append(message["body"]) + if not message.get("more_body", False): + break + raise WebSocketDenialResponse(status_code=status_code, headers=headers, content=b"".join(body)) + + def send(self, message: Message) -> None: + self.portal.call(self._receive_tx.send, message) + + def send_text(self, data: str) -> None: + self.send({"type": "websocket.receive", "text": data}) + + def send_bytes(self, data: bytes) -> None: + self.send({"type": "websocket.receive", "bytes": data}) + + def send_json(self, data: typing.Any, mode: typing.Literal["text", "binary"] = "text") -> None: + text = json.dumps(data, separators=(",", ":"), ensure_ascii=False) + if mode == "text": + self.send({"type": "websocket.receive", "text": text}) + else: + self.send({"type": "websocket.receive", "bytes": text.encode("utf-8")}) + + def close(self, code: int = 1000, reason: str | None = None) -> None: + self.send({"type": "websocket.disconnect", "code": code, "reason": reason}) + + def receive(self) -> Message: + return self.portal.call(self._send_rx.receive) + + def receive_text(self) -> str: + message = self.receive() + self._raise_on_close(message) + return typing.cast(str, message["text"]) + + def receive_bytes(self) -> bytes: + message = self.receive() + self._raise_on_close(message) + return typing.cast(bytes, message["bytes"]) + + def receive_json(self, mode: typing.Literal["text", "binary"] = "text") -> typing.Any: + message = self.receive() + self._raise_on_close(message) + if mode == "text": + text = message["text"] + else: + text = message["bytes"].decode("utf-8") + return json.loads(text) + + +class _TestClientTransport(httpx.BaseTransport): + def __init__( + self, + app: ASGI3App, + portal_factory: _PortalFactoryType, + raise_server_exceptions: bool = True, + root_path: str = "", + *, + client: tuple[str, int], + app_state: dict[str, typing.Any], + ) -> None: + self.app = app + self.raise_server_exceptions = raise_server_exceptions + self.root_path = root_path + self.portal_factory = portal_factory + self.app_state = app_state + self.client = client + + def handle_request(self, request: httpx.Request) -> httpx.Response: + scheme = request.url.scheme + netloc = request.url.netloc.decode(encoding="ascii") + path = request.url.path + raw_path = request.url.raw_path + query = request.url.query.decode(encoding="ascii") + + default_port = {"http": 80, "ws": 80, "https": 443, "wss": 443}[scheme] + + if ":" in netloc: + host, port_string = netloc.split(":", 1) + port = int(port_string) + else: + host = netloc + port = default_port + + # Include the 'host' header. + if "host" in request.headers: + headers: list[tuple[bytes, bytes]] = [] + elif port == default_port: # pragma: no cover + headers = [(b"host", host.encode())] + else: # pragma: no cover + headers = [(b"host", (f"{host}:{port}").encode())] + + # Include other request headers. + headers += [(key.lower().encode(), value.encode()) for key, value in request.headers.multi_items()] + + scope: dict[str, typing.Any] + + if scheme in {"ws", "wss"}: + subprotocol = request.headers.get("sec-websocket-protocol", None) + if subprotocol is None: + subprotocols: typing.Sequence[str] = [] + else: + subprotocols = [value.strip() for value in subprotocol.split(",")] + scope = { + "type": "websocket", + "path": unquote(path), + "raw_path": raw_path.split(b"?", 1)[0], + "root_path": self.root_path, + "scheme": scheme, + "query_string": query.encode(), + "headers": headers, + "client": self.client, + "server": [host, port], + "subprotocols": subprotocols, + "state": self.app_state.copy(), + "extensions": {"websocket.http.response": {}}, + } + session = WebSocketTestSession(self.app, scope, self.portal_factory) + raise _Upgrade(session) + + scope = { + "type": "http", + "http_version": "1.1", + "method": request.method, + "path": unquote(path), + "raw_path": raw_path.split(b"?", 1)[0], + "root_path": self.root_path, + "scheme": scheme, + "query_string": query.encode(), + "headers": headers, + "client": self.client, + "server": [host, port], + "extensions": {"http.response.debug": {}}, + "state": self.app_state.copy(), + } + + request_complete = False + response_started = False + response_complete: anyio.Event + raw_kwargs: dict[str, typing.Any] = {"stream": io.BytesIO()} + template = None + context = None + + async def receive() -> Message: + nonlocal request_complete + + if request_complete: + if not response_complete.is_set(): + await response_complete.wait() + return {"type": "http.disconnect"} + + body = request.read() + if isinstance(body, str): + body_bytes: bytes = body.encode("utf-8") # pragma: no cover + elif body is None: + body_bytes = b"" # pragma: no cover + elif isinstance(body, GeneratorType): + try: # pragma: no cover + chunk = body.send(None) + if isinstance(chunk, str): + chunk = chunk.encode("utf-8") + return {"type": "http.request", "body": chunk, "more_body": True} + except StopIteration: # pragma: no cover + request_complete = True + return {"type": "http.request", "body": b""} + else: + body_bytes = body + + request_complete = True + return {"type": "http.request", "body": body_bytes} + + async def send(message: Message) -> None: + nonlocal raw_kwargs, response_started, template, context + + if message["type"] == "http.response.start": + assert not response_started, 'Received multiple "http.response.start" messages.' + raw_kwargs["status_code"] = message["status"] + raw_kwargs["headers"] = [(key.decode(), value.decode()) for key, value in message.get("headers", [])] + response_started = True + elif message["type"] == "http.response.body": + assert response_started, 'Received "http.response.body" without "http.response.start".' + assert not response_complete.is_set(), 'Received "http.response.body" after response completed.' + body = message.get("body", b"") + more_body = message.get("more_body", False) + if request.method != "HEAD": + raw_kwargs["stream"].write(body) + if not more_body: + raw_kwargs["stream"].seek(0) + response_complete.set() + elif message["type"] == "http.response.debug": + template = message["info"]["template"] + context = message["info"]["context"] + + try: + with self.portal_factory() as portal: + response_complete = portal.call(anyio.Event) + portal.call(self.app, scope, receive, send) + except BaseException as exc: + if self.raise_server_exceptions: + raise exc + + if self.raise_server_exceptions: + assert response_started, "TestClient did not receive any response." + elif not response_started: + raw_kwargs = { + "status_code": 500, + "headers": [], + "stream": io.BytesIO(), + } + + raw_kwargs["stream"] = httpx.ByteStream(raw_kwargs["stream"].read()) + + response = httpx.Response(**raw_kwargs, request=request) + if template is not None: + response.template = template # type: ignore[attr-defined] + response.context = context # type: ignore[attr-defined] + return response + + +class TestClient(httpx.Client): + __test__ = False + task: Future[None] + portal: anyio.abc.BlockingPortal | None = None + + def __init__( + self, + app: ASGIApp, + base_url: str = "http://testserver", + raise_server_exceptions: bool = True, + root_path: str = "", + backend: typing.Literal["asyncio", "trio"] = "asyncio", + backend_options: dict[str, typing.Any] | None = None, + cookies: httpx._types.CookieTypes | None = None, + headers: dict[str, str] | None = None, + follow_redirects: bool = True, + client: tuple[str, int] = ("testclient", 50000), + ) -> None: + self.async_backend = _AsyncBackend(backend=backend, backend_options=backend_options or {}) + if _is_asgi3(app): + asgi_app = app + else: + app = typing.cast(ASGI2App, app) # type: ignore[assignment] + asgi_app = _WrapASGI2(app) # type: ignore[arg-type] + self.app = asgi_app + self.app_state: dict[str, typing.Any] = {} + transport = _TestClientTransport( + self.app, + portal_factory=self._portal_factory, + raise_server_exceptions=raise_server_exceptions, + root_path=root_path, + app_state=self.app_state, + client=client, + ) + if headers is None: + headers = {} + headers.setdefault("user-agent", "testclient") + super().__init__( + base_url=base_url, + headers=headers, + transport=transport, + follow_redirects=follow_redirects, + cookies=cookies, + ) + + @contextlib.contextmanager + def _portal_factory(self) -> typing.Generator[anyio.abc.BlockingPortal, None, None]: + if self.portal is not None: + yield self.portal + else: + with anyio.from_thread.start_blocking_portal(**self.async_backend) as portal: + yield portal + + def request( # type: ignore[override] + self, + method: str, + url: httpx._types.URLTypes, + *, + content: httpx._types.RequestContent | None = None, + data: _RequestData | None = None, + files: httpx._types.RequestFiles | None = None, + json: typing.Any = None, + params: httpx._types.QueryParamTypes | None = None, + headers: httpx._types.HeaderTypes | None = None, + cookies: httpx._types.CookieTypes | None = None, + auth: httpx._types.AuthTypes | httpx._client.UseClientDefault = httpx._client.USE_CLIENT_DEFAULT, + follow_redirects: bool | httpx._client.UseClientDefault = httpx._client.USE_CLIENT_DEFAULT, + timeout: httpx._types.TimeoutTypes | httpx._client.UseClientDefault = httpx._client.USE_CLIENT_DEFAULT, + extensions: dict[str, typing.Any] | None = None, + ) -> httpx.Response: + if timeout is not httpx.USE_CLIENT_DEFAULT: + warnings.warn( + "You should not use the 'timeout' argument with the TestClient. " + "See https://github.com/encode/starlette/issues/1108 for more information.", + DeprecationWarning, + ) + url = self._merge_url(url) + return super().request( + method, + url, + content=content, + data=data, + files=files, + json=json, + params=params, + headers=headers, + cookies=cookies, + auth=auth, + follow_redirects=follow_redirects, + timeout=timeout, + extensions=extensions, + ) + + def get( # type: ignore[override] + self, + url: httpx._types.URLTypes, + *, + params: httpx._types.QueryParamTypes | None = None, + headers: httpx._types.HeaderTypes | None = None, + cookies: httpx._types.CookieTypes | None = None, + auth: httpx._types.AuthTypes | httpx._client.UseClientDefault = httpx._client.USE_CLIENT_DEFAULT, + follow_redirects: bool | httpx._client.UseClientDefault = httpx._client.USE_CLIENT_DEFAULT, + timeout: httpx._types.TimeoutTypes | httpx._client.UseClientDefault = httpx._client.USE_CLIENT_DEFAULT, + extensions: dict[str, typing.Any] | None = None, + ) -> httpx.Response: + return super().get( + url, + params=params, + headers=headers, + cookies=cookies, + auth=auth, + follow_redirects=follow_redirects, + timeout=timeout, + extensions=extensions, + ) + + def options( # type: ignore[override] + self, + url: httpx._types.URLTypes, + *, + params: httpx._types.QueryParamTypes | None = None, + headers: httpx._types.HeaderTypes | None = None, + cookies: httpx._types.CookieTypes | None = None, + auth: httpx._types.AuthTypes | httpx._client.UseClientDefault = httpx._client.USE_CLIENT_DEFAULT, + follow_redirects: bool | httpx._client.UseClientDefault = httpx._client.USE_CLIENT_DEFAULT, + timeout: httpx._types.TimeoutTypes | httpx._client.UseClientDefault = httpx._client.USE_CLIENT_DEFAULT, + extensions: dict[str, typing.Any] | None = None, + ) -> httpx.Response: + return super().options( + url, + params=params, + headers=headers, + cookies=cookies, + auth=auth, + follow_redirects=follow_redirects, + timeout=timeout, + extensions=extensions, + ) + + def head( # type: ignore[override] + self, + url: httpx._types.URLTypes, + *, + params: httpx._types.QueryParamTypes | None = None, + headers: httpx._types.HeaderTypes | None = None, + cookies: httpx._types.CookieTypes | None = None, + auth: httpx._types.AuthTypes | httpx._client.UseClientDefault = httpx._client.USE_CLIENT_DEFAULT, + follow_redirects: bool | httpx._client.UseClientDefault = httpx._client.USE_CLIENT_DEFAULT, + timeout: httpx._types.TimeoutTypes | httpx._client.UseClientDefault = httpx._client.USE_CLIENT_DEFAULT, + extensions: dict[str, typing.Any] | None = None, + ) -> httpx.Response: + return super().head( + url, + params=params, + headers=headers, + cookies=cookies, + auth=auth, + follow_redirects=follow_redirects, + timeout=timeout, + extensions=extensions, + ) + + def post( # type: ignore[override] + self, + url: httpx._types.URLTypes, + *, + content: httpx._types.RequestContent | None = None, + data: _RequestData | None = None, + files: httpx._types.RequestFiles | None = None, + json: typing.Any = None, + params: httpx._types.QueryParamTypes | None = None, + headers: httpx._types.HeaderTypes | None = None, + cookies: httpx._types.CookieTypes | None = None, + auth: httpx._types.AuthTypes | httpx._client.UseClientDefault = httpx._client.USE_CLIENT_DEFAULT, + follow_redirects: bool | httpx._client.UseClientDefault = httpx._client.USE_CLIENT_DEFAULT, + timeout: httpx._types.TimeoutTypes | httpx._client.UseClientDefault = httpx._client.USE_CLIENT_DEFAULT, + extensions: dict[str, typing.Any] | None = None, + ) -> httpx.Response: + return super().post( + url, + content=content, + data=data, + files=files, + json=json, + params=params, + headers=headers, + cookies=cookies, + auth=auth, + follow_redirects=follow_redirects, + timeout=timeout, + extensions=extensions, + ) + + def put( # type: ignore[override] + self, + url: httpx._types.URLTypes, + *, + content: httpx._types.RequestContent | None = None, + data: _RequestData | None = None, + files: httpx._types.RequestFiles | None = None, + json: typing.Any = None, + params: httpx._types.QueryParamTypes | None = None, + headers: httpx._types.HeaderTypes | None = None, + cookies: httpx._types.CookieTypes | None = None, + auth: httpx._types.AuthTypes | httpx._client.UseClientDefault = httpx._client.USE_CLIENT_DEFAULT, + follow_redirects: bool | httpx._client.UseClientDefault = httpx._client.USE_CLIENT_DEFAULT, + timeout: httpx._types.TimeoutTypes | httpx._client.UseClientDefault = httpx._client.USE_CLIENT_DEFAULT, + extensions: dict[str, typing.Any] | None = None, + ) -> httpx.Response: + return super().put( + url, + content=content, + data=data, + files=files, + json=json, + params=params, + headers=headers, + cookies=cookies, + auth=auth, + follow_redirects=follow_redirects, + timeout=timeout, + extensions=extensions, + ) + + def patch( # type: ignore[override] + self, + url: httpx._types.URLTypes, + *, + content: httpx._types.RequestContent | None = None, + data: _RequestData | None = None, + files: httpx._types.RequestFiles | None = None, + json: typing.Any = None, + params: httpx._types.QueryParamTypes | None = None, + headers: httpx._types.HeaderTypes | None = None, + cookies: httpx._types.CookieTypes | None = None, + auth: httpx._types.AuthTypes | httpx._client.UseClientDefault = httpx._client.USE_CLIENT_DEFAULT, + follow_redirects: bool | httpx._client.UseClientDefault = httpx._client.USE_CLIENT_DEFAULT, + timeout: httpx._types.TimeoutTypes | httpx._client.UseClientDefault = httpx._client.USE_CLIENT_DEFAULT, + extensions: dict[str, typing.Any] | None = None, + ) -> httpx.Response: + return super().patch( + url, + content=content, + data=data, + files=files, + json=json, + params=params, + headers=headers, + cookies=cookies, + auth=auth, + follow_redirects=follow_redirects, + timeout=timeout, + extensions=extensions, + ) + + def delete( # type: ignore[override] + self, + url: httpx._types.URLTypes, + *, + params: httpx._types.QueryParamTypes | None = None, + headers: httpx._types.HeaderTypes | None = None, + cookies: httpx._types.CookieTypes | None = None, + auth: httpx._types.AuthTypes | httpx._client.UseClientDefault = httpx._client.USE_CLIENT_DEFAULT, + follow_redirects: bool | httpx._client.UseClientDefault = httpx._client.USE_CLIENT_DEFAULT, + timeout: httpx._types.TimeoutTypes | httpx._client.UseClientDefault = httpx._client.USE_CLIENT_DEFAULT, + extensions: dict[str, typing.Any] | None = None, + ) -> httpx.Response: + return super().delete( + url, + params=params, + headers=headers, + cookies=cookies, + auth=auth, + follow_redirects=follow_redirects, + timeout=timeout, + extensions=extensions, + ) + + def websocket_connect( + self, + url: str, + subprotocols: typing.Sequence[str] | None = None, + **kwargs: typing.Any, + ) -> WebSocketTestSession: + url = urljoin("ws://testserver", url) + headers = kwargs.get("headers", {}) + headers.setdefault("connection", "upgrade") + headers.setdefault("sec-websocket-key", "testserver==") + headers.setdefault("sec-websocket-version", "13") + if subprotocols is not None: + headers.setdefault("sec-websocket-protocol", ", ".join(subprotocols)) + kwargs["headers"] = headers + try: + super().request("GET", url, **kwargs) + except _Upgrade as exc: + session = exc.session + else: + raise RuntimeError("Expected WebSocket upgrade") # pragma: no cover + + return session + + def __enter__(self) -> TestClient: + with contextlib.ExitStack() as stack: + self.portal = portal = stack.enter_context(anyio.from_thread.start_blocking_portal(**self.async_backend)) + + @stack.callback + def reset_portal() -> None: + self.portal = None + + send: anyio.create_memory_object_stream[typing.MutableMapping[str, typing.Any] | None] = ( + anyio.create_memory_object_stream(math.inf) + ) + receive: anyio.create_memory_object_stream[typing.MutableMapping[str, typing.Any]] = ( + anyio.create_memory_object_stream(math.inf) + ) + for channel in (*send, *receive): + stack.callback(channel.close) + self.stream_send = StapledObjectStream(*send) + self.stream_receive = StapledObjectStream(*receive) + self.task = portal.start_task_soon(self.lifespan) + portal.call(self.wait_startup) + + @stack.callback + def wait_shutdown() -> None: + portal.call(self.wait_shutdown) + + self.exit_stack = stack.pop_all() + + return self + + def __exit__(self, *args: typing.Any) -> None: + self.exit_stack.close() + + async def lifespan(self) -> None: + scope = {"type": "lifespan", "state": self.app_state} + try: + await self.app(scope, self.stream_receive.receive, self.stream_send.send) + finally: + await self.stream_send.send(None) + + async def wait_startup(self) -> None: + await self.stream_receive.send({"type": "lifespan.startup"}) + + async def receive() -> typing.Any: + message = await self.stream_send.receive() + if message is None: + self.task.result() + return message + + message = await receive() + assert message["type"] in ( + "lifespan.startup.complete", + "lifespan.startup.failed", + ) + if message["type"] == "lifespan.startup.failed": + await receive() + + async def wait_shutdown(self) -> None: + async def receive() -> typing.Any: + message = await self.stream_send.receive() + if message is None: + self.task.result() + return message + + await self.stream_receive.send({"type": "lifespan.shutdown"}) + message = await receive() + assert message["type"] in ( + "lifespan.shutdown.complete", + "lifespan.shutdown.failed", + ) + if message["type"] == "lifespan.shutdown.failed": + await receive() diff --git a/lib/python3.12/site-packages/starlette/types.py b/lib/python3.12/site-packages/starlette/types.py new file mode 100644 index 0000000000000000000000000000000000000000..893f872964c2e6df9a81fdba3dd8fadfeaab9731 --- /dev/null +++ b/lib/python3.12/site-packages/starlette/types.py @@ -0,0 +1,24 @@ +import typing + +if typing.TYPE_CHECKING: + from starlette.requests import Request + from starlette.responses import Response + from starlette.websockets import WebSocket + +AppType = typing.TypeVar("AppType") + +Scope = typing.MutableMapping[str, typing.Any] +Message = typing.MutableMapping[str, typing.Any] + +Receive = typing.Callable[[], typing.Awaitable[Message]] +Send = typing.Callable[[Message], typing.Awaitable[None]] + +ASGIApp = typing.Callable[[Scope, Receive, Send], typing.Awaitable[None]] + +StatelessLifespan = typing.Callable[[AppType], typing.AsyncContextManager[None]] +StatefulLifespan = typing.Callable[[AppType], typing.AsyncContextManager[typing.Mapping[str, typing.Any]]] +Lifespan = typing.Union[StatelessLifespan[AppType], StatefulLifespan[AppType]] + +HTTPExceptionHandler = typing.Callable[["Request", Exception], "Response | typing.Awaitable[Response]"] +WebSocketExceptionHandler = typing.Callable[["WebSocket", Exception], typing.Awaitable[None]] +ExceptionHandler = typing.Union[HTTPExceptionHandler, WebSocketExceptionHandler] diff --git a/lib/python3.12/site-packages/starlette/websockets.py b/lib/python3.12/site-packages/starlette/websockets.py new file mode 100644 index 0000000000000000000000000000000000000000..6b46f4eaef63a97d9263f0add4da1ac657b86ced --- /dev/null +++ b/lib/python3.12/site-packages/starlette/websockets.py @@ -0,0 +1,195 @@ +from __future__ import annotations + +import enum +import json +import typing + +from starlette.requests import HTTPConnection +from starlette.responses import Response +from starlette.types import Message, Receive, Scope, Send + + +class WebSocketState(enum.Enum): + CONNECTING = 0 + CONNECTED = 1 + DISCONNECTED = 2 + RESPONSE = 3 + + +class WebSocketDisconnect(Exception): + def __init__(self, code: int = 1000, reason: str | None = None) -> None: + self.code = code + self.reason = reason or "" + + +class WebSocket(HTTPConnection): + def __init__(self, scope: Scope, receive: Receive, send: Send) -> None: + super().__init__(scope) + assert scope["type"] == "websocket" + self._receive = receive + self._send = send + self.client_state = WebSocketState.CONNECTING + self.application_state = WebSocketState.CONNECTING + + async def receive(self) -> Message: + """ + Receive ASGI websocket messages, ensuring valid state transitions. + """ + if self.client_state == WebSocketState.CONNECTING: + message = await self._receive() + message_type = message["type"] + if message_type != "websocket.connect": + raise RuntimeError(f'Expected ASGI message "websocket.connect", but got {message_type!r}') + self.client_state = WebSocketState.CONNECTED + return message + elif self.client_state == WebSocketState.CONNECTED: + message = await self._receive() + message_type = message["type"] + if message_type not in {"websocket.receive", "websocket.disconnect"}: + raise RuntimeError( + f'Expected ASGI message "websocket.receive" or "websocket.disconnect", but got {message_type!r}' + ) + if message_type == "websocket.disconnect": + self.client_state = WebSocketState.DISCONNECTED + return message + else: + raise RuntimeError('Cannot call "receive" once a disconnect message has been received.') + + async def send(self, message: Message) -> None: + """ + Send ASGI websocket messages, ensuring valid state transitions. + """ + if self.application_state == WebSocketState.CONNECTING: + message_type = message["type"] + if message_type not in {"websocket.accept", "websocket.close", "websocket.http.response.start"}: + raise RuntimeError( + 'Expected ASGI message "websocket.accept", "websocket.close" or "websocket.http.response.start", ' + f"but got {message_type!r}" + ) + if message_type == "websocket.close": + self.application_state = WebSocketState.DISCONNECTED + elif message_type == "websocket.http.response.start": + self.application_state = WebSocketState.RESPONSE + else: + self.application_state = WebSocketState.CONNECTED + await self._send(message) + elif self.application_state == WebSocketState.CONNECTED: + message_type = message["type"] + if message_type not in {"websocket.send", "websocket.close"}: + raise RuntimeError( + f'Expected ASGI message "websocket.send" or "websocket.close", but got {message_type!r}' + ) + if message_type == "websocket.close": + self.application_state = WebSocketState.DISCONNECTED + try: + await self._send(message) + except OSError: + self.application_state = WebSocketState.DISCONNECTED + raise WebSocketDisconnect(code=1006) + elif self.application_state == WebSocketState.RESPONSE: + message_type = message["type"] + if message_type != "websocket.http.response.body": + raise RuntimeError(f'Expected ASGI message "websocket.http.response.body", but got {message_type!r}') + if not message.get("more_body", False): + self.application_state = WebSocketState.DISCONNECTED + await self._send(message) + else: + raise RuntimeError('Cannot call "send" once a close message has been sent.') + + async def accept( + self, + subprotocol: str | None = None, + headers: typing.Iterable[tuple[bytes, bytes]] | None = None, + ) -> None: + headers = headers or [] + + if self.client_state == WebSocketState.CONNECTING: # pragma: no branch + # If we haven't yet seen the 'connect' message, then wait for it first. + await self.receive() + await self.send({"type": "websocket.accept", "subprotocol": subprotocol, "headers": headers}) + + def _raise_on_disconnect(self, message: Message) -> None: + if message["type"] == "websocket.disconnect": + raise WebSocketDisconnect(message["code"], message.get("reason")) + + async def receive_text(self) -> str: + if self.application_state != WebSocketState.CONNECTED: + raise RuntimeError('WebSocket is not connected. Need to call "accept" first.') + message = await self.receive() + self._raise_on_disconnect(message) + return typing.cast(str, message["text"]) + + async def receive_bytes(self) -> bytes: + if self.application_state != WebSocketState.CONNECTED: + raise RuntimeError('WebSocket is not connected. Need to call "accept" first.') + message = await self.receive() + self._raise_on_disconnect(message) + return typing.cast(bytes, message["bytes"]) + + async def receive_json(self, mode: str = "text") -> typing.Any: + if mode not in {"text", "binary"}: + raise RuntimeError('The "mode" argument should be "text" or "binary".') + if self.application_state != WebSocketState.CONNECTED: + raise RuntimeError('WebSocket is not connected. Need to call "accept" first.') + message = await self.receive() + self._raise_on_disconnect(message) + + if mode == "text": + text = message["text"] + else: + text = message["bytes"].decode("utf-8") + return json.loads(text) + + async def iter_text(self) -> typing.AsyncIterator[str]: + try: + while True: + yield await self.receive_text() + except WebSocketDisconnect: + pass + + async def iter_bytes(self) -> typing.AsyncIterator[bytes]: + try: + while True: + yield await self.receive_bytes() + except WebSocketDisconnect: + pass + + async def iter_json(self) -> typing.AsyncIterator[typing.Any]: + try: + while True: + yield await self.receive_json() + except WebSocketDisconnect: + pass + + async def send_text(self, data: str) -> None: + await self.send({"type": "websocket.send", "text": data}) + + async def send_bytes(self, data: bytes) -> None: + await self.send({"type": "websocket.send", "bytes": data}) + + async def send_json(self, data: typing.Any, mode: str = "text") -> None: + if mode not in {"text", "binary"}: + raise RuntimeError('The "mode" argument should be "text" or "binary".') + text = json.dumps(data, separators=(",", ":"), ensure_ascii=False) + if mode == "text": + await self.send({"type": "websocket.send", "text": text}) + else: + await self.send({"type": "websocket.send", "bytes": text.encode("utf-8")}) + + async def close(self, code: int = 1000, reason: str | None = None) -> None: + await self.send({"type": "websocket.close", "code": code, "reason": reason or ""}) + + async def send_denial_response(self, response: Response) -> None: + if "websocket.http.response" in self.scope.get("extensions", {}): + await response(self.scope, self.receive, self.send) + else: + raise RuntimeError("The server doesn't support the Websocket Denial Response extension.") + + +class WebSocketClose: + def __init__(self, code: int = 1000, reason: str | None = None) -> None: + self.code = code + self.reason = reason or "" + + async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None: + await send({"type": "websocket.close", "code": self.code, "reason": self.reason}) diff --git a/lib/python3.12/site-packages/tokenizers-0.21.1.dist-info/INSTALLER b/lib/python3.12/site-packages/tokenizers-0.21.1.dist-info/INSTALLER new file mode 100644 index 0000000000000000000000000000000000000000..a1b589e38a32041e49332e5e81c2d363dc418d68 --- /dev/null +++ b/lib/python3.12/site-packages/tokenizers-0.21.1.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/lib/python3.12/site-packages/tokenizers-0.21.1.dist-info/RECORD b/lib/python3.12/site-packages/tokenizers-0.21.1.dist-info/RECORD new file mode 100644 index 0000000000000000000000000000000000000000..b118f10a38a2deedef21e98a8e0dc6ee26599c61 --- /dev/null +++ b/lib/python3.12/site-packages/tokenizers-0.21.1.dist-info/RECORD @@ -0,0 +1,46 @@ +tokenizers-0.21.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +tokenizers-0.21.1.dist-info/METADATA,sha256=GL78TpkKOH8KBbB1Bba01wXgp9ImYCYPAtmQl3J4dZk,6774 +tokenizers-0.21.1.dist-info/RECORD,, +tokenizers-0.21.1.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +tokenizers-0.21.1.dist-info/WHEEL,sha256=f5fytCivPqoitFei8k2YKm7f1DR3oNmqLTfZvuAlYZc,127 +tokenizers/__init__.py,sha256=ZE5ZagUvobBScrHBQdEobhx4wqM0bsq9F9aLYkBNjYQ,2615 +tokenizers/__init__.pyi,sha256=jw34WZXaYu8NBBJ2_cypfOqJYxI7CXKPzlveisXw4XQ,40182 +tokenizers/__pycache__/__init__.cpython-312.pyc,, +tokenizers/decoders/__init__.py,sha256=hfwM6CFUDvlMGGL4-xsaaYz81K9P5rQI5ZL5UHWK8Y4,372 +tokenizers/decoders/__init__.pyi,sha256=qylMinPKn2_NAMC6BzcXqFyndoicLws34Q6OLH1gcp4,7378 +tokenizers/decoders/__pycache__/__init__.cpython-312.pyc,, +tokenizers/implementations/__init__.py,sha256=VzAsplaIo7rl4AFO8Miu7ig7MfZjvonwVblZw01zR6M,310 +tokenizers/implementations/__pycache__/__init__.cpython-312.pyc,, +tokenizers/implementations/__pycache__/base_tokenizer.cpython-312.pyc,, +tokenizers/implementations/__pycache__/bert_wordpiece.cpython-312.pyc,, +tokenizers/implementations/__pycache__/byte_level_bpe.cpython-312.pyc,, +tokenizers/implementations/__pycache__/char_level_bpe.cpython-312.pyc,, +tokenizers/implementations/__pycache__/sentencepiece_bpe.cpython-312.pyc,, +tokenizers/implementations/__pycache__/sentencepiece_unigram.cpython-312.pyc,, +tokenizers/implementations/base_tokenizer.py,sha256=2TFZhLupaJiMDYGJuUNmxYJv-cnR8bDHmbMzaYpFROs,14206 +tokenizers/implementations/bert_wordpiece.py,sha256=sKCum0FKPYdSgJFJN8LDerVBoTDRSqyqSdrcm-lvQqI,5520 +tokenizers/implementations/byte_level_bpe.py,sha256=OA_jyy3EQmYTa6hnf-EKwLOFuyroqFYOJz25ysM2BUk,4289 +tokenizers/implementations/char_level_bpe.py,sha256=Q2ZEAW0xMQHF7YCUtmplwaxbU-J0P2NK4PJGMxUb-_c,5466 +tokenizers/implementations/sentencepiece_bpe.py,sha256=LwrofoohnUfME2lK2lQYoyQIhP84RP0CIlHRaj0hyNs,3738 +tokenizers/implementations/sentencepiece_unigram.py,sha256=SYiVXL8ZtqLXKpuqwnwmrfxgGotu8yAkOu7dLztEXIo,7580 +tokenizers/models/__init__.py,sha256=eJZ4HTAQZpxnKILNylWaTFqxXy-Ba6OKswWN47feeV8,176 +tokenizers/models/__init__.pyi,sha256=clPTwiyjz7FlVdEuwo_3Wa_TmQrbZhW0SGmnNylepnY,16929 +tokenizers/models/__pycache__/__init__.cpython-312.pyc,, +tokenizers/normalizers/__init__.py,sha256=_06w4cqRItveEgIddYaLMScgkSOkIAMIzYCesb5AA4U,841 +tokenizers/normalizers/__init__.pyi,sha256=lSFqDb_lPZBfRxEG99EcFEaU1HlnIhIQUu7zZIyP4AY,20898 +tokenizers/normalizers/__pycache__/__init__.cpython-312.pyc,, +tokenizers/pre_tokenizers/__init__.py,sha256=wd6KYQA_RsGSQK-HeG9opTRhv4ttSRkyno2dk6az-PM,557 +tokenizers/pre_tokenizers/__init__.pyi,sha256=k9Jnez-t4ww_Pyj8OkVeo78v9TntcNn6ri9kwB590KE,23606 +tokenizers/pre_tokenizers/__pycache__/__init__.cpython-312.pyc,, +tokenizers/processors/__init__.py,sha256=xM2DEKwKtHIumHsszM8AMkq-AlaqvBZFXWgLU8SNhOY,307 +tokenizers/processors/__init__.pyi,sha256=hx767ZY8SHhxb_hiXPRxm-f_KcoR4XDx7vfK2c0lR-Q,11357 +tokenizers/processors/__pycache__/__init__.cpython-312.pyc,, +tokenizers/tokenizers.abi3.so,sha256=kA2fIqNga2xHoJAhXfwqsJZQhWnWNipHoi0kZwt5yP8,9086856 +tokenizers/tools/__init__.py,sha256=xG8caB9OHC8cbB01S5vYV14HZxhO6eWbLehsb70ppio,55 +tokenizers/tools/__pycache__/__init__.cpython-312.pyc,, +tokenizers/tools/__pycache__/visualizer.cpython-312.pyc,, +tokenizers/tools/visualizer-styles.css,sha256=zAydq1oGWD8QEll4-eyL8Llw0B1sty_hpIE3tYxL02k,4850 +tokenizers/tools/visualizer.py,sha256=0W90s4Qm8Nd6P-npqQX-bCMLQCfAEPk2qgj-K8r7OMc,14624 +tokenizers/trainers/__init__.py,sha256=UTu22AGcp76IvpW45xLRbJWET04NxPW6NfCb2YYz0EM,248 +tokenizers/trainers/__init__.pyi,sha256=3TwFKts4me7zQfVRcSTmtXYiP4XwcRjfAYtwqoZVtoQ,5382 +tokenizers/trainers/__pycache__/__init__.cpython-312.pyc,, diff --git a/lib/python3.12/site-packages/tokenizers-0.21.1.dist-info/WHEEL b/lib/python3.12/site-packages/tokenizers-0.21.1.dist-info/WHEEL new file mode 100644 index 0000000000000000000000000000000000000000..d41d6e41aa91717992f1f36354e534d145515f83 --- /dev/null +++ b/lib/python3.12/site-packages/tokenizers-0.21.1.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: maturin (1.8.2) +Root-Is-Purelib: false +Tag: cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64 diff --git a/lib/python3.12/site-packages/tzdata/__init__.py b/lib/python3.12/site-packages/tzdata/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..b1534dfb64286636dcdf93f172b4ac5bdcb44232 --- /dev/null +++ b/lib/python3.12/site-packages/tzdata/__init__.py @@ -0,0 +1,6 @@ +# IANA versions like 2020a are not valid PEP 440 identifiers; the recommended +# way to translate the version is to use YYYY.n where `n` is a 0-based index. +__version__ = "2025.3" + +# This exposes the original IANA version number. +IANA_VERSION = "2025c" diff --git a/lib/python3.12/site-packages/tzdata/zoneinfo/Asia/Aqtobe b/lib/python3.12/site-packages/tzdata/zoneinfo/Asia/Aqtobe new file mode 100644 index 0000000000000000000000000000000000000000..62c5840a83e29b4fcedba95e438581cec96b3cf6 Binary files /dev/null and b/lib/python3.12/site-packages/tzdata/zoneinfo/Asia/Aqtobe differ diff --git a/lib/python3.12/site-packages/tzdata/zoneinfo/Asia/Baku b/lib/python3.12/site-packages/tzdata/zoneinfo/Asia/Baku new file mode 100644 index 0000000000000000000000000000000000000000..96203d7a4266cd8646032165950374761090e318 Binary files /dev/null and b/lib/python3.12/site-packages/tzdata/zoneinfo/Asia/Baku differ diff --git a/lib/python3.12/site-packages/tzdata/zoneinfo/Asia/Dacca b/lib/python3.12/site-packages/tzdata/zoneinfo/Asia/Dacca new file mode 100644 index 0000000000000000000000000000000000000000..28136808b6d1029676448d8711265d8c55cb4bae Binary files /dev/null and b/lib/python3.12/site-packages/tzdata/zoneinfo/Asia/Dacca differ diff --git a/lib/python3.12/site-packages/tzdata/zoneinfo/Asia/Dushanbe b/lib/python3.12/site-packages/tzdata/zoneinfo/Asia/Dushanbe new file mode 100644 index 0000000000000000000000000000000000000000..d83fb076a256817ca0a3ec4e43c7768e6680dc84 Binary files /dev/null and b/lib/python3.12/site-packages/tzdata/zoneinfo/Asia/Dushanbe differ diff --git a/lib/python3.12/site-packages/tzdata/zoneinfo/Asia/Harbin b/lib/python3.12/site-packages/tzdata/zoneinfo/Asia/Harbin new file mode 100644 index 0000000000000000000000000000000000000000..d6b66984a2f36ae36b35e174756707aa7286c292 Binary files /dev/null and b/lib/python3.12/site-packages/tzdata/zoneinfo/Asia/Harbin differ diff --git a/lib/python3.12/site-packages/tzdata/zoneinfo/Asia/Krasnoyarsk b/lib/python3.12/site-packages/tzdata/zoneinfo/Asia/Krasnoyarsk new file mode 100644 index 0000000000000000000000000000000000000000..e0d4fcb5c3d781943a65dc53cca9fab5d1905f9f Binary files /dev/null and b/lib/python3.12/site-packages/tzdata/zoneinfo/Asia/Krasnoyarsk differ diff --git a/lib/python3.12/site-packages/tzdata/zoneinfo/Asia/Kuala_Lumpur b/lib/python3.12/site-packages/tzdata/zoneinfo/Asia/Kuala_Lumpur new file mode 100644 index 0000000000000000000000000000000000000000..dbbdea3c8149004cfd525a0fc26e5da72b20e8a1 Binary files /dev/null and b/lib/python3.12/site-packages/tzdata/zoneinfo/Asia/Kuala_Lumpur differ diff --git a/lib/python3.12/site-packages/tzdata/zoneinfo/Asia/Macau b/lib/python3.12/site-packages/tzdata/zoneinfo/Asia/Macau new file mode 100644 index 0000000000000000000000000000000000000000..c22f75e42db6b12db3c837056436cb77d176b83e Binary files /dev/null and b/lib/python3.12/site-packages/tzdata/zoneinfo/Asia/Macau differ diff --git a/lib/python3.12/site-packages/tzdata/zoneinfo/Asia/Nicosia b/lib/python3.12/site-packages/tzdata/zoneinfo/Asia/Nicosia new file mode 100644 index 0000000000000000000000000000000000000000..390347f442a486e296689c189e3346695bba5105 Binary files /dev/null and b/lib/python3.12/site-packages/tzdata/zoneinfo/Asia/Nicosia differ diff --git a/lib/python3.12/site-packages/tzdata/zoneinfo/Asia/Novokuznetsk b/lib/python3.12/site-packages/tzdata/zoneinfo/Asia/Novokuznetsk new file mode 100644 index 0000000000000000000000000000000000000000..9378d50539dfa8f1dabcb40d3720f64906d36202 Binary files /dev/null and b/lib/python3.12/site-packages/tzdata/zoneinfo/Asia/Novokuznetsk differ diff --git a/lib/python3.12/site-packages/tzdata/zoneinfo/Asia/Qatar b/lib/python3.12/site-packages/tzdata/zoneinfo/Asia/Qatar new file mode 100644 index 0000000000000000000000000000000000000000..7409d74983c8d0cd8347a663c3bfbc1c041124da Binary files /dev/null and b/lib/python3.12/site-packages/tzdata/zoneinfo/Asia/Qatar differ diff --git a/lib/python3.12/site-packages/tzdata/zoneinfo/Asia/Singapore b/lib/python3.12/site-packages/tzdata/zoneinfo/Asia/Singapore new file mode 100644 index 0000000000000000000000000000000000000000..dbbdea3c8149004cfd525a0fc26e5da72b20e8a1 Binary files /dev/null and b/lib/python3.12/site-packages/tzdata/zoneinfo/Asia/Singapore differ diff --git a/lib/python3.12/site-packages/tzdata/zoneinfo/Asia/Srednekolymsk b/lib/python3.12/site-packages/tzdata/zoneinfo/Asia/Srednekolymsk new file mode 100644 index 0000000000000000000000000000000000000000..7fdee5cbee2b1ba0904a672dde16240404466fb9 Binary files /dev/null and b/lib/python3.12/site-packages/tzdata/zoneinfo/Asia/Srednekolymsk differ diff --git a/lib/python3.12/site-packages/tzdata/zoneinfo/Asia/Taipei b/lib/python3.12/site-packages/tzdata/zoneinfo/Asia/Taipei new file mode 100644 index 0000000000000000000000000000000000000000..35d89d036d07c3f28dec64092ab1b533c21ae2bc Binary files /dev/null and b/lib/python3.12/site-packages/tzdata/zoneinfo/Asia/Taipei differ diff --git a/lib/python3.12/site-packages/tzdata/zoneinfo/Asia/Tashkent b/lib/python3.12/site-packages/tzdata/zoneinfo/Asia/Tashkent new file mode 100644 index 0000000000000000000000000000000000000000..65ee428ce1c5093a4b3dd29512d98a33b4c753a0 Binary files /dev/null and b/lib/python3.12/site-packages/tzdata/zoneinfo/Asia/Tashkent differ diff --git a/lib/python3.12/site-packages/tzdata/zoneinfo/Asia/Tbilisi b/lib/python3.12/site-packages/tzdata/zoneinfo/Asia/Tbilisi new file mode 100644 index 0000000000000000000000000000000000000000..166e4341d6ce65728367641a467a925800044df6 Binary files /dev/null and b/lib/python3.12/site-packages/tzdata/zoneinfo/Asia/Tbilisi differ diff --git a/lib/python3.12/site-packages/tzdata/zoneinfo/Asia/Tehran b/lib/python3.12/site-packages/tzdata/zoneinfo/Asia/Tehran new file mode 100644 index 0000000000000000000000000000000000000000..6fd31e075a29223eeea3f9a1a747b4531775f8ef Binary files /dev/null and b/lib/python3.12/site-packages/tzdata/zoneinfo/Asia/Tehran differ diff --git a/lib/python3.12/site-packages/tzdata/zoneinfo/Asia/Ulan_Bator b/lib/python3.12/site-packages/tzdata/zoneinfo/Asia/Ulan_Bator new file mode 100644 index 0000000000000000000000000000000000000000..6f5d3a15abbe48b8a4dc72aadc88c416160a56a6 Binary files /dev/null and b/lib/python3.12/site-packages/tzdata/zoneinfo/Asia/Ulan_Bator differ diff --git a/lib/python3.12/site-packages/tzdata/zoneinfo/Asia/Yangon b/lib/python3.12/site-packages/tzdata/zoneinfo/Asia/Yangon new file mode 100644 index 0000000000000000000000000000000000000000..14b2ad09ead50a62d5e2b426396c51f9beb293be Binary files /dev/null and b/lib/python3.12/site-packages/tzdata/zoneinfo/Asia/Yangon differ diff --git a/lib/python3.12/site-packages/tzdata/zoneinfo/CET b/lib/python3.12/site-packages/tzdata/zoneinfo/CET new file mode 100644 index 0000000000000000000000000000000000000000..31973271d2f87f7e9df4e8b0a1481f09a9e5407d Binary files /dev/null and b/lib/python3.12/site-packages/tzdata/zoneinfo/CET differ diff --git a/lib/python3.12/site-packages/tzdata/zoneinfo/CST6CDT b/lib/python3.12/site-packages/tzdata/zoneinfo/CST6CDT new file mode 100644 index 0000000000000000000000000000000000000000..b016880653929aa40dd5ac0e82e4094a9d787cdf Binary files /dev/null and b/lib/python3.12/site-packages/tzdata/zoneinfo/CST6CDT differ diff --git a/lib/python3.12/site-packages/tzdata/zoneinfo/Chile/Continental b/lib/python3.12/site-packages/tzdata/zoneinfo/Chile/Continental new file mode 100644 index 0000000000000000000000000000000000000000..d3fc9b8343369abf5f14be781397f426c5969608 Binary files /dev/null and b/lib/python3.12/site-packages/tzdata/zoneinfo/Chile/Continental differ diff --git a/lib/python3.12/site-packages/tzdata/zoneinfo/Chile/EasterIsland b/lib/python3.12/site-packages/tzdata/zoneinfo/Chile/EasterIsland new file mode 100644 index 0000000000000000000000000000000000000000..54dff005b876339f5c1ff3dc0aeae1519c29b368 Binary files /dev/null and b/lib/python3.12/site-packages/tzdata/zoneinfo/Chile/EasterIsland differ diff --git a/lib/python3.12/site-packages/tzdata/zoneinfo/Chile/__init__.py b/lib/python3.12/site-packages/tzdata/zoneinfo/Chile/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/lib/python3.12/site-packages/tzdata/zoneinfo/Chile/__pycache__/__init__.cpython-312.pyc b/lib/python3.12/site-packages/tzdata/zoneinfo/Chile/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4d3731f4504d3aa1d4f95aa0bb046193aa589dd6 Binary files /dev/null and b/lib/python3.12/site-packages/tzdata/zoneinfo/Chile/__pycache__/__init__.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/tzdata/zoneinfo/Cuba b/lib/python3.12/site-packages/tzdata/zoneinfo/Cuba new file mode 100644 index 0000000000000000000000000000000000000000..e06629d36841463326ff3350bc2f94d0417c3cdd Binary files /dev/null and b/lib/python3.12/site-packages/tzdata/zoneinfo/Cuba differ diff --git a/lib/python3.12/site-packages/tzdata/zoneinfo/EET b/lib/python3.12/site-packages/tzdata/zoneinfo/EET new file mode 100644 index 0000000000000000000000000000000000000000..231bf9c3b713e3676dbd8f3ced867973c601e104 Binary files /dev/null and b/lib/python3.12/site-packages/tzdata/zoneinfo/EET differ diff --git a/lib/python3.12/site-packages/tzdata/zoneinfo/EST b/lib/python3.12/site-packages/tzdata/zoneinfo/EST new file mode 100644 index 0000000000000000000000000000000000000000..9154643f4c9189998392afb8a93e2e2eb9eaecf5 Binary files /dev/null and b/lib/python3.12/site-packages/tzdata/zoneinfo/EST differ diff --git a/lib/python3.12/site-packages/tzdata/zoneinfo/EST5EDT b/lib/python3.12/site-packages/tzdata/zoneinfo/EST5EDT new file mode 100644 index 0000000000000000000000000000000000000000..2b6c2eea14df07392729ae9f5712a44ec4f02bae Binary files /dev/null and b/lib/python3.12/site-packages/tzdata/zoneinfo/EST5EDT differ diff --git a/lib/python3.12/site-packages/tzdata/zoneinfo/Egypt b/lib/python3.12/site-packages/tzdata/zoneinfo/Egypt new file mode 100644 index 0000000000000000000000000000000000000000..1e6d48d1ca4e5416913c41e8814dc045c57d5b58 Binary files /dev/null and b/lib/python3.12/site-packages/tzdata/zoneinfo/Egypt differ diff --git a/lib/python3.12/site-packages/tzdata/zoneinfo/Eire b/lib/python3.12/site-packages/tzdata/zoneinfo/Eire new file mode 100644 index 0000000000000000000000000000000000000000..17d2b1582df89d5794f20fb028956dd9da154922 Binary files /dev/null and b/lib/python3.12/site-packages/tzdata/zoneinfo/Eire differ diff --git a/lib/python3.12/site-packages/tzdata/zoneinfo/Factory b/lib/python3.12/site-packages/tzdata/zoneinfo/Factory new file mode 100644 index 0000000000000000000000000000000000000000..b4dd7735ed5b945e3403f2dd7cd57712dc9184d3 Binary files /dev/null and b/lib/python3.12/site-packages/tzdata/zoneinfo/Factory differ diff --git a/lib/python3.12/site-packages/tzdata/zoneinfo/GB b/lib/python3.12/site-packages/tzdata/zoneinfo/GB new file mode 100644 index 0000000000000000000000000000000000000000..b9e95d92623c6cc4c35b16f7ceba3006f5ce4934 Binary files /dev/null and b/lib/python3.12/site-packages/tzdata/zoneinfo/GB differ diff --git a/lib/python3.12/site-packages/tzdata/zoneinfo/GB-Eire b/lib/python3.12/site-packages/tzdata/zoneinfo/GB-Eire new file mode 100644 index 0000000000000000000000000000000000000000..b9e95d92623c6cc4c35b16f7ceba3006f5ce4934 Binary files /dev/null and b/lib/python3.12/site-packages/tzdata/zoneinfo/GB-Eire differ diff --git a/lib/python3.12/site-packages/tzdata/zoneinfo/GMT b/lib/python3.12/site-packages/tzdata/zoneinfo/GMT new file mode 100644 index 0000000000000000000000000000000000000000..157573b1d340e0f57a0dd4d9698bd3798cbf7136 Binary files /dev/null and b/lib/python3.12/site-packages/tzdata/zoneinfo/GMT differ diff --git a/lib/python3.12/site-packages/tzdata/zoneinfo/GMT+0 b/lib/python3.12/site-packages/tzdata/zoneinfo/GMT+0 new file mode 100644 index 0000000000000000000000000000000000000000..157573b1d340e0f57a0dd4d9698bd3798cbf7136 Binary files /dev/null and b/lib/python3.12/site-packages/tzdata/zoneinfo/GMT+0 differ diff --git a/lib/python3.12/site-packages/tzdata/zoneinfo/GMT-0 b/lib/python3.12/site-packages/tzdata/zoneinfo/GMT-0 new file mode 100644 index 0000000000000000000000000000000000000000..157573b1d340e0f57a0dd4d9698bd3798cbf7136 Binary files /dev/null and b/lib/python3.12/site-packages/tzdata/zoneinfo/GMT-0 differ diff --git a/lib/python3.12/site-packages/tzdata/zoneinfo/GMT0 b/lib/python3.12/site-packages/tzdata/zoneinfo/GMT0 new file mode 100644 index 0000000000000000000000000000000000000000..157573b1d340e0f57a0dd4d9698bd3798cbf7136 Binary files /dev/null and b/lib/python3.12/site-packages/tzdata/zoneinfo/GMT0 differ diff --git a/lib/python3.12/site-packages/tzdata/zoneinfo/Greenwich b/lib/python3.12/site-packages/tzdata/zoneinfo/Greenwich new file mode 100644 index 0000000000000000000000000000000000000000..157573b1d340e0f57a0dd4d9698bd3798cbf7136 Binary files /dev/null and b/lib/python3.12/site-packages/tzdata/zoneinfo/Greenwich differ diff --git a/lib/python3.12/site-packages/tzdata/zoneinfo/HST b/lib/python3.12/site-packages/tzdata/zoneinfo/HST new file mode 100644 index 0000000000000000000000000000000000000000..40e3d492e6c22c30041c31f159d4fe0ee9451c03 Binary files /dev/null and b/lib/python3.12/site-packages/tzdata/zoneinfo/HST differ diff --git a/lib/python3.12/site-packages/tzdata/zoneinfo/Hongkong b/lib/python3.12/site-packages/tzdata/zoneinfo/Hongkong new file mode 100644 index 0000000000000000000000000000000000000000..c80e364801be87687625f72e8e2c3dbd0f7ae4bc Binary files /dev/null and b/lib/python3.12/site-packages/tzdata/zoneinfo/Hongkong differ diff --git a/lib/python3.12/site-packages/tzdata/zoneinfo/Iceland b/lib/python3.12/site-packages/tzdata/zoneinfo/Iceland new file mode 100644 index 0000000000000000000000000000000000000000..8906e88c819d9ad3b794eb6356a240a74a95ffae Binary files /dev/null and b/lib/python3.12/site-packages/tzdata/zoneinfo/Iceland differ diff --git a/lib/python3.12/site-packages/tzdata/zoneinfo/Iran b/lib/python3.12/site-packages/tzdata/zoneinfo/Iran new file mode 100644 index 0000000000000000000000000000000000000000..6fd31e075a29223eeea3f9a1a747b4531775f8ef Binary files /dev/null and b/lib/python3.12/site-packages/tzdata/zoneinfo/Iran differ diff --git a/lib/python3.12/site-packages/tzdata/zoneinfo/Israel b/lib/python3.12/site-packages/tzdata/zoneinfo/Israel new file mode 100644 index 0000000000000000000000000000000000000000..4c49bbf52440631eca750cacb7d79f259eeb8bd2 Binary files /dev/null and b/lib/python3.12/site-packages/tzdata/zoneinfo/Israel differ diff --git a/lib/python3.12/site-packages/tzdata/zoneinfo/Jamaica b/lib/python3.12/site-packages/tzdata/zoneinfo/Jamaica new file mode 100644 index 0000000000000000000000000000000000000000..be6b1b6f1e77a8f13a7400bcfc10c63a7ee1d55d Binary files /dev/null and b/lib/python3.12/site-packages/tzdata/zoneinfo/Jamaica differ diff --git a/lib/python3.12/site-packages/tzdata/zoneinfo/Japan b/lib/python3.12/site-packages/tzdata/zoneinfo/Japan new file mode 100644 index 0000000000000000000000000000000000000000..1aa066ce38fce7bd0a680f51d6f075718d153a77 Binary files /dev/null and b/lib/python3.12/site-packages/tzdata/zoneinfo/Japan differ diff --git a/lib/python3.12/site-packages/tzdata/zoneinfo/Kwajalein b/lib/python3.12/site-packages/tzdata/zoneinfo/Kwajalein new file mode 100644 index 0000000000000000000000000000000000000000..9416d522d0a3e19ab6b81317f5b94961c55e91fc Binary files /dev/null and b/lib/python3.12/site-packages/tzdata/zoneinfo/Kwajalein differ diff --git a/lib/python3.12/site-packages/tzdata/zoneinfo/Libya b/lib/python3.12/site-packages/tzdata/zoneinfo/Libya new file mode 100644 index 0000000000000000000000000000000000000000..e0c89971aabea2c87842a9276b043d0fd946e34e Binary files /dev/null and b/lib/python3.12/site-packages/tzdata/zoneinfo/Libya differ diff --git a/lib/python3.12/site-packages/tzdata/zoneinfo/MET b/lib/python3.12/site-packages/tzdata/zoneinfo/MET new file mode 100644 index 0000000000000000000000000000000000000000..31973271d2f87f7e9df4e8b0a1481f09a9e5407d Binary files /dev/null and b/lib/python3.12/site-packages/tzdata/zoneinfo/MET differ diff --git a/lib/python3.12/site-packages/tzdata/zoneinfo/MST b/lib/python3.12/site-packages/tzdata/zoneinfo/MST new file mode 100644 index 0000000000000000000000000000000000000000..c2bd2f949b248b835c98216b4dc66f9f6eb0265e Binary files /dev/null and b/lib/python3.12/site-packages/tzdata/zoneinfo/MST differ diff --git a/lib/python3.12/site-packages/tzdata/zoneinfo/MST7MDT b/lib/python3.12/site-packages/tzdata/zoneinfo/MST7MDT new file mode 100644 index 0000000000000000000000000000000000000000..09e54e5c7c5bb2384e37626d4b985cfad29ed29b Binary files /dev/null and b/lib/python3.12/site-packages/tzdata/zoneinfo/MST7MDT differ diff --git a/lib/python3.12/site-packages/tzdata/zoneinfo/NZ b/lib/python3.12/site-packages/tzdata/zoneinfo/NZ new file mode 100644 index 0000000000000000000000000000000000000000..afb3929318475d4f0ea9d6c9e94d0f5f81d8b82e Binary files /dev/null and b/lib/python3.12/site-packages/tzdata/zoneinfo/NZ differ diff --git a/lib/python3.12/site-packages/tzdata/zoneinfo/NZ-CHAT b/lib/python3.12/site-packages/tzdata/zoneinfo/NZ-CHAT new file mode 100644 index 0000000000000000000000000000000000000000..f06065ebd18315683f60cf87839d477a1d699f01 Binary files /dev/null and b/lib/python3.12/site-packages/tzdata/zoneinfo/NZ-CHAT differ diff --git a/lib/python3.12/site-packages/tzdata/zoneinfo/Navajo b/lib/python3.12/site-packages/tzdata/zoneinfo/Navajo new file mode 100644 index 0000000000000000000000000000000000000000..09e54e5c7c5bb2384e37626d4b985cfad29ed29b Binary files /dev/null and b/lib/python3.12/site-packages/tzdata/zoneinfo/Navajo differ diff --git a/lib/python3.12/site-packages/tzdata/zoneinfo/PRC b/lib/python3.12/site-packages/tzdata/zoneinfo/PRC new file mode 100644 index 0000000000000000000000000000000000000000..d6b66984a2f36ae36b35e174756707aa7286c292 Binary files /dev/null and b/lib/python3.12/site-packages/tzdata/zoneinfo/PRC differ diff --git a/lib/python3.12/site-packages/tzdata/zoneinfo/PST8PDT b/lib/python3.12/site-packages/tzdata/zoneinfo/PST8PDT new file mode 100644 index 0000000000000000000000000000000000000000..aaf07787ad92b65eadae63b64bba290f9f961507 Binary files /dev/null and b/lib/python3.12/site-packages/tzdata/zoneinfo/PST8PDT differ diff --git a/lib/python3.12/site-packages/tzdata/zoneinfo/Poland b/lib/python3.12/site-packages/tzdata/zoneinfo/Poland new file mode 100644 index 0000000000000000000000000000000000000000..efe1a40f2a8ffd499d22cd83e6b5df6c6c1e8e5c Binary files /dev/null and b/lib/python3.12/site-packages/tzdata/zoneinfo/Poland differ diff --git a/lib/python3.12/site-packages/tzdata/zoneinfo/Portugal b/lib/python3.12/site-packages/tzdata/zoneinfo/Portugal new file mode 100644 index 0000000000000000000000000000000000000000..7e9aae727b2b660e7f5e383121f445daf033a9c5 Binary files /dev/null and b/lib/python3.12/site-packages/tzdata/zoneinfo/Portugal differ diff --git a/lib/python3.12/site-packages/tzdata/zoneinfo/ROC b/lib/python3.12/site-packages/tzdata/zoneinfo/ROC new file mode 100644 index 0000000000000000000000000000000000000000..35d89d036d07c3f28dec64092ab1b533c21ae2bc Binary files /dev/null and b/lib/python3.12/site-packages/tzdata/zoneinfo/ROC differ diff --git a/lib/python3.12/site-packages/tzdata/zoneinfo/ROK b/lib/python3.12/site-packages/tzdata/zoneinfo/ROK new file mode 100644 index 0000000000000000000000000000000000000000..1755147fab44e07b7527ce1eaf3ae991473fb222 Binary files /dev/null and b/lib/python3.12/site-packages/tzdata/zoneinfo/ROK differ diff --git a/lib/python3.12/site-packages/tzdata/zoneinfo/Singapore b/lib/python3.12/site-packages/tzdata/zoneinfo/Singapore new file mode 100644 index 0000000000000000000000000000000000000000..dbbdea3c8149004cfd525a0fc26e5da72b20e8a1 Binary files /dev/null and b/lib/python3.12/site-packages/tzdata/zoneinfo/Singapore differ diff --git a/lib/python3.12/site-packages/tzdata/zoneinfo/Turkey b/lib/python3.12/site-packages/tzdata/zoneinfo/Turkey new file mode 100644 index 0000000000000000000000000000000000000000..c89186687300068ac4e8505cc0012a1dbf6a9960 Binary files /dev/null and b/lib/python3.12/site-packages/tzdata/zoneinfo/Turkey differ diff --git a/lib/python3.12/site-packages/tzdata/zoneinfo/UCT b/lib/python3.12/site-packages/tzdata/zoneinfo/UCT new file mode 100644 index 0000000000000000000000000000000000000000..00841a62213e6cccf7f0b7353e5e8ae214185486 Binary files /dev/null and b/lib/python3.12/site-packages/tzdata/zoneinfo/UCT differ diff --git a/lib/python3.12/site-packages/tzdata/zoneinfo/UTC b/lib/python3.12/site-packages/tzdata/zoneinfo/UTC new file mode 100644 index 0000000000000000000000000000000000000000..00841a62213e6cccf7f0b7353e5e8ae214185486 Binary files /dev/null and b/lib/python3.12/site-packages/tzdata/zoneinfo/UTC differ diff --git a/lib/python3.12/site-packages/tzdata/zoneinfo/Universal b/lib/python3.12/site-packages/tzdata/zoneinfo/Universal new file mode 100644 index 0000000000000000000000000000000000000000..00841a62213e6cccf7f0b7353e5e8ae214185486 Binary files /dev/null and b/lib/python3.12/site-packages/tzdata/zoneinfo/Universal differ diff --git a/lib/python3.12/site-packages/tzdata/zoneinfo/W-SU b/lib/python3.12/site-packages/tzdata/zoneinfo/W-SU new file mode 100644 index 0000000000000000000000000000000000000000..5e6b6de6451b4408fb71ef73950712a0827d49a6 Binary files /dev/null and b/lib/python3.12/site-packages/tzdata/zoneinfo/W-SU differ diff --git a/lib/python3.12/site-packages/tzdata/zoneinfo/WET b/lib/python3.12/site-packages/tzdata/zoneinfo/WET new file mode 100644 index 0000000000000000000000000000000000000000..7e9aae727b2b660e7f5e383121f445daf033a9c5 Binary files /dev/null and b/lib/python3.12/site-packages/tzdata/zoneinfo/WET differ diff --git a/lib/python3.12/site-packages/tzdata/zoneinfo/Zulu b/lib/python3.12/site-packages/tzdata/zoneinfo/Zulu new file mode 100644 index 0000000000000000000000000000000000000000..00841a62213e6cccf7f0b7353e5e8ae214185486 Binary files /dev/null and b/lib/python3.12/site-packages/tzdata/zoneinfo/Zulu differ diff --git a/lib/python3.12/site-packages/tzdata/zoneinfo/__init__.py b/lib/python3.12/site-packages/tzdata/zoneinfo/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/lib/python3.12/site-packages/tzdata/zoneinfo/iso3166.tab b/lib/python3.12/site-packages/tzdata/zoneinfo/iso3166.tab new file mode 100644 index 0000000000000000000000000000000000000000..4ae35234b9722a8b673823e67106479eae6d31a6 --- /dev/null +++ b/lib/python3.12/site-packages/tzdata/zoneinfo/iso3166.tab @@ -0,0 +1,279 @@ +# ISO 3166 alpha-2 country codes +# +# This file is in the public domain, so clarified as of +# 2009-05-17 by Arthur David Olson. +# +# From Paul Eggert (2025-07-01): +# This file contains a table of two-letter country codes. Columns are +# separated by a single tab. Lines beginning with ‘#’ are comments. +# All text uses UTF-8 encoding. The columns of the table are as follows: +# +# 1. ISO 3166-1 alpha-2 country code, current as of +# ISO/TC 46 N1127 (2024-02-29). See: ISO/TC 46 Documents +# https://www.iso.org/committee/48750.html?view=documents +# 2. The usual English name for the coded region. This sometimes +# departs from ISO-listed names, sometimes so that sorted subsets +# of names are useful (e.g., “Samoa (American)” and “Samoa +# (western)” rather than “American Samoa” and “Samoa”), +# sometimes to avoid confusion among non-experts (e.g., +# “Czech Republic” and “Turkey” rather than “Czechia” and “Türkiye”), +# and sometimes to omit needless detail or churn (e.g., “Netherlands” +# rather than “Netherlands (the)” or “Netherlands (Kingdom of the)”). +# +# The table is sorted by country code. +# +# This table is intended as an aid for users, to help them select time +# zone data appropriate for their practical needs. It is not intended +# to take or endorse any position on legal or territorial claims. +# +#country- +#code name of country, territory, area, or subdivision +AD Andorra +AE United Arab Emirates +AF Afghanistan +AG Antigua & Barbuda +AI Anguilla +AL Albania +AM Armenia +AO Angola +AQ Antarctica +AR Argentina +AS Samoa (American) +AT Austria +AU Australia +AW Aruba +AX Åland Islands +AZ Azerbaijan +BA Bosnia & Herzegovina +BB Barbados +BD Bangladesh +BE Belgium +BF Burkina Faso +BG Bulgaria +BH Bahrain +BI Burundi +BJ Benin +BL St Barthelemy +BM Bermuda +BN Brunei +BO Bolivia +BQ Caribbean NL +BR Brazil +BS Bahamas +BT Bhutan +BV Bouvet Island +BW Botswana +BY Belarus +BZ Belize +CA Canada +CC Cocos (Keeling) Islands +CD Congo (Dem. Rep.) +CF Central African Rep. +CG Congo (Rep.) +CH Switzerland +CI Côte d’Ivoire +CK Cook Islands +CL Chile +CM Cameroon +CN China +CO Colombia +CR Costa Rica +CU Cuba +CV Cape Verde +CW Curaçao +CX Christmas Island +CY Cyprus +CZ Czech Republic +DE Germany +DJ Djibouti +DK Denmark +DM Dominica +DO Dominican Republic +DZ Algeria +EC Ecuador +EE Estonia +EG Egypt +EH Western Sahara +ER Eritrea +ES Spain +ET Ethiopia +FI Finland +FJ Fiji +FK Falkland Islands +FM Micronesia +FO Faroe Islands +FR France +GA Gabon +GB Britain (UK) +GD Grenada +GE Georgia +GF French Guiana +GG Guernsey +GH Ghana +GI Gibraltar +GL Greenland +GM Gambia +GN Guinea +GP Guadeloupe +GQ Equatorial Guinea +GR Greece +GS South Georgia & the South Sandwich Islands +GT Guatemala +GU Guam +GW Guinea-Bissau +GY Guyana +HK Hong Kong +HM Heard Island & McDonald Islands +HN Honduras +HR Croatia +HT Haiti +HU Hungary +ID Indonesia +IE Ireland +IL Israel +IM Isle of Man +IN India +IO British Indian Ocean Territory +IQ Iraq +IR Iran +IS Iceland +IT Italy +JE Jersey +JM Jamaica +JO Jordan +JP Japan +KE Kenya +KG Kyrgyzstan +KH Cambodia +KI Kiribati +KM Comoros +KN St Kitts & Nevis +KP Korea (North) +KR Korea (South) +KW Kuwait +KY Cayman Islands +KZ Kazakhstan +LA Laos +LB Lebanon +LC St Lucia +LI Liechtenstein +LK Sri Lanka +LR Liberia +LS Lesotho +LT Lithuania +LU Luxembourg +LV Latvia +LY Libya +MA Morocco +MC Monaco +MD Moldova +ME Montenegro +MF St Martin (French) +MG Madagascar +MH Marshall Islands +MK North Macedonia +ML Mali +MM Myanmar (Burma) +MN Mongolia +MO Macau +MP Northern Mariana Islands +MQ Martinique +MR Mauritania +MS Montserrat +MT Malta +MU Mauritius +MV Maldives +MW Malawi +MX Mexico +MY Malaysia +MZ Mozambique +NA Namibia +NC New Caledonia +NE Niger +NF Norfolk Island +NG Nigeria +NI Nicaragua +NL Netherlands +NO Norway +NP Nepal +NR Nauru +NU Niue +NZ New Zealand +OM Oman +PA Panama +PE Peru +PF French Polynesia +PG Papua New Guinea +PH Philippines +PK Pakistan +PL Poland +PM St Pierre & Miquelon +PN Pitcairn +PR Puerto Rico +PS Palestine +PT Portugal +PW Palau +PY Paraguay +QA Qatar +RE Réunion +RO Romania +RS Serbia +RU Russia +RW Rwanda +SA Saudi Arabia +SB Solomon Islands +SC Seychelles +SD Sudan +SE Sweden +SG Singapore +SH St Helena +SI Slovenia +SJ Svalbard & Jan Mayen +SK Slovakia +SL Sierra Leone +SM San Marino +SN Senegal +SO Somalia +SR Suriname +SS South Sudan +ST Sao Tome & Principe +SV El Salvador +SX St Maarten (Dutch) +SY Syria +SZ Eswatini (Swaziland) +TC Turks & Caicos Is +TD Chad +TF French S. Terr. +TG Togo +TH Thailand +TJ Tajikistan +TK Tokelau +TL East Timor +TM Turkmenistan +TN Tunisia +TO Tonga +TR Turkey +TT Trinidad & Tobago +TV Tuvalu +TW Taiwan +TZ Tanzania +UA Ukraine +UG Uganda +UM US minor outlying islands +US United States +UY Uruguay +UZ Uzbekistan +VA Vatican City +VC St Vincent +VE Venezuela +VG Virgin Islands (UK) +VI Virgin Islands (US) +VN Vietnam +VU Vanuatu +WF Wallis & Futuna +WS Samoa (western) +YE Yemen +YT Mayotte +ZA South Africa +ZM Zambia +ZW Zimbabwe diff --git a/lib/python3.12/site-packages/tzdata/zoneinfo/leapseconds b/lib/python3.12/site-packages/tzdata/zoneinfo/leapseconds new file mode 100644 index 0000000000000000000000000000000000000000..2a42c83efe16c3c8da4d750df0a82be4908c8896 --- /dev/null +++ b/lib/python3.12/site-packages/tzdata/zoneinfo/leapseconds @@ -0,0 +1,86 @@ +# Allowance for leap seconds added to each time zone file. + +# This file is in the public domain. + +# This file is generated automatically from the data in the public-domain +# NIST/IERS format leap-seconds.list file, which can be copied from +# +# or via a less-secure protocol and with different comments and +# less volatile last-modified and expiration timestamps, from +# . +# For more about leap-seconds.list, please see +# The NTP Timescale and Leap Seconds +# . + +# The rules for leap seconds are specified in Annex 1 (Time scales) of: +# Standard-frequency and time-signal emissions. +# International Telecommunication Union - Radiocommunication Sector +# (ITU-R) Recommendation TF.460-6 (02/2002) +# . +# The International Earth Rotation and Reference Systems Service (IERS) +# periodically uses leap seconds to keep UTC to within 0.9 s of UT1 +# (a proxy for Earth's angle in space as measured by astronomers) +# and publishes leap second data in a copyrighted file +# . +# See: Levine J. Coordinated Universal Time and the leap second. +# URSI Radio Sci Bull. 2016;89(4):30-6. doi:10.23919/URSIRSB.2016.7909995 +# . + +# There were no leap seconds before 1972, as no official mechanism +# accounted for the discrepancy between atomic time (TAI) and the earth's +# rotation. The first ("1 Jan 1972") data line in leap-seconds.list +# does not denote a leap second; it denotes the start of the current definition +# of UTC. + +# All leap-seconds are Stationary (S) at the given UTC time. +# The correction (+ or -) is made at the given time, so in the unlikely +# event of a negative leap second, a line would look like this: +# Leap YEAR MON DAY 23:59:59 - S +# Typical lines look like this: +# Leap YEAR MON DAY 23:59:60 + S +Leap 1972 Jun 30 23:59:60 + S +Leap 1972 Dec 31 23:59:60 + S +Leap 1973 Dec 31 23:59:60 + S +Leap 1974 Dec 31 23:59:60 + S +Leap 1975 Dec 31 23:59:60 + S +Leap 1976 Dec 31 23:59:60 + S +Leap 1977 Dec 31 23:59:60 + S +Leap 1978 Dec 31 23:59:60 + S +Leap 1979 Dec 31 23:59:60 + S +Leap 1981 Jun 30 23:59:60 + S +Leap 1982 Jun 30 23:59:60 + S +Leap 1983 Jun 30 23:59:60 + S +Leap 1985 Jun 30 23:59:60 + S +Leap 1987 Dec 31 23:59:60 + S +Leap 1989 Dec 31 23:59:60 + S +Leap 1990 Dec 31 23:59:60 + S +Leap 1992 Jun 30 23:59:60 + S +Leap 1993 Jun 30 23:59:60 + S +Leap 1994 Jun 30 23:59:60 + S +Leap 1995 Dec 31 23:59:60 + S +Leap 1997 Jun 30 23:59:60 + S +Leap 1998 Dec 31 23:59:60 + S +Leap 2005 Dec 31 23:59:60 + S +Leap 2008 Dec 31 23:59:60 + S +Leap 2012 Jun 30 23:59:60 + S +Leap 2015 Jun 30 23:59:60 + S +Leap 2016 Dec 31 23:59:60 + S + +# UTC timestamp when this leap second list expires. +# Any additional leap seconds will come after this. +# This Expires line is commented out for now, +# so that pre-2020a zic implementations do not reject this file. +#Expires 2026 Jun 28 00:00:00 + +# Here are POSIX timestamps for the data in this file. +# "#updated" gives the last time the leap seconds data changed +# or, if this file was derived from the IERS leap-seconds.list, +# the last time that file changed in any way. +# "#expires" gives the first time this file might be wrong; +# if this file was derived from the IERS leap-seconds.list, +# this is typically a bit less than one year after "updated". +#updated 1751846400 (2025-07-07 00:00:00 UTC) +#expires 1782604800 (2026-06-28 00:00:00 UTC) + +# Updated through IERS Bulletin C (https://hpiers.obspm.fr/iers/bul/bulc/bulletinc.dat) +# File expires on 28 June 2026 diff --git a/lib/python3.12/site-packages/tzdata/zoneinfo/tzdata.zi b/lib/python3.12/site-packages/tzdata/zoneinfo/tzdata.zi new file mode 100644 index 0000000000000000000000000000000000000000..c56f67c02f6d2ef01b80e7fc568fcdd7d53e69cd --- /dev/null +++ b/lib/python3.12/site-packages/tzdata/zoneinfo/tzdata.zi @@ -0,0 +1,4298 @@ +# version 2025c +# This zic input file is in the public domain. +R d 1916 o - Jun 14 23s 1 S +R d 1916 1919 - O Su>=1 23s 0 - +R d 1917 o - Mar 24 23s 1 S +R d 1918 o - Mar 9 23s 1 S +R d 1919 o - Mar 1 23s 1 S +R d 1920 o - F 14 23s 1 S +R d 1920 o - O 23 23s 0 - +R d 1921 o - Mar 14 23s 1 S +R d 1921 o - Jun 21 23s 0 - +R d 1939 o - S 11 23s 1 S +R d 1939 o - N 19 1 0 - +R d 1944 1945 - Ap M>=1 2 1 S +R d 1944 o - O 8 2 0 - +R d 1945 o - S 16 1 0 - +R d 1971 o - Ap 25 23s 1 S +R d 1971 o - S 26 23s 0 - +R d 1977 o - May 6 0 1 S +R d 1977 o - O 21 0 0 - +R d 1978 o - Mar 24 1 1 S +R d 1978 o - S 22 3 0 - +R d 1980 o - Ap 25 0 1 S +R d 1980 o - O 31 2 0 - +R K 1940 o - Jul 15 0 1 S +R K 1940 o - O 1 0 0 - +R K 1941 o - Ap 15 0 1 S +R K 1941 o - S 16 0 0 - +R K 1942 1944 - Ap 1 0 1 S +R K 1942 o - O 27 0 0 - +R K 1943 1945 - N 1 0 0 - +R K 1945 o - Ap 16 0 1 S +R K 1957 o - May 10 0 1 S +R K 1957 1958 - O 1 0 0 - +R K 1958 o - May 1 0 1 S +R K 1959 1981 - May 1 1 1 S +R K 1959 1965 - S 30 3 0 - +R K 1966 1994 - O 1 3 0 - +R K 1982 o - Jul 25 1 1 S +R K 1983 o - Jul 12 1 1 S +R K 1984 1988 - May 1 1 1 S +R K 1989 o - May 6 1 1 S +R K 1990 1994 - May 1 1 1 S +R K 1995 2010 - Ap lastF 0s 1 S +R K 1995 2005 - S lastTh 24 0 - +R K 2006 o - S 21 24 0 - +R K 2007 o - S Th>=1 24 0 - +R K 2008 o - Au lastTh 24 0 - +R K 2009 o - Au 20 24 0 - +R K 2010 o - Au 10 24 0 - +R K 2010 o - S 9 24 1 S +R K 2010 o - S lastTh 24 0 - +R K 2014 o - May 15 24 1 S +R K 2014 o - Jun 26 24 0 - +R K 2014 o - Jul 31 24 1 S +R K 2014 o - S lastTh 24 0 - +R K 2023 ma - Ap lastF 0 1 S +R K 2023 ma - O lastTh 24 0 - +R L 1951 o - O 14 2 1 S +R L 1952 o - Ja 1 0 0 - +R L 1953 o - O 9 2 1 S +R L 1954 o - Ja 1 0 0 - +R L 1955 o - S 30 0 1 S +R L 1956 o - Ja 1 0 0 - +R L 1982 1984 - Ap 1 0 1 S +R L 1982 1985 - O 1 0 0 - +R L 1985 o - Ap 6 0 1 S +R L 1986 o - Ap 4 0 1 S +R L 1986 o - O 3 0 0 - +R L 1987 1989 - Ap 1 0 1 S +R L 1987 1989 - O 1 0 0 - +R L 1997 o - Ap 4 0 1 S +R L 1997 o - O 4 0 0 - +R L 2013 o - Mar lastF 1 1 S +R L 2013 o - O lastF 2 0 - +R MU 1982 o - O 10 0 1 - +R MU 1983 o - Mar 21 0 0 - +R MU 2008 o - O lastSu 2 1 - +R MU 2009 o - Mar lastSu 2 0 - +R M 1939 o - S 12 0 1 - +R M 1939 o - N 19 0 0 - +R M 1940 o - F 25 0 1 - +R M 1945 o - N 18 0 0 - +R M 1950 o - Jun 11 0 1 - +R M 1950 o - O 29 0 0 - +R M 1967 o - Jun 3 12 1 - +R M 1967 o - O 1 0 0 - +R M 1974 o - Jun 24 0 1 - +R M 1974 o - S 1 0 0 - +R M 1976 1977 - May 1 0 1 - +R M 1976 o - Au 1 0 0 - +R M 1977 o - S 28 0 0 - +R M 1978 o - Jun 1 0 1 - +R M 1978 o - Au 4 0 0 - +R M 2008 o - Jun 1 0 1 - +R M 2008 o - S 1 0 0 - +R M 2009 o - Jun 1 0 1 - +R M 2009 o - Au 21 0 0 - +R M 2010 o - May 2 0 1 - +R M 2010 o - Au 8 0 0 - +R M 2011 o - Ap 3 0 1 - +R M 2011 o - Jul 31 0 0 - +R M 2012 2013 - Ap lastSu 2 1 - +R M 2012 o - Jul 20 3 0 - +R M 2012 o - Au 20 2 1 - +R M 2012 o - S 30 3 0 - +R M 2013 o - Jul 7 3 0 - +R M 2013 o - Au 10 2 1 - +R M 2013 2018 - O lastSu 3 0 - +R M 2014 2018 - Mar lastSu 2 1 - +R M 2014 o - Jun 28 3 0 - +R M 2014 o - Au 2 2 1 - +R M 2015 o - Jun 14 3 0 - +R M 2015 o - Jul 19 2 1 - +R M 2016 o - Jun 5 3 0 - +R M 2016 o - Jul 10 2 1 - +R M 2017 o - May 21 3 0 - +R M 2017 o - Jul 2 2 1 - +R M 2018 o - May 13 3 0 - +R M 2018 o - Jun 17 2 1 - +R M 2019 o - May 5 3 -1 - +R M 2019 o - Jun 9 2 0 - +R M 2020 o - Ap 19 3 -1 - +R M 2020 o - May 31 2 0 - +R M 2021 o - Ap 11 3 -1 - +R M 2021 o - May 16 2 0 - +R M 2022 o - Mar 27 3 -1 - +R M 2022 o - May 8 2 0 - +R M 2023 o - Mar 19 3 -1 - +R M 2023 o - Ap 23 2 0 - +R M 2024 o - Mar 10 3 -1 - +R M 2024 o - Ap 14 2 0 - +R M 2025 o - F 23 3 -1 - +R M 2025 o - Ap 6 2 0 - +R M 2026 o - F 15 3 -1 - +R M 2026 o - Mar 22 2 0 - +R M 2027 o - F 7 3 -1 - +R M 2027 o - Mar 14 2 0 - +R M 2028 o - Ja 23 3 -1 - +R M 2028 o - Mar 5 2 0 - +R M 2029 o - Ja 14 3 -1 - +R M 2029 o - F 18 2 0 - +R M 2029 o - D 30 3 -1 - +R M 2030 o - F 10 2 0 - +R M 2030 o - D 22 3 -1 - +R M 2031 o - Ja 26 2 0 - +R M 2031 o - D 14 3 -1 - +R M 2032 o - Ja 18 2 0 - +R M 2032 o - N 28 3 -1 - +R M 2033 o - Ja 9 2 0 - +R M 2033 o - N 20 3 -1 - +R M 2033 o - D 25 2 0 - +R M 2034 o - N 5 3 -1 - +R M 2034 o - D 17 2 0 - +R M 2035 o - O 28 3 -1 - +R M 2035 o - D 9 2 0 - +R M 2036 o - O 19 3 -1 - +R M 2036 o - N 23 2 0 - +R M 2037 o - O 4 3 -1 - +R M 2037 o - N 15 2 0 - +R M 2038 o - S 26 3 -1 - +R M 2038 o - O 31 2 0 - +R M 2039 o - S 18 3 -1 - +R M 2039 o - O 23 2 0 - +R M 2040 o - S 2 3 -1 - +R M 2040 o - O 14 2 0 - +R M 2041 o - Au 25 3 -1 - +R M 2041 o - S 29 2 0 - +R M 2042 o - Au 10 3 -1 - +R M 2042 o - S 21 2 0 - +R M 2043 o - Au 2 3 -1 - +R M 2043 o - S 13 2 0 - +R M 2044 o - Jul 24 3 -1 - +R M 2044 o - Au 28 2 0 - +R M 2045 o - Jul 9 3 -1 - +R M 2045 o - Au 20 2 0 - +R M 2046 o - Jul 1 3 -1 - +R M 2046 o - Au 5 2 0 - +R M 2047 o - Jun 23 3 -1 - +R M 2047 o - Jul 28 2 0 - +R M 2048 o - Jun 7 3 -1 - +R M 2048 o - Jul 19 2 0 - +R M 2049 o - May 30 3 -1 - +R M 2049 o - Jul 4 2 0 - +R M 2050 o - May 15 3 -1 - +R M 2050 o - Jun 26 2 0 - +R M 2051 o - May 7 3 -1 - +R M 2051 o - Jun 18 2 0 - +R M 2052 o - Ap 28 3 -1 - +R M 2052 o - Jun 2 2 0 - +R M 2053 o - Ap 13 3 -1 - +R M 2053 o - May 25 2 0 - +R M 2054 o - Ap 5 3 -1 - +R M 2054 o - May 10 2 0 - +R M 2055 o - Mar 28 3 -1 - +R M 2055 o - May 2 2 0 - +R M 2056 o - Mar 12 3 -1 - +R M 2056 o - Ap 23 2 0 - +R M 2057 o - Mar 4 3 -1 - +R M 2057 o - Ap 8 2 0 - +R M 2058 o - F 17 3 -1 - +R M 2058 o - Mar 31 2 0 - +R M 2059 o - F 9 3 -1 - +R M 2059 o - Mar 23 2 0 - +R M 2060 o - F 1 3 -1 - +R M 2060 o - Mar 7 2 0 - +R M 2061 o - Ja 16 3 -1 - +R M 2061 o - F 27 2 0 - +R M 2062 o - Ja 8 3 -1 - +R M 2062 o - F 12 2 0 - +R M 2062 o - D 31 3 -1 - +R M 2063 o - F 4 2 0 - +R M 2063 o - D 16 3 -1 - +R M 2064 o - Ja 27 2 0 - +R M 2064 o - D 7 3 -1 - +R M 2065 o - Ja 11 2 0 - +R M 2065 o - N 22 3 -1 - +R M 2066 o - Ja 3 2 0 - +R M 2066 o - N 14 3 -1 - +R M 2066 o - D 26 2 0 - +R M 2067 o - N 6 3 -1 - +R M 2067 o - D 11 2 0 - +R M 2068 o - O 21 3 -1 - +R M 2068 o - D 2 2 0 - +R M 2069 o - O 13 3 -1 - +R M 2069 o - N 17 2 0 - +R M 2070 o - O 5 3 -1 - +R M 2070 o - N 9 2 0 - +R M 2071 o - S 20 3 -1 - +R M 2071 o - N 1 2 0 - +R M 2072 o - S 11 3 -1 - +R M 2072 o - O 16 2 0 - +R M 2073 o - Au 27 3 -1 - +R M 2073 o - O 8 2 0 - +R M 2074 o - Au 19 3 -1 - +R M 2074 o - S 30 2 0 - +R M 2075 o - Au 11 3 -1 - +R M 2075 o - S 15 2 0 - +R M 2076 o - Jul 26 3 -1 - +R M 2076 o - S 6 2 0 - +R M 2077 o - Jul 18 3 -1 - +R M 2077 o - Au 22 2 0 - +R M 2078 o - Jul 10 3 -1 - +R M 2078 o - Au 14 2 0 - +R M 2079 o - Jun 25 3 -1 - +R M 2079 o - Au 6 2 0 - +R M 2080 o - Jun 16 3 -1 - +R M 2080 o - Jul 21 2 0 - +R M 2081 o - Jun 1 3 -1 - +R M 2081 o - Jul 13 2 0 - +R M 2082 o - May 24 3 -1 - +R M 2082 o - Jun 28 2 0 - +R M 2083 o - May 16 3 -1 - +R M 2083 o - Jun 20 2 0 - +R M 2084 o - Ap 30 3 -1 - +R M 2084 o - Jun 11 2 0 - +R M 2085 o - Ap 22 3 -1 - +R M 2085 o - May 27 2 0 - +R M 2086 o - Ap 14 3 -1 - +R M 2086 o - May 19 2 0 - +R M 2087 o - Mar 30 3 -1 - +R M 2087 o - May 11 2 0 - +R NA 1994 o - Mar 21 0 -1 WAT +R NA 1994 2017 - S Su>=1 2 0 CAT +R NA 1995 2017 - Ap Su>=1 2 -1 WAT +R SA 1942 1943 - S Su>=15 2 1 - +R SA 1943 1944 - Mar Su>=15 2 0 - +R SD 1970 o - May 1 0 1 S +R SD 1970 1985 - O 15 0 0 - +R SD 1971 o - Ap 30 0 1 S +R SD 1972 1985 - Ap lastSu 0 1 S +R n 1939 o - Ap 15 23s 1 S +R n 1939 o - N 18 23s 0 - +R n 1940 o - F 25 23s 1 S +R n 1941 o - O 6 0 0 - +R n 1942 o - Mar 9 0 1 S +R n 1942 o - N 2 3 0 - +R n 1943 o - Mar 29 2 1 S +R n 1943 o - Ap 17 2 0 - +R n 1943 o - Ap 25 2 1 S +R n 1943 o - O 4 2 0 - +R n 1944 1945 - Ap M>=1 2 1 S +R n 1944 o - O 8 0 0 - +R n 1945 o - S 16 0 0 - +R n 1977 o - Ap 30 0s 1 S +R n 1977 o - S 24 0s 0 - +R n 1978 o - May 1 0s 1 S +R n 1978 o - O 1 0s 0 - +R n 1988 o - Jun 1 0s 1 S +R n 1988 1990 - S lastSu 0s 0 - +R n 1989 o - Mar 26 0s 1 S +R n 1990 o - May 1 0s 1 S +R n 2005 o - May 1 0s 1 S +R n 2005 o - S 30 1s 0 - +R n 2006 2008 - Mar lastSu 2s 1 S +R n 2006 2008 - O lastSu 2s 0 - +R Tr 2005 ma - Mar lastSu 1u 2 +02 +R Tr 2004 ma - O lastSu 1u 0 +00 +R AM 2011 o - Mar lastSu 2s 1 - +R AM 2011 o - O lastSu 2s 0 - +R AZ 1997 2015 - Mar lastSu 4 1 - +R AZ 1997 2015 - O lastSu 5 0 - +R BD 2009 o - Jun 19 23 1 - +R BD 2009 o - D 31 24 0 - +R Sh 1919 o - Ap 12 24 1 D +R Sh 1919 o - S 30 24 0 S +R Sh 1940 o - Jun 1 0 1 D +R Sh 1940 o - O 12 24 0 S +R Sh 1941 o - Mar 15 0 1 D +R Sh 1941 o - N 1 24 0 S +R Sh 1942 o - Ja 31 0 1 D +R Sh 1945 o - S 1 24 0 S +R Sh 1946 o - May 15 0 1 D +R Sh 1946 o - S 30 24 0 S +R Sh 1947 o - Ap 15 0 1 D +R Sh 1947 o - O 31 24 0 S +R Sh 1948 1949 - May 1 0 1 D +R Sh 1948 1949 - S 30 24 0 S +R CN 1986 o - May 4 2 1 D +R CN 1986 1991 - S Su>=11 2 0 S +R CN 1987 1991 - Ap Su>=11 2 1 D +R HK 1946 o - Ap 21 0 1 S +R HK 1946 o - D 1 3:30s 0 - +R HK 1947 o - Ap 13 3:30s 1 S +R HK 1947 o - N 30 3:30s 0 - +R HK 1948 o - May 2 3:30s 1 S +R HK 1948 1952 - O Su>=28 3:30s 0 - +R HK 1949 1953 - Ap Su>=1 3:30 1 S +R HK 1953 1964 - O Su>=31 3:30 0 - +R HK 1954 1964 - Mar Su>=18 3:30 1 S +R HK 1965 1976 - Ap Su>=16 3:30 1 S +R HK 1965 1976 - O Su>=16 3:30 0 - +R HK 1973 o - D 30 3:30 1 S +R HK 1979 o - May 13 3:30 1 S +R HK 1979 o - O 21 3:30 0 - +R f 1946 o - May 15 0 1 D +R f 1946 o - O 1 0 0 S +R f 1947 o - Ap 15 0 1 D +R f 1947 o - N 1 0 0 S +R f 1948 1951 - May 1 0 1 D +R f 1948 1951 - O 1 0 0 S +R f 1952 o - Mar 1 0 1 D +R f 1952 1954 - N 1 0 0 S +R f 1953 1959 - Ap 1 0 1 D +R f 1955 1961 - O 1 0 0 S +R f 1960 1961 - Jun 1 0 1 D +R f 1974 1975 - Ap 1 0 1 D +R f 1974 1975 - O 1 0 0 S +R f 1979 o - Jul 1 0 1 D +R f 1979 o - O 1 0 0 S +R _ 1942 1943 - Ap 30 23 1 - +R _ 1942 o - N 17 23 0 - +R _ 1943 o - S 30 23 0 S +R _ 1946 o - Ap 30 23s 1 D +R _ 1946 o - S 30 23s 0 S +R _ 1947 o - Ap 19 23s 1 D +R _ 1947 o - N 30 23s 0 S +R _ 1948 o - May 2 23s 1 D +R _ 1948 o - O 31 23s 0 S +R _ 1949 1950 - Ap Sa>=1 23s 1 D +R _ 1949 1950 - O lastSa 23s 0 S +R _ 1951 o - Mar 31 23s 1 D +R _ 1951 o - O 28 23s 0 S +R _ 1952 1953 - Ap Sa>=1 23s 1 D +R _ 1952 o - N 1 23s 0 S +R _ 1953 1954 - O lastSa 23s 0 S +R _ 1954 1956 - Mar Sa>=17 23s 1 D +R _ 1955 o - N 5 23s 0 S +R _ 1956 1964 - N Su>=1 3:30 0 S +R _ 1957 1964 - Mar Su>=18 3:30 1 D +R _ 1965 1973 - Ap Su>=16 3:30 1 D +R _ 1965 1966 - O Su>=16 2:30 0 S +R _ 1967 1976 - O Su>=16 3:30 0 S +R _ 1973 o - D 30 3:30 1 D +R _ 1975 1976 - Ap Su>=16 3:30 1 D +R _ 1979 o - May 13 3:30 1 D +R _ 1979 o - O Su>=16 3:30 0 S +R CY 1975 o - Ap 13 0 1 S +R CY 1975 o - O 12 0 0 - +R CY 1976 o - May 15 0 1 S +R CY 1976 o - O 11 0 0 - +R CY 1977 1980 - Ap Su>=1 0 1 S +R CY 1977 o - S 25 0 0 - +R CY 1978 o - O 2 0 0 - +R CY 1979 1997 - S lastSu 0 0 - +R CY 1981 1998 - Mar lastSu 0 1 S +R i 1910 o - Ja 1 0 0 - +R i 1977 o - Mar 21 23 1 - +R i 1977 o - O 20 24 0 - +R i 1978 o - Mar 24 24 1 - +R i 1978 o - Au 5 1 0 - +R i 1979 o - May 26 24 1 - +R i 1979 o - S 18 24 0 - +R i 1980 o - Mar 20 24 1 - +R i 1980 o - S 22 24 0 - +R i 1991 o - May 2 24 1 - +R i 1992 1995 - Mar 21 24 1 - +R i 1991 1995 - S 21 24 0 - +R i 1996 o - Mar 20 24 1 - +R i 1996 o - S 20 24 0 - +R i 1997 1999 - Mar 21 24 1 - +R i 1997 1999 - S 21 24 0 - +R i 2000 o - Mar 20 24 1 - +R i 2000 o - S 20 24 0 - +R i 2001 2003 - Mar 21 24 1 - +R i 2001 2003 - S 21 24 0 - +R i 2004 o - Mar 20 24 1 - +R i 2004 o - S 20 24 0 - +R i 2005 o - Mar 21 24 1 - +R i 2005 o - S 21 24 0 - +R i 2008 o - Mar 20 24 1 - +R i 2008 o - S 20 24 0 - +R i 2009 2011 - Mar 21 24 1 - +R i 2009 2011 - S 21 24 0 - +R i 2012 o - Mar 20 24 1 - +R i 2012 o - S 20 24 0 - +R i 2013 2015 - Mar 21 24 1 - +R i 2013 2015 - S 21 24 0 - +R i 2016 o - Mar 20 24 1 - +R i 2016 o - S 20 24 0 - +R i 2017 2019 - Mar 21 24 1 - +R i 2017 2019 - S 21 24 0 - +R i 2020 o - Mar 20 24 1 - +R i 2020 o - S 20 24 0 - +R i 2021 2022 - Mar 21 24 1 - +R i 2021 2022 - S 21 24 0 - +R IQ 1982 o - May 1 0 1 - +R IQ 1982 1984 - O 1 0 0 - +R IQ 1983 o - Mar 31 0 1 - +R IQ 1984 1985 - Ap 1 0 1 - +R IQ 1985 1990 - S lastSu 1s 0 - +R IQ 1986 1990 - Mar lastSu 1s 1 - +R IQ 1991 2007 - Ap 1 3s 1 - +R IQ 1991 2007 - O 1 3s 0 - +R Z 1940 o - May 31 24u 1 D +R Z 1940 o - S 30 24u 0 S +R Z 1940 o - N 16 24u 1 D +R Z 1942 1946 - O 31 24u 0 S +R Z 1943 1944 - Mar 31 24u 1 D +R Z 1945 1946 - Ap 15 24u 1 D +R Z 1948 o - May 22 24u 2 DD +R Z 1948 o - Au 31 24u 1 D +R Z 1948 1949 - O 31 24u 0 S +R Z 1949 o - Ap 30 24u 1 D +R Z 1950 o - Ap 15 24u 1 D +R Z 1950 o - S 14 24u 0 S +R Z 1951 o - Mar 31 24u 1 D +R Z 1951 o - N 10 24u 0 S +R Z 1952 o - Ap 19 24u 1 D +R Z 1952 o - O 18 24u 0 S +R Z 1953 o - Ap 11 24u 1 D +R Z 1953 o - S 12 24u 0 S +R Z 1954 o - Jun 12 24u 1 D +R Z 1954 o - S 11 24u 0 S +R Z 1955 o - Jun 11 24u 1 D +R Z 1955 o - S 10 24u 0 S +R Z 1956 o - Jun 2 24u 1 D +R Z 1956 o - S 29 24u 0 S +R Z 1957 o - Ap 27 24u 1 D +R Z 1957 o - S 21 24u 0 S +R Z 1974 o - Jul 6 24 1 D +R Z 1974 o - O 12 24 0 S +R Z 1975 o - Ap 19 24 1 D +R Z 1975 o - Au 30 24 0 S +R Z 1980 o - Au 2 24s 1 D +R Z 1980 o - S 13 24s 0 S +R Z 1984 o - May 5 24s 1 D +R Z 1984 o - Au 25 24s 0 S +R Z 1985 o - Ap 13 24 1 D +R Z 1985 o - Au 31 24 0 S +R Z 1986 o - May 17 24 1 D +R Z 1986 o - S 6 24 0 S +R Z 1987 o - Ap 14 24 1 D +R Z 1987 o - S 12 24 0 S +R Z 1988 o - Ap 9 24 1 D +R Z 1988 o - S 3 24 0 S +R Z 1989 o - Ap 29 24 1 D +R Z 1989 o - S 2 24 0 S +R Z 1990 o - Mar 24 24 1 D +R Z 1990 o - Au 25 24 0 S +R Z 1991 o - Mar 23 24 1 D +R Z 1991 o - Au 31 24 0 S +R Z 1992 o - Mar 28 24 1 D +R Z 1992 o - S 5 24 0 S +R Z 1993 o - Ap 2 0 1 D +R Z 1993 o - S 5 0 0 S +R Z 1994 o - Ap 1 0 1 D +R Z 1994 o - Au 28 0 0 S +R Z 1995 o - Mar 31 0 1 D +R Z 1995 o - S 3 0 0 S +R Z 1996 o - Mar 14 24 1 D +R Z 1996 o - S 15 24 0 S +R Z 1997 o - Mar 20 24 1 D +R Z 1997 o - S 13 24 0 S +R Z 1998 o - Mar 20 0 1 D +R Z 1998 o - S 6 0 0 S +R Z 1999 o - Ap 2 2 1 D +R Z 1999 o - S 3 2 0 S +R Z 2000 o - Ap 14 2 1 D +R Z 2000 o - O 6 1 0 S +R Z 2001 o - Ap 9 1 1 D +R Z 2001 o - S 24 1 0 S +R Z 2002 o - Mar 29 1 1 D +R Z 2002 o - O 7 1 0 S +R Z 2003 o - Mar 28 1 1 D +R Z 2003 o - O 3 1 0 S +R Z 2004 o - Ap 7 1 1 D +R Z 2004 o - S 22 1 0 S +R Z 2005 2012 - Ap F<=1 2 1 D +R Z 2005 o - O 9 2 0 S +R Z 2006 o - O 1 2 0 S +R Z 2007 o - S 16 2 0 S +R Z 2008 o - O 5 2 0 S +R Z 2009 o - S 27 2 0 S +R Z 2010 o - S 12 2 0 S +R Z 2011 o - O 2 2 0 S +R Z 2012 o - S 23 2 0 S +R Z 2013 ma - Mar F>=23 2 1 D +R Z 2013 ma - O lastSu 2 0 S +R JP 1948 o - May Sa>=1 24 1 D +R JP 1948 1951 - S Sa>=8 25 0 S +R JP 1949 o - Ap Sa>=1 24 1 D +R JP 1950 1951 - May Sa>=1 24 1 D +R J 1973 o - Jun 6 0 1 S +R J 1973 1975 - O 1 0 0 - +R J 1974 1977 - May 1 0 1 S +R J 1976 o - N 1 0 0 - +R J 1977 o - O 1 0 0 - +R J 1978 o - Ap 30 0 1 S +R J 1978 o - S 30 0 0 - +R J 1985 o - Ap 1 0 1 S +R J 1985 o - O 1 0 0 - +R J 1986 1988 - Ap F>=1 0 1 S +R J 1986 1990 - O F>=1 0 0 - +R J 1989 o - May 8 0 1 S +R J 1990 o - Ap 27 0 1 S +R J 1991 o - Ap 17 0 1 S +R J 1991 o - S 27 0 0 - +R J 1992 o - Ap 10 0 1 S +R J 1992 1993 - O F>=1 0 0 - +R J 1993 1998 - Ap F>=1 0 1 S +R J 1994 o - S F>=15 0 0 - +R J 1995 1998 - S F>=15 0s 0 - +R J 1999 o - Jul 1 0s 1 S +R J 1999 2002 - S lastF 0s 0 - +R J 2000 2001 - Mar lastTh 0s 1 S +R J 2002 2012 - Mar lastTh 24 1 S +R J 2003 o - O 24 0s 0 - +R J 2004 o - O 15 0s 0 - +R J 2005 o - S lastF 0s 0 - +R J 2006 2011 - O lastF 0s 0 - +R J 2013 o - D 20 0 0 - +R J 2014 2021 - Mar lastTh 24 1 S +R J 2014 2022 - O lastF 0s 0 - +R J 2022 o - F lastTh 24 1 S +R KG 1992 1996 - Ap Su>=7 0s 1 - +R KG 1992 1996 - S lastSu 0 0 - +R KG 1997 2005 - Mar lastSu 2:30 1 - +R KG 1997 2004 - O lastSu 2:30 0 - +R KR 1948 o - Jun 1 0 1 D +R KR 1948 o - S 12 24 0 S +R KR 1949 o - Ap 3 0 1 D +R KR 1949 1951 - S Sa>=7 24 0 S +R KR 1950 o - Ap 1 0 1 D +R KR 1951 o - May 6 0 1 D +R KR 1955 o - May 5 0 1 D +R KR 1955 o - S 8 24 0 S +R KR 1956 o - May 20 0 1 D +R KR 1956 o - S 29 24 0 S +R KR 1957 1960 - May Su>=1 0 1 D +R KR 1957 1960 - S Sa>=17 24 0 S +R KR 1987 1988 - May Su>=8 2 1 D +R KR 1987 1988 - O Su>=8 3 0 S +R l 1920 o - Mar 28 0 1 S +R l 1920 o - O 25 0 0 - +R l 1921 o - Ap 3 0 1 S +R l 1921 o - O 3 0 0 - +R l 1922 o - Mar 26 0 1 S +R l 1922 o - O 8 0 0 - +R l 1923 o - Ap 22 0 1 S +R l 1923 o - S 16 0 0 - +R l 1957 1961 - May 1 0 1 S +R l 1957 1961 - O 1 0 0 - +R l 1972 o - Jun 22 0 1 S +R l 1972 1977 - O 1 0 0 - +R l 1973 1977 - May 1 0 1 S +R l 1978 o - Ap 30 0 1 S +R l 1978 o - S 30 0 0 - +R l 1984 1987 - May 1 0 1 S +R l 1984 1991 - O 16 0 0 - +R l 1988 o - Jun 1 0 1 S +R l 1989 o - May 10 0 1 S +R l 1990 1992 - May 1 0 1 S +R l 1992 o - O 4 0 0 - +R l 1993 ma - Mar lastSu 0 1 S +R l 1993 1998 - S lastSu 0 0 - +R l 1999 ma - O lastSu 0 0 - +R NB 1935 1941 - S 14 0 0:20 - +R NB 1935 1941 - D 14 0 0 - +R X 1983 1984 - Ap 1 0 1 - +R X 1983 o - O 1 0 0 - +R X 1985 1998 - Mar lastSu 0 1 - +R X 1984 1998 - S lastSu 0 0 - +R X 2001 o - Ap lastSa 2 1 - +R X 2001 2006 - S lastSa 2 0 - +R X 2002 2006 - Mar lastSa 2 1 - +R X 2015 2016 - Mar lastSa 2 1 - +R X 2015 2016 - S lastSa 0 0 - +R PK 2002 o - Ap Su>=2 0 1 S +R PK 2002 o - O Su>=2 0 0 - +R PK 2008 o - Jun 1 0 1 S +R PK 2008 2009 - N 1 0 0 - +R PK 2009 o - Ap 15 0 1 S +R P 1999 2005 - Ap F>=15 0 1 S +R P 1999 2003 - O F>=15 0 0 - +R P 2004 o - O 1 1 0 - +R P 2005 o - O 4 2 0 - +R P 2006 2007 - Ap 1 0 1 S +R P 2006 o - S 22 0 0 - +R P 2007 o - S 13 2 0 - +R P 2008 2009 - Mar lastF 0 1 S +R P 2008 o - S 1 0 0 - +R P 2009 o - S 4 1 0 - +R P 2010 o - Mar 26 0 1 S +R P 2010 o - Au 11 0 0 - +R P 2011 o - Ap 1 0:1 1 S +R P 2011 o - Au 1 0 0 - +R P 2011 o - Au 30 0 1 S +R P 2011 o - S 30 0 0 - +R P 2012 2014 - Mar lastTh 24 1 S +R P 2012 o - S 21 1 0 - +R P 2013 o - S 27 0 0 - +R P 2014 o - O 24 0 0 - +R P 2015 o - Mar 28 0 1 S +R P 2015 o - O 23 1 0 - +R P 2016 2018 - Mar Sa<=30 1 1 S +R P 2016 2018 - O Sa<=30 1 0 - +R P 2019 o - Mar 29 0 1 S +R P 2019 o - O Sa<=30 0 0 - +R P 2020 2021 - Mar Sa<=30 0 1 S +R P 2020 o - O 24 1 0 - +R P 2021 o - O 29 1 0 - +R P 2022 o - Mar 27 0 1 S +R P 2022 2035 - O Sa<=30 2 0 - +R P 2023 o - Ap 29 2 1 S +R P 2024 o - Ap 20 2 1 S +R P 2025 o - Ap 12 2 1 S +R P 2026 2054 - Mar Sa<=30 2 1 S +R P 2036 o - O 18 2 0 - +R P 2037 o - O 10 2 0 - +R P 2038 o - S 25 2 0 - +R P 2039 o - S 17 2 0 - +R P 2040 o - S 1 2 0 - +R P 2040 o - O 20 2 1 S +R P 2040 2067 - O Sa<=30 2 0 - +R P 2041 o - Au 24 2 0 - +R P 2041 o - O 5 2 1 S +R P 2042 o - Au 16 2 0 - +R P 2042 o - S 27 2 1 S +R P 2043 o - Au 1 2 0 - +R P 2043 o - S 19 2 1 S +R P 2044 o - Jul 23 2 0 - +R P 2044 o - S 3 2 1 S +R P 2045 o - Jul 15 2 0 - +R P 2045 o - Au 26 2 1 S +R P 2046 o - Jun 30 2 0 - +R P 2046 o - Au 18 2 1 S +R P 2047 o - Jun 22 2 0 - +R P 2047 o - Au 3 2 1 S +R P 2048 o - Jun 6 2 0 - +R P 2048 o - Jul 25 2 1 S +R P 2049 o - May 29 2 0 - +R P 2049 o - Jul 10 2 1 S +R P 2050 o - May 21 2 0 - +R P 2050 o - Jul 2 2 1 S +R P 2051 o - May 6 2 0 - +R P 2051 o - Jun 24 2 1 S +R P 2052 o - Ap 27 2 0 - +R P 2052 o - Jun 8 2 1 S +R P 2053 o - Ap 12 2 0 - +R P 2053 o - May 31 2 1 S +R P 2054 o - Ap 4 2 0 - +R P 2054 o - May 23 2 1 S +R P 2055 o - May 8 2 1 S +R P 2056 o - Ap 29 2 1 S +R P 2057 o - Ap 14 2 1 S +R P 2058 o - Ap 6 2 1 S +R P 2059 ma - Mar Sa<=30 2 1 S +R P 2068 o - O 20 2 0 - +R P 2069 o - O 12 2 0 - +R P 2070 o - O 4 2 0 - +R P 2071 o - S 19 2 0 - +R P 2072 o - S 10 2 0 - +R P 2072 o - O 22 2 1 S +R P 2072 ma - O Sa<=30 2 0 - +R P 2073 o - S 2 2 0 - +R P 2073 o - O 14 2 1 S +R P 2074 o - Au 18 2 0 - +R P 2074 o - O 6 2 1 S +R P 2075 o - Au 10 2 0 - +R P 2075 o - S 21 2 1 S +R P 2076 o - Jul 25 2 0 - +R P 2076 o - S 12 2 1 S +R P 2077 o - Jul 17 2 0 - +R P 2077 o - S 4 2 1 S +R P 2078 o - Jul 9 2 0 - +R P 2078 o - Au 20 2 1 S +R P 2079 o - Jun 24 2 0 - +R P 2079 o - Au 12 2 1 S +R P 2080 o - Jun 15 2 0 - +R P 2080 o - Jul 27 2 1 S +R P 2081 o - Jun 7 2 0 - +R P 2081 o - Jul 19 2 1 S +R P 2082 o - May 23 2 0 - +R P 2082 o - Jul 11 2 1 S +R P 2083 o - May 15 2 0 - +R P 2083 o - Jun 26 2 1 S +R P 2084 o - Ap 29 2 0 - +R P 2084 o - Jun 17 2 1 S +R P 2085 o - Ap 21 2 0 - +R P 2085 o - Jun 9 2 1 S +R P 2086 o - Ap 13 2 0 - +R P 2086 o - May 25 2 1 S +R PH 1936 o - O 31 24 1 D +R PH 1937 o - Ja 15 24 0 S +R PH 1941 o - D 15 24 1 D +R PH 1945 o - N 30 24 0 S +R PH 1954 o - Ap 11 24 1 D +R PH 1954 o - Jun 4 24 0 S +R PH 1977 o - Mar 27 24 1 D +R PH 1977 o - S 21 24 0 S +R PH 1990 o - May 21 0 1 D +R PH 1990 o - Jul 28 24 0 S +R S 1920 1923 - Ap Su>=15 2 1 S +R S 1920 1923 - O Su>=1 2 0 - +R S 1962 o - Ap 29 2 1 S +R S 1962 o - O 1 2 0 - +R S 1963 1965 - May 1 2 1 S +R S 1963 o - S 30 2 0 - +R S 1964 o - O 1 2 0 - +R S 1965 o - S 30 2 0 - +R S 1966 o - Ap 24 2 1 S +R S 1966 1976 - O 1 2 0 - +R S 1967 1978 - May 1 2 1 S +R S 1977 1978 - S 1 2 0 - +R S 1983 1984 - Ap 9 2 1 S +R S 1983 1984 - O 1 2 0 - +R S 1986 o - F 16 2 1 S +R S 1986 o - O 9 2 0 - +R S 1987 o - Mar 1 2 1 S +R S 1987 1988 - O 31 2 0 - +R S 1988 o - Mar 15 2 1 S +R S 1989 o - Mar 31 2 1 S +R S 1989 o - O 1 2 0 - +R S 1990 o - Ap 1 2 1 S +R S 1990 o - S 30 2 0 - +R S 1991 o - Ap 1 0 1 S +R S 1991 1992 - O 1 0 0 - +R S 1992 o - Ap 8 0 1 S +R S 1993 o - Mar 26 0 1 S +R S 1993 o - S 25 0 0 - +R S 1994 1996 - Ap 1 0 1 S +R S 1994 2005 - O 1 0 0 - +R S 1997 1998 - Mar lastM 0 1 S +R S 1999 2006 - Ap 1 0 1 S +R S 2006 o - S 22 0 0 - +R S 2007 o - Mar lastF 0 1 S +R S 2007 o - N F>=1 0 0 - +R S 2008 o - Ap F>=1 0 1 S +R S 2008 o - N 1 0 0 - +R S 2009 o - Mar lastF 0 1 S +R S 2010 2011 - Ap F>=1 0 1 S +R S 2012 2022 - Mar lastF 0 1 S +R S 2009 2022 - O lastF 0 0 - +R AU 1917 o - Ja 1 2s 1 D +R AU 1917 o - Mar lastSu 2s 0 S +R AU 1942 o - Ja 1 2s 1 D +R AU 1942 o - Mar lastSu 2s 0 S +R AU 1942 o - S 27 2s 1 D +R AU 1943 1944 - Mar lastSu 2s 0 S +R AU 1943 o - O 3 2s 1 D +R AW 1974 o - O lastSu 2s 1 D +R AW 1975 o - Mar Su>=1 2s 0 S +R AW 1983 o - O lastSu 2s 1 D +R AW 1984 o - Mar Su>=1 2s 0 S +R AW 1991 o - N 17 2s 1 D +R AW 1992 o - Mar Su>=1 2s 0 S +R AW 2006 o - D 3 2s 1 D +R AW 2007 2009 - Mar lastSu 2s 0 S +R AW 2007 2008 - O lastSu 2s 1 D +R AQ 1971 o - O lastSu 2s 1 D +R AQ 1972 o - F lastSu 2s 0 S +R AQ 1989 1991 - O lastSu 2s 1 D +R AQ 1990 1992 - Mar Su>=1 2s 0 S +R Ho 1992 1993 - O lastSu 2s 1 D +R Ho 1993 1994 - Mar Su>=1 2s 0 S +R AS 1971 1985 - O lastSu 2s 1 D +R AS 1986 o - O 19 2s 1 D +R AS 1987 2007 - O lastSu 2s 1 D +R AS 1972 o - F 27 2s 0 S +R AS 1973 1985 - Mar Su>=1 2s 0 S +R AS 1986 1990 - Mar Su>=15 2s 0 S +R AS 1991 o - Mar 3 2s 0 S +R AS 1992 o - Mar 22 2s 0 S +R AS 1993 o - Mar 7 2s 0 S +R AS 1994 o - Mar 20 2s 0 S +R AS 1995 2005 - Mar lastSu 2s 0 S +R AS 2006 o - Ap 2 2s 0 S +R AS 2007 o - Mar lastSu 2s 0 S +R AS 2008 ma - Ap Su>=1 2s 0 S +R AS 2008 ma - O Su>=1 2s 1 D +R AT 1916 o - O Su>=1 2s 1 D +R AT 1917 o - Mar lastSu 2s 0 S +R AT 1917 1918 - O Su>=22 2s 1 D +R AT 1918 1919 - Mar Su>=1 2s 0 S +R AT 1967 o - O Su>=1 2s 1 D +R AT 1968 o - Mar Su>=29 2s 0 S +R AT 1968 1985 - O lastSu 2s 1 D +R AT 1969 1971 - Mar Su>=8 2s 0 S +R AT 1972 o - F lastSu 2s 0 S +R AT 1973 1981 - Mar Su>=1 2s 0 S +R AT 1982 1983 - Mar lastSu 2s 0 S +R AT 1984 1986 - Mar Su>=1 2s 0 S +R AT 1986 o - O Su>=15 2s 1 D +R AT 1987 1990 - Mar Su>=15 2s 0 S +R AT 1987 o - O Su>=22 2s 1 D +R AT 1988 1990 - O lastSu 2s 1 D +R AT 1991 1999 - O Su>=1 2s 1 D +R AT 1991 2005 - Mar lastSu 2s 0 S +R AT 2000 o - Au lastSu 2s 1 D +R AT 2001 ma - O Su>=1 2s 1 D +R AT 2006 o - Ap Su>=1 2s 0 S +R AT 2007 o - Mar lastSu 2s 0 S +R AT 2008 ma - Ap Su>=1 2s 0 S +R AV 1971 1985 - O lastSu 2s 1 D +R AV 1972 o - F lastSu 2s 0 S +R AV 1973 1985 - Mar Su>=1 2s 0 S +R AV 1986 1990 - Mar Su>=15 2s 0 S +R AV 1986 1987 - O Su>=15 2s 1 D +R AV 1988 1999 - O lastSu 2s 1 D +R AV 1991 1994 - Mar Su>=1 2s 0 S +R AV 1995 2005 - Mar lastSu 2s 0 S +R AV 2000 o - Au lastSu 2s 1 D +R AV 2001 2007 - O lastSu 2s 1 D +R AV 2006 o - Ap Su>=1 2s 0 S +R AV 2007 o - Mar lastSu 2s 0 S +R AV 2008 ma - Ap Su>=1 2s 0 S +R AV 2008 ma - O Su>=1 2s 1 D +R AN 1971 1985 - O lastSu 2s 1 D +R AN 1972 o - F 27 2s 0 S +R AN 1973 1981 - Mar Su>=1 2s 0 S +R AN 1982 o - Ap Su>=1 2s 0 S +R AN 1983 1985 - Mar Su>=1 2s 0 S +R AN 1986 1989 - Mar Su>=15 2s 0 S +R AN 1986 o - O 19 2s 1 D +R AN 1987 1999 - O lastSu 2s 1 D +R AN 1990 1995 - Mar Su>=1 2s 0 S +R AN 1996 2005 - Mar lastSu 2s 0 S +R AN 2000 o - Au lastSu 2s 1 D +R AN 2001 2007 - O lastSu 2s 1 D +R AN 2006 o - Ap Su>=1 2s 0 S +R AN 2007 o - Mar lastSu 2s 0 S +R AN 2008 ma - Ap Su>=1 2s 0 S +R AN 2008 ma - O Su>=1 2s 1 D +R LH 1981 1984 - O lastSu 2 1 - +R LH 1982 1985 - Mar Su>=1 2 0 - +R LH 1985 o - O lastSu 2 0:30 - +R LH 1986 1989 - Mar Su>=15 2 0 - +R LH 1986 o - O 19 2 0:30 - +R LH 1987 1999 - O lastSu 2 0:30 - +R LH 1990 1995 - Mar Su>=1 2 0 - +R LH 1996 2005 - Mar lastSu 2 0 - +R LH 2000 o - Au lastSu 2 0:30 - +R LH 2001 2007 - O lastSu 2 0:30 - +R LH 2006 o - Ap Su>=1 2 0 - +R LH 2007 o - Mar lastSu 2 0 - +R LH 2008 ma - Ap Su>=1 2 0 - +R LH 2008 ma - O Su>=1 2 0:30 - +R FJ 1998 1999 - N Su>=1 2 1 - +R FJ 1999 2000 - F lastSu 3 0 - +R FJ 2009 o - N 29 2 1 - +R FJ 2010 o - Mar lastSu 3 0 - +R FJ 2010 2013 - O Su>=21 2 1 - +R FJ 2011 o - Mar Su>=1 3 0 - +R FJ 2012 2013 - Ja Su>=18 3 0 - +R FJ 2014 o - Ja Su>=18 2 0 - +R FJ 2014 2018 - N Su>=1 2 1 - +R FJ 2015 2021 - Ja Su>=12 3 0 - +R FJ 2019 o - N Su>=8 2 1 - +R FJ 2020 o - D 20 2 1 - +R Gu 1959 o - Jun 27 2 1 D +R Gu 1961 o - Ja 29 2 0 S +R Gu 1967 o - S 1 2 1 D +R Gu 1969 o - Ja 26 0:1 0 S +R Gu 1969 o - Jun 22 2 1 D +R Gu 1969 o - Au 31 2 0 S +R Gu 1970 1971 - Ap lastSu 2 1 D +R Gu 1970 1971 - S Su>=1 2 0 S +R Gu 1973 o - D 16 2 1 D +R Gu 1974 o - F 24 2 0 S +R Gu 1976 o - May 26 2 1 D +R Gu 1976 o - Au 22 2:1 0 S +R Gu 1977 o - Ap 24 2 1 D +R Gu 1977 o - Au 28 2 0 S +R NC 1977 1978 - D Su>=1 0 1 - +R NC 1978 1979 - F 27 0 0 - +R NC 1996 o - D 1 2s 1 - +R NC 1997 o - Mar 2 2s 0 - +R NZ 1927 o - N 6 2 1 S +R NZ 1928 o - Mar 4 2 0 M +R NZ 1928 1933 - O Su>=8 2 0:30 S +R NZ 1929 1933 - Mar Su>=15 2 0 M +R NZ 1934 1940 - Ap lastSu 2 0 M +R NZ 1934 1940 - S lastSu 2 0:30 S +R NZ 1946 o - Ja 1 0 0 S +R NZ 1974 o - N Su>=1 2s 1 D +R k 1974 o - N Su>=1 2:45s 1 - +R NZ 1975 o - F lastSu 2s 0 S +R k 1975 o - F lastSu 2:45s 0 - +R NZ 1975 1988 - O lastSu 2s 1 D +R k 1975 1988 - O lastSu 2:45s 1 - +R NZ 1976 1989 - Mar Su>=1 2s 0 S +R k 1976 1989 - Mar Su>=1 2:45s 0 - +R NZ 1989 o - O Su>=8 2s 1 D +R k 1989 o - O Su>=8 2:45s 1 - +R NZ 1990 2006 - O Su>=1 2s 1 D +R k 1990 2006 - O Su>=1 2:45s 1 - +R NZ 1990 2007 - Mar Su>=15 2s 0 S +R k 1990 2007 - Mar Su>=15 2:45s 0 - +R NZ 2007 ma - S lastSu 2s 1 D +R k 2007 ma - S lastSu 2:45s 1 - +R NZ 2008 ma - Ap Su>=1 2s 0 S +R k 2008 ma - Ap Su>=1 2:45s 0 - +R CK 1978 o - N 12 0 0:30 - +R CK 1979 1991 - Mar Su>=1 0 0 - +R CK 1979 1990 - O lastSu 0 0:30 - +R WS 2010 o - S lastSu 0 1 - +R WS 2011 o - Ap Sa>=1 4 0 - +R WS 2011 o - S lastSa 3 1 - +R WS 2012 2021 - Ap Su>=1 4 0 - +R WS 2012 2020 - S lastSu 3 1 - +R TO 1999 o - O 7 2s 1 - +R TO 2000 o - Mar 19 2s 0 - +R TO 2000 2001 - N Su>=1 2 1 - +R TO 2001 2002 - Ja lastSu 2 0 - +R TO 2016 o - N Su>=1 2 1 - +R TO 2017 o - Ja Su>=15 3 0 - +R VU 1973 o - D 22 12u 1 - +R VU 1974 o - Mar 30 12u 0 - +R VU 1983 1991 - S Sa>=22 24 1 - +R VU 1984 1991 - Mar Sa>=22 24 0 - +R VU 1992 1993 - Ja Sa>=22 24 0 - +R VU 1992 o - O Sa>=22 24 1 - +R G 1916 o - May 21 2s 1 BST +R G 1916 o - O 1 2s 0 GMT +R G 1917 o - Ap 8 2s 1 BST +R G 1917 o - S 17 2s 0 GMT +R G 1918 o - Mar 24 2s 1 BST +R G 1918 o - S 30 2s 0 GMT +R G 1919 o - Mar 30 2s 1 BST +R G 1919 o - S 29 2s 0 GMT +R G 1920 o - Mar 28 2s 1 BST +R G 1920 o - O 25 2s 0 GMT +R G 1921 o - Ap 3 2s 1 BST +R G 1921 o - O 3 2s 0 GMT +R G 1922 o - Mar 26 2s 1 BST +R G 1922 o - O 8 2s 0 GMT +R G 1923 o - Ap Su>=16 2s 1 BST +R G 1923 1924 - S Su>=16 2s 0 GMT +R G 1924 o - Ap Su>=9 2s 1 BST +R G 1925 1926 - Ap Su>=16 2s 1 BST +R G 1925 1938 - O Su>=2 2s 0 GMT +R G 1927 o - Ap Su>=9 2s 1 BST +R G 1928 1929 - Ap Su>=16 2s 1 BST +R G 1930 o - Ap Su>=9 2s 1 BST +R G 1931 1932 - Ap Su>=16 2s 1 BST +R G 1933 o - Ap Su>=9 2s 1 BST +R G 1934 o - Ap Su>=16 2s 1 BST +R G 1935 o - Ap Su>=9 2s 1 BST +R G 1936 1937 - Ap Su>=16 2s 1 BST +R G 1938 o - Ap Su>=9 2s 1 BST +R G 1939 o - Ap Su>=16 2s 1 BST +R G 1939 o - N Su>=16 2s 0 GMT +R G 1940 o - F Su>=23 2s 1 BST +R G 1941 o - May Su>=2 1s 2 BDST +R G 1941 1943 - Au Su>=9 1s 1 BST +R G 1942 1944 - Ap Su>=2 1s 2 BDST +R G 1944 o - S Su>=16 1s 1 BST +R G 1945 o - Ap M>=2 1s 2 BDST +R G 1945 o - Jul Su>=9 1s 1 BST +R G 1945 1946 - O Su>=2 2s 0 GMT +R G 1946 o - Ap Su>=9 2s 1 BST +R G 1947 o - Mar 16 2s 1 BST +R G 1947 o - Ap 13 1s 2 BDST +R G 1947 o - Au 10 1s 1 BST +R G 1947 o - N 2 2s 0 GMT +R G 1948 o - Mar 14 2s 1 BST +R G 1948 o - O 31 2s 0 GMT +R G 1949 o - Ap 3 2s 1 BST +R G 1949 o - O 30 2s 0 GMT +R G 1950 1952 - Ap Su>=14 2s 1 BST +R G 1950 1952 - O Su>=21 2s 0 GMT +R G 1953 o - Ap Su>=16 2s 1 BST +R G 1953 1960 - O Su>=2 2s 0 GMT +R G 1954 o - Ap Su>=9 2s 1 BST +R G 1955 1956 - Ap Su>=16 2s 1 BST +R G 1957 o - Ap Su>=9 2s 1 BST +R G 1958 1959 - Ap Su>=16 2s 1 BST +R G 1960 o - Ap Su>=9 2s 1 BST +R G 1961 1963 - Mar lastSu 2s 1 BST +R G 1961 1968 - O Su>=23 2s 0 GMT +R G 1964 1967 - Mar Su>=19 2s 1 BST +R G 1968 o - F 18 2s 1 BST +R G 1972 1980 - Mar Su>=16 2s 1 BST +R G 1972 1980 - O Su>=23 2s 0 GMT +R G 1981 1995 - Mar lastSu 1u 1 BST +R G 1981 1989 - O Su>=23 1u 0 GMT +R G 1990 1995 - O Su>=22 1u 0 GMT +R IE 1971 o - O 31 2u -1 - +R IE 1972 1980 - Mar Su>=16 2u 0 - +R IE 1972 1980 - O Su>=23 2u -1 - +R IE 1981 ma - Mar lastSu 1u 0 - +R IE 1981 1989 - O Su>=23 1u -1 - +R IE 1990 1995 - O Su>=22 1u -1 - +R IE 1996 ma - O lastSu 1u -1 - +R E 1977 1980 - Ap Su>=1 1u 1 S +R E 1977 o - S lastSu 1u 0 - +R E 1978 o - O 1 1u 0 - +R E 1979 1995 - S lastSu 1u 0 - +R E 1981 ma - Mar lastSu 1u 1 S +R E 1996 ma - O lastSu 1u 0 - +R W- 1977 1980 - Ap Su>=1 1s 1 S +R W- 1977 o - S lastSu 1s 0 - +R W- 1978 o - O 1 1s 0 - +R W- 1979 1995 - S lastSu 1s 0 - +R W- 1981 ma - Mar lastSu 1s 1 S +R W- 1996 ma - O lastSu 1s 0 - +R c 1916 o - Ap 30 23 1 S +R c 1916 o - O 1 1 0 - +R c 1917 1918 - Ap M>=15 2s 1 S +R c 1917 1918 - S M>=15 2s 0 - +R c 1940 o - Ap 1 2s 1 S +R c 1942 o - N 2 2s 0 - +R c 1943 o - Mar 29 2s 1 S +R c 1943 o - O 4 2s 0 - +R c 1944 1945 - Ap M>=1 2s 1 S +R c 1944 o - O 2 2s 0 - +R c 1945 o - S 16 2s 0 - +R c 1977 1980 - Ap Su>=1 2s 1 S +R c 1977 o - S lastSu 2s 0 - +R c 1978 o - O 1 2s 0 - +R c 1979 1995 - S lastSu 2s 0 - +R c 1981 ma - Mar lastSu 2s 1 S +R c 1996 ma - O lastSu 2s 0 - +R e 1977 1980 - Ap Su>=1 0 1 S +R e 1977 o - S lastSu 0 0 - +R e 1978 o - O 1 0 0 - +R e 1979 1995 - S lastSu 0 0 - +R e 1981 ma - Mar lastSu 0 1 S +R e 1996 ma - O lastSu 0 0 - +R R 1917 o - Jul 1 23 1 MST +R R 1917 o - D 28 0 0 MMT +R R 1918 o - May 31 22 2 MDST +R R 1918 o - S 16 1 1 MST +R R 1919 o - May 31 23 2 MDST +R R 1919 o - Jul 1 0u 1 MSD +R R 1919 o - Au 16 0 0 MSK +R R 1921 o - F 14 23 1 MSD +R R 1921 o - Mar 20 23 2 +05 +R R 1921 o - S 1 0 1 MSD +R R 1921 o - O 1 0 0 - +R R 1981 1984 - Ap 1 0 1 S +R R 1981 1983 - O 1 0 0 - +R R 1984 1995 - S lastSu 2s 0 - +R R 1985 2010 - Mar lastSu 2s 1 S +R R 1996 2010 - O lastSu 2s 0 - +R q 1940 o - Jun 16 0 1 S +R q 1942 o - N 2 3 0 - +R q 1943 o - Mar 29 2 1 S +R q 1943 o - Ap 10 3 0 - +R q 1974 o - May 4 0 1 S +R q 1974 o - O 2 0 0 - +R q 1975 o - May 1 0 1 S +R q 1975 o - O 2 0 0 - +R q 1976 o - May 2 0 1 S +R q 1976 o - O 3 0 0 - +R q 1977 o - May 8 0 1 S +R q 1977 o - O 2 0 0 - +R q 1978 o - May 6 0 1 S +R q 1978 o - O 1 0 0 - +R q 1979 o - May 5 0 1 S +R q 1979 o - S 30 0 0 - +R q 1980 o - May 3 0 1 S +R q 1980 o - O 4 0 0 - +R q 1981 o - Ap 26 0 1 S +R q 1981 o - S 27 0 0 - +R q 1982 o - May 2 0 1 S +R q 1982 o - O 3 0 0 - +R q 1983 o - Ap 18 0 1 S +R q 1983 o - O 1 0 0 - +R q 1984 o - Ap 1 0 1 S +R a 1920 o - Ap 5 2s 1 S +R a 1920 o - S 13 2s 0 - +R a 1946 o - Ap 14 2s 1 S +R a 1946 o - O 7 2s 0 - +R a 1947 1948 - O Su>=1 2s 0 - +R a 1947 o - Ap 6 2s 1 S +R a 1948 o - Ap 18 2s 1 S +R a 1980 o - Ap 6 0 1 S +R a 1980 o - S 28 0 0 - +R b 1918 o - Mar 9 0s 1 S +R b 1918 1919 - O Sa>=1 23s 0 - +R b 1919 o - Mar 1 23s 1 S +R b 1920 o - F 14 23s 1 S +R b 1920 o - O 23 23s 0 - +R b 1921 o - Mar 14 23s 1 S +R b 1921 o - O 25 23s 0 - +R b 1922 o - Mar 25 23s 1 S +R b 1922 1927 - O Sa>=1 23s 0 - +R b 1923 o - Ap 21 23s 1 S +R b 1924 o - Mar 29 23s 1 S +R b 1925 o - Ap 4 23s 1 S +R b 1926 o - Ap 17 23s 1 S +R b 1927 o - Ap 9 23s 1 S +R b 1928 o - Ap 14 23s 1 S +R b 1928 1938 - O Su>=2 2s 0 - +R b 1929 o - Ap 21 2s 1 S +R b 1930 o - Ap 13 2s 1 S +R b 1931 o - Ap 19 2s 1 S +R b 1932 o - Ap 3 2s 1 S +R b 1933 o - Mar 26 2s 1 S +R b 1934 o - Ap 8 2s 1 S +R b 1935 o - Mar 31 2s 1 S +R b 1936 o - Ap 19 2s 1 S +R b 1937 o - Ap 4 2s 1 S +R b 1938 o - Mar 27 2s 1 S +R b 1939 o - Ap 16 2s 1 S +R b 1939 o - N 19 2s 0 - +R b 1940 o - F 25 2s 1 S +R b 1944 o - S 17 2s 0 - +R b 1945 o - Ap 2 2s 1 S +R b 1945 o - S 16 2s 0 - +R b 1946 o - May 19 2s 1 S +R b 1946 o - O 7 2s 0 - +R BG 1979 o - Mar 31 23 1 S +R BG 1979 o - O 1 1 0 - +R BG 1980 1982 - Ap Sa>=1 23 1 S +R BG 1980 o - S 29 1 0 - +R BG 1981 o - S 27 2 0 - +R CZ 1945 o - Ap M>=1 2s 1 S +R CZ 1945 o - O 1 2s 0 - +R CZ 1946 o - May 6 2s 1 S +R CZ 1946 1949 - O Su>=1 2s 0 - +R CZ 1947 1948 - Ap Su>=15 2s 1 S +R CZ 1949 o - Ap 9 2s 1 S +R Th 1991 1992 - Mar lastSu 2 1 D +R Th 1991 1992 - S lastSu 2 0 S +R Th 1993 2006 - Ap Su>=1 2 1 D +R Th 1993 2006 - O lastSu 2 0 S +R Th 2007 ma - Mar Su>=8 2 1 D +R Th 2007 ma - N Su>=1 2 0 S +R FI 1942 o - Ap 2 24 1 S +R FI 1942 o - O 4 1 0 - +R FI 1981 1982 - Mar lastSu 2 1 S +R FI 1981 1982 - S lastSu 3 0 - +R F 1916 o - Jun 14 23s 1 S +R F 1916 1919 - O Su>=1 23s 0 - +R F 1917 o - Mar 24 23s 1 S +R F 1918 o - Mar 9 23s 1 S +R F 1919 o - Mar 1 23s 1 S +R F 1920 o - F 14 23s 1 S +R F 1920 o - O 23 23s 0 - +R F 1921 o - Mar 14 23s 1 S +R F 1921 o - O 25 23s 0 - +R F 1922 o - Mar 25 23s 1 S +R F 1922 1938 - O Sa>=1 23s 0 - +R F 1923 o - May 26 23s 1 S +R F 1924 o - Mar 29 23s 1 S +R F 1925 o - Ap 4 23s 1 S +R F 1926 o - Ap 17 23s 1 S +R F 1927 o - Ap 9 23s 1 S +R F 1928 o - Ap 14 23s 1 S +R F 1929 o - Ap 20 23s 1 S +R F 1930 o - Ap 12 23s 1 S +R F 1931 o - Ap 18 23s 1 S +R F 1932 o - Ap 2 23s 1 S +R F 1933 o - Mar 25 23s 1 S +R F 1934 o - Ap 7 23s 1 S +R F 1935 o - Mar 30 23s 1 S +R F 1936 o - Ap 18 23s 1 S +R F 1937 o - Ap 3 23s 1 S +R F 1938 o - Mar 26 23s 1 S +R F 1939 o - Ap 15 23s 1 S +R F 1939 o - N 18 23s 0 - +R F 1940 o - F 25 2 1 S +R F 1941 o - May 5 0 2 M +R F 1941 o - O 6 0 1 S +R F 1942 o - Mar 9 0 2 M +R F 1942 o - N 2 3 1 S +R F 1943 o - Mar 29 2 2 M +R F 1943 o - O 4 3 1 S +R F 1944 o - Ap 3 2 2 M +R F 1944 o - O 8 1 1 S +R F 1945 o - Ap 2 2 2 M +R F 1945 o - S 16 3 0 - +R F 1976 o - Mar 28 1 1 S +R F 1976 o - S 26 1 0 - +R DE 1946 o - Ap 14 2s 1 S +R DE 1946 o - O 7 2s 0 - +R DE 1947 1949 - O Su>=1 2s 0 - +R DE 1947 o - Ap 6 3s 1 S +R DE 1947 o - May 11 2s 2 M +R DE 1947 o - Jun 29 3 1 S +R DE 1948 o - Ap 18 2s 1 S +R DE 1949 o - Ap 10 2s 1 S +R So 1945 o - May 24 2 2 M +R So 1945 o - S 24 3 1 S +R So 1945 o - N 18 2s 0 - +R g 1932 o - Jul 7 0 1 S +R g 1932 o - S 1 0 0 - +R g 1941 o - Ap 7 0 1 S +R g 1942 o - N 2 3 0 - +R g 1943 o - Mar 30 0 1 S +R g 1943 o - O 4 0 0 - +R g 1952 o - Jul 1 0 1 S +R g 1952 o - N 2 0 0 - +R g 1975 o - Ap 12 0s 1 S +R g 1975 o - N 26 0s 0 - +R g 1976 o - Ap 11 2s 1 S +R g 1976 o - O 10 2s 0 - +R g 1977 1978 - Ap Su>=1 2s 1 S +R g 1977 o - S 26 2s 0 - +R g 1978 o - S 24 4 0 - +R g 1979 o - Ap 1 9 1 S +R g 1979 o - S 29 2 0 - +R g 1980 o - Ap 1 0 1 S +R g 1980 o - S 28 0 0 - +R h 1918 1919 - Ap 15 2 1 S +R h 1918 1920 - S M>=15 3 0 - +R h 1920 o - Ap 5 2 1 S +R h 1945 o - May 1 23 1 S +R h 1945 o - N 1 1 0 - +R h 1946 o - Mar 31 2s 1 S +R h 1946 o - O 7 2 0 - +R h 1947 1949 - Ap Su>=4 2s 1 S +R h 1947 1949 - O Su>=1 2s 0 - +R h 1954 o - May 23 0 1 S +R h 1954 o - O 3 0 0 - +R h 1955 o - May 22 2 1 S +R h 1955 o - O 2 3 0 - +R h 1956 1957 - Jun Su>=1 2 1 S +R h 1956 1957 - S lastSu 3 0 - +R h 1980 o - Ap 6 0 1 S +R h 1980 o - S 28 1 0 - +R h 1981 1983 - Mar lastSu 0 1 S +R h 1981 1983 - S lastSu 1 0 - +R I 1916 o - Jun 3 24 1 S +R I 1916 1917 - S 30 24 0 - +R I 1917 o - Mar 31 24 1 S +R I 1918 o - Mar 9 24 1 S +R I 1918 o - O 6 24 0 - +R I 1919 o - Mar 1 24 1 S +R I 1919 o - O 4 24 0 - +R I 1920 o - Mar 20 24 1 S +R I 1920 o - S 18 24 0 - +R I 1940 o - Jun 14 24 1 S +R I 1942 o - N 2 2s 0 - +R I 1943 o - Mar 29 2s 1 S +R I 1943 o - O 4 2s 0 - +R I 1944 o - Ap 2 2s 1 S +R I 1944 o - S 17 2s 0 - +R I 1945 o - Ap 2 2 1 S +R I 1945 o - S 15 1 0 - +R I 1946 o - Mar 17 2s 1 S +R I 1946 o - O 6 2s 0 - +R I 1947 o - Mar 16 0s 1 S +R I 1947 o - O 5 0s 0 - +R I 1948 o - F 29 2s 1 S +R I 1948 o - O 3 2s 0 - +R I 1966 1968 - May Su>=22 0s 1 S +R I 1966 o - S 24 24 0 - +R I 1967 1969 - S Su>=22 0s 0 - +R I 1969 o - Jun 1 0s 1 S +R I 1970 o - May 31 0s 1 S +R I 1970 o - S lastSu 0s 0 - +R I 1971 1972 - May Su>=22 0s 1 S +R I 1971 o - S lastSu 0s 0 - +R I 1972 o - O 1 0s 0 - +R I 1973 o - Jun 3 0s 1 S +R I 1973 1974 - S lastSu 0s 0 - +R I 1974 o - May 26 0s 1 S +R I 1975 o - Jun 1 0s 1 S +R I 1975 1977 - S lastSu 0s 0 - +R I 1976 o - May 30 0s 1 S +R I 1977 1979 - May Su>=22 0s 1 S +R I 1978 o - O 1 0s 0 - +R I 1979 o - S 30 0s 0 - +R LV 1989 1996 - Mar lastSu 2s 1 S +R LV 1989 1996 - S lastSu 2s 0 - +R MT 1973 o - Mar 31 0s 1 S +R MT 1973 o - S 29 0s 0 - +R MT 1974 o - Ap 21 0s 1 S +R MT 1974 o - S 16 0s 0 - +R MT 1975 1979 - Ap Su>=15 2 1 S +R MT 1975 1980 - S Su>=15 2 0 - +R MT 1980 o - Mar 31 2 1 S +R MD 1997 ma - Mar lastSu 2 1 S +R MD 1997 ma - O lastSu 3 0 - +R O 1918 1919 - S 16 2s 0 - +R O 1919 o - Ap 15 2s 1 S +R O 1944 o - Ap 3 2s 1 S +R O 1944 o - O 4 2 0 - +R O 1945 o - Ap 29 0 1 S +R O 1945 o - N 1 0 0 - +R O 1946 o - Ap 14 0s 1 S +R O 1946 o - O 7 2s 0 - +R O 1947 o - May 4 2s 1 S +R O 1947 1949 - O Su>=1 2s 0 - +R O 1948 o - Ap 18 2s 1 S +R O 1949 o - Ap 10 2s 1 S +R O 1957 o - Jun 2 1s 1 S +R O 1957 1958 - S lastSu 1s 0 - +R O 1958 o - Mar 30 1s 1 S +R O 1959 o - May 31 1s 1 S +R O 1959 1961 - O Su>=1 1s 0 - +R O 1960 o - Ap 3 1s 1 S +R O 1961 1964 - May lastSu 1s 1 S +R O 1962 1964 - S lastSu 1s 0 - +R p 1916 o - Jun 17 23 1 S +R p 1916 o - N 1 1 0 - +R p 1917 1921 - Mar 1 0 1 S +R p 1917 1921 - O 14 24 0 - +R p 1924 o - Ap 16 23s 1 S +R p 1924 o - O 4 23s 0 - +R p 1926 o - Ap 17 23s 1 S +R p 1926 1929 - O Sa>=1 23s 0 - +R p 1927 o - Ap 9 23s 1 S +R p 1928 o - Ap 14 23s 1 S +R p 1929 o - Ap 20 23s 1 S +R p 1931 o - Ap 18 23s 1 S +R p 1931 1932 - O Sa>=1 23s 0 - +R p 1932 o - Ap 2 23s 1 S +R p 1934 o - Ap 7 23s 1 S +R p 1934 1938 - O Sa>=1 23s 0 - +R p 1935 o - Mar 30 23s 1 S +R p 1936 o - Ap 18 23s 1 S +R p 1937 o - Ap 3 23s 1 S +R p 1938 o - Mar 26 23s 1 S +R p 1939 o - Ap 15 23s 1 S +R p 1939 o - N 18 23s 0 - +R p 1940 o - F 24 23s 1 S +R p 1940 o - O 7 23s 0 - +R p 1941 o - Ap 5 23s 1 S +R p 1941 o - O 5 23s 0 - +R p 1942 1945 - Mar Sa>=8 23s 1 S +R p 1942 o - Ap 25 22s 2 M +R p 1942 o - Au 15 22s 1 S +R p 1942 1945 - O Sa>=24 23s 0 - +R p 1943 o - Ap 17 22s 2 M +R p 1943 1945 - Au Sa>=25 22s 1 S +R p 1944 1945 - Ap Sa>=21 22s 2 M +R p 1946 o - Ap Sa>=1 23s 1 S +R p 1946 o - O Sa>=1 23s 0 - +R p 1947 1966 - Ap Su>=1 2s 1 S +R p 1947 1965 - O Su>=1 2s 0 - +R p 1976 o - S lastSu 1 0 - +R p 1977 o - Mar lastSu 0s 1 S +R p 1977 o - S lastSu 0s 0 - +R p 1978 1980 - Ap Su>=1 1s 1 S +R p 1978 o - O 1 1s 0 - +R p 1979 1980 - S lastSu 1s 0 - +R p 1981 1986 - Mar lastSu 0s 1 S +R p 1981 1985 - S lastSu 0s 0 - +R z 1932 o - May 21 0s 1 S +R z 1932 1939 - O Su>=1 0s 0 - +R z 1933 1939 - Ap Su>=2 0s 1 S +R z 1979 o - May 27 0 1 S +R z 1979 o - S lastSu 0 0 - +R z 1980 o - Ap 5 23 1 S +R z 1980 o - S lastSu 1 0 - +R z 1991 1993 - Mar lastSu 0s 1 S +R z 1991 1993 - S lastSu 0s 0 - +R s 1918 o - Ap 15 23 1 S +R s 1918 1919 - O 6 24s 0 - +R s 1919 o - Ap 6 23 1 S +R s 1924 o - Ap 16 23 1 S +R s 1924 o - O 4 24s 0 - +R s 1926 o - Ap 17 23 1 S +R s 1926 1929 - O Sa>=1 24s 0 - +R s 1927 o - Ap 9 23 1 S +R s 1928 o - Ap 15 0 1 S +R s 1929 o - Ap 20 23 1 S +R s 1937 o - Jun 16 23 1 S +R s 1937 o - O 2 24s 0 - +R s 1938 o - Ap 2 23 1 S +R s 1938 o - Ap 30 23 2 M +R s 1938 o - O 2 24 1 S +R s 1939 o - O 7 24s 0 - +R s 1942 o - May 2 23 1 S +R s 1942 o - S 1 1 0 - +R s 1943 1946 - Ap Sa>=13 23 1 S +R s 1943 1944 - O Su>=1 1 0 - +R s 1945 1946 - S lastSu 1 0 - +R s 1949 o - Ap 30 23 1 S +R s 1949 o - O 2 1 0 - +R s 1974 1975 - Ap Sa>=12 23 1 S +R s 1974 1975 - O Su>=1 1 0 - +R s 1976 o - Mar 27 23 1 S +R s 1976 1977 - S lastSu 1 0 - +R s 1977 o - Ap 2 23 1 S +R s 1978 o - Ap 2 2s 1 S +R s 1978 o - O 1 2s 0 - +R Sp 1967 o - Jun 3 12 1 S +R Sp 1967 o - O 1 0 0 - +R Sp 1974 o - Jun 24 0 1 S +R Sp 1974 o - S 1 0 0 - +R Sp 1976 1977 - May 1 0 1 S +R Sp 1976 o - Au 1 0 0 - +R Sp 1977 o - S 28 0 0 - +R Sp 1978 o - Jun 1 0 1 S +R Sp 1978 o - Au 4 0 0 - +R CH 1941 1942 - May M>=1 1 1 S +R CH 1941 1942 - O M>=1 2 0 - +R T 1916 o - May 1 0 1 S +R T 1916 o - O 1 0 0 - +R T 1920 o - Mar 28 0 1 S +R T 1920 o - O 25 0 0 - +R T 1921 o - Ap 3 0 1 S +R T 1921 o - O 3 0 0 - +R T 1922 o - Mar 26 0 1 S +R T 1922 o - O 8 0 0 - +R T 1924 o - May 13 0 1 S +R T 1924 1925 - O 1 0 0 - +R T 1925 o - May 1 0 1 S +R T 1940 o - Jul 1 0 1 S +R T 1940 o - O 6 0 0 - +R T 1940 o - D 1 0 1 S +R T 1941 o - S 21 0 0 - +R T 1942 o - Ap 1 0 1 S +R T 1945 o - O 8 0 0 - +R T 1946 o - Jun 1 0 1 S +R T 1946 o - O 1 0 0 - +R T 1947 1948 - Ap Su>=16 0 1 S +R T 1947 1951 - O Su>=2 0 0 - +R T 1949 o - Ap 10 0 1 S +R T 1950 o - Ap 16 0 1 S +R T 1951 o - Ap 22 0 1 S +R T 1962 o - Jul 15 0 1 S +R T 1963 o - O 30 0 0 - +R T 1964 o - May 15 0 1 S +R T 1964 o - O 1 0 0 - +R T 1973 o - Jun 3 1 1 S +R T 1973 1976 - O Su>=31 2 0 - +R T 1974 o - Mar 31 2 1 S +R T 1975 o - Mar 22 2 1 S +R T 1976 o - Mar 21 2 1 S +R T 1977 1978 - Ap Su>=1 2 1 S +R T 1977 1978 - O Su>=15 2 0 - +R T 1978 o - Jun 29 0 0 - +R T 1983 o - Jul 31 2 1 S +R T 1983 o - O 2 2 0 - +R T 1985 o - Ap 20 1s 1 S +R T 1985 o - S 28 1s 0 - +R T 1986 1993 - Mar lastSu 1s 1 S +R T 1986 1995 - S lastSu 1s 0 - +R T 1994 o - Mar 20 1s 1 S +R T 1995 2006 - Mar lastSu 1s 1 S +R T 1996 2006 - O lastSu 1s 0 - +R u 1918 1919 - Mar lastSu 2 1 D +R u 1918 1919 - O lastSu 2 0 S +R u 1942 o - F 9 2 1 W +R u 1945 o - Au 14 23u 1 P +R u 1945 o - S 30 2 0 S +R u 1967 2006 - O lastSu 2 0 S +R u 1967 1973 - Ap lastSu 2 1 D +R u 1974 o - Ja 6 2 1 D +R u 1975 o - F lastSu 2 1 D +R u 1976 1986 - Ap lastSu 2 1 D +R u 1987 2006 - Ap Su>=1 2 1 D +R u 2007 ma - Mar Su>=8 2 1 D +R u 2007 ma - N Su>=1 2 0 S +R NY 1920 o - Mar lastSu 2 1 D +R NY 1920 o - O lastSu 2 0 S +R NY 1921 1966 - Ap lastSu 2 1 D +R NY 1921 1954 - S lastSu 2 0 S +R NY 1955 1966 - O lastSu 2 0 S +R Ch 1920 o - Jun 13 2 1 D +R Ch 1920 1921 - O lastSu 2 0 S +R Ch 1921 o - Mar lastSu 2 1 D +R Ch 1922 1966 - Ap lastSu 2 1 D +R Ch 1922 1954 - S lastSu 2 0 S +R Ch 1955 1966 - O lastSu 2 0 S +R De 1920 1921 - Mar lastSu 2 1 D +R De 1920 o - O lastSu 2 0 S +R De 1921 o - May 22 2 0 S +R De 1965 1966 - Ap lastSu 2 1 D +R De 1965 1966 - O lastSu 2 0 S +R CA 1948 o - Mar 14 2:1 1 D +R CA 1949 o - Ja 1 2 0 S +R CA 1950 1966 - Ap lastSu 1 1 D +R CA 1950 1961 - S lastSu 2 0 S +R CA 1962 1966 - O lastSu 2 0 S +R In 1941 o - Jun 22 2 1 D +R In 1941 1954 - S lastSu 2 0 S +R In 1946 1954 - Ap lastSu 2 1 D +R Ma 1951 o - Ap lastSu 2 1 D +R Ma 1951 o - S lastSu 2 0 S +R Ma 1954 1960 - Ap lastSu 2 1 D +R Ma 1954 1960 - S lastSu 2 0 S +R V 1946 o - Ap lastSu 2 1 D +R V 1946 o - S lastSu 2 0 S +R V 1953 1954 - Ap lastSu 2 1 D +R V 1953 1959 - S lastSu 2 0 S +R V 1955 o - May 1 0 1 D +R V 1956 1963 - Ap lastSu 2 1 D +R V 1960 o - O lastSu 2 0 S +R V 1961 o - S lastSu 2 0 S +R V 1962 1963 - O lastSu 2 0 S +R Pe 1955 o - May 1 0 1 D +R Pe 1955 1960 - S lastSu 2 0 S +R Pe 1956 1963 - Ap lastSu 2 1 D +R Pe 1961 1963 - O lastSu 2 0 S +R Pi 1955 o - May 1 0 1 D +R Pi 1955 1960 - S lastSu 2 0 S +R Pi 1956 1964 - Ap lastSu 2 1 D +R Pi 1961 1964 - O lastSu 2 0 S +R St 1947 1961 - Ap lastSu 2 1 D +R St 1947 1954 - S lastSu 2 0 S +R St 1955 1956 - O lastSu 2 0 S +R St 1957 1958 - S lastSu 2 0 S +R St 1959 1961 - O lastSu 2 0 S +R Pu 1946 1960 - Ap lastSu 2 1 D +R Pu 1946 1954 - S lastSu 2 0 S +R Pu 1955 1956 - O lastSu 2 0 S +R Pu 1957 1960 - S lastSu 2 0 S +R v 1921 o - May 1 2 1 D +R v 1921 o - S 1 2 0 S +R v 1941 o - Ap lastSu 2 1 D +R v 1941 o - S lastSu 2 0 S +R v 1946 o - Ap lastSu 0:1 1 D +R v 1946 o - Jun 2 2 0 S +R v 1950 1961 - Ap lastSu 2 1 D +R v 1950 1955 - S lastSu 2 0 S +R v 1956 1961 - O lastSu 2 0 S +R Dt 1948 o - Ap lastSu 2 1 D +R Dt 1948 o - S lastSu 2 0 S +R Me 1946 o - Ap lastSu 2 1 D +R Me 1946 o - S lastSu 2 0 S +R Me 1966 o - Ap lastSu 2 1 D +R Me 1966 o - O lastSu 2 0 S +R C 1918 o - Ap 14 2 1 D +R C 1918 o - O 27 2 0 S +R C 1942 o - F 9 2 1 W +R C 1945 o - Au 14 23u 1 P +R C 1945 o - S 30 2 0 S +R C 1974 1986 - Ap lastSu 2 1 D +R C 1974 2006 - O lastSu 2 0 S +R C 1987 2006 - Ap Su>=1 2 1 D +R C 2007 ma - Mar Su>=8 2 1 D +R C 2007 ma - N Su>=1 2 0 S +R j 1917 o - Ap 8 2 1 D +R j 1917 o - S 17 2 0 S +R j 1919 o - May 5 23 1 D +R j 1919 o - Au 12 23 0 S +R j 1920 1935 - May Su>=1 23 1 D +R j 1920 1935 - O lastSu 23 0 S +R j 1936 1941 - May M>=9 0 1 D +R j 1936 1941 - O M>=2 0 0 S +R j 1946 1950 - May Su>=8 2 1 D +R j 1946 1950 - O Su>=2 2 0 S +R j 1951 1986 - Ap lastSu 2 1 D +R j 1951 1959 - S lastSu 2 0 S +R j 1960 1986 - O lastSu 2 0 S +R j 1987 o - Ap Su>=1 0:1 1 D +R j 1987 2006 - O lastSu 0:1 0 S +R j 1988 o - Ap Su>=1 0:1 2 DD +R j 1989 2006 - Ap Su>=1 0:1 1 D +R j 2007 2011 - Mar Su>=8 0:1 1 D +R j 2007 2010 - N Su>=1 0:1 0 S +R H 1916 o - Ap 1 0 1 D +R H 1916 o - O 1 0 0 S +R H 1920 o - May 9 0 1 D +R H 1920 o - Au 29 0 0 S +R H 1921 o - May 6 0 1 D +R H 1921 1922 - S 5 0 0 S +R H 1922 o - Ap 30 0 1 D +R H 1923 1925 - May Su>=1 0 1 D +R H 1923 o - S 4 0 0 S +R H 1924 o - S 15 0 0 S +R H 1925 o - S 28 0 0 S +R H 1926 o - May 16 0 1 D +R H 1926 o - S 13 0 0 S +R H 1927 o - May 1 0 1 D +R H 1927 o - S 26 0 0 S +R H 1928 1931 - May Su>=8 0 1 D +R H 1928 o - S 9 0 0 S +R H 1929 o - S 3 0 0 S +R H 1930 o - S 15 0 0 S +R H 1931 1932 - S M>=24 0 0 S +R H 1932 o - May 1 0 1 D +R H 1933 o - Ap 30 0 1 D +R H 1933 o - O 2 0 0 S +R H 1934 o - May 20 0 1 D +R H 1934 o - S 16 0 0 S +R H 1935 o - Jun 2 0 1 D +R H 1935 o - S 30 0 0 S +R H 1936 o - Jun 1 0 1 D +R H 1936 o - S 14 0 0 S +R H 1937 1938 - May Su>=1 0 1 D +R H 1937 1941 - S M>=24 0 0 S +R H 1939 o - May 28 0 1 D +R H 1940 1941 - May Su>=1 0 1 D +R H 1946 1949 - Ap lastSu 2 1 D +R H 1946 1949 - S lastSu 2 0 S +R H 1951 1954 - Ap lastSu 2 1 D +R H 1951 1954 - S lastSu 2 0 S +R H 1956 1959 - Ap lastSu 2 1 D +R H 1956 1959 - S lastSu 2 0 S +R H 1962 1973 - Ap lastSu 2 1 D +R H 1962 1973 - O lastSu 2 0 S +R o 1933 1935 - Jun Su>=8 1 1 D +R o 1933 1935 - S Su>=8 1 0 S +R o 1936 1938 - Jun Su>=1 1 1 D +R o 1936 1938 - S Su>=1 1 0 S +R o 1939 o - May 27 1 1 D +R o 1939 1941 - S Sa>=21 1 0 S +R o 1940 o - May 19 1 1 D +R o 1941 o - May 4 1 1 D +R o 1946 1972 - Ap lastSu 2 1 D +R o 1946 1956 - S lastSu 2 0 S +R o 1957 1972 - O lastSu 2 0 S +R o 1993 2006 - Ap Su>=1 0:1 1 D +R o 1993 2006 - O lastSu 0:1 0 S +R t 1919 o - Mar 30 23:30 1 D +R t 1919 o - O 26 0 0 S +R t 1920 o - May 2 2 1 D +R t 1920 o - S 26 0 0 S +R t 1921 o - May 15 2 1 D +R t 1921 o - S 15 2 0 S +R t 1922 1923 - May Su>=8 2 1 D +R t 1922 1926 - S Su>=15 2 0 S +R t 1924 1927 - May Su>=1 2 1 D +R t 1927 1937 - S Su>=25 2 0 S +R t 1928 1937 - Ap Su>=25 2 1 D +R t 1938 1940 - Ap lastSu 2 1 D +R t 1938 1939 - S lastSu 2 0 S +R t 1945 1948 - S lastSu 2 0 S +R t 1946 1973 - Ap lastSu 2 1 D +R t 1949 1950 - N lastSu 2 0 S +R t 1951 1956 - S lastSu 2 0 S +R t 1957 1973 - O lastSu 2 0 S +R W 1916 o - Ap 23 0 1 D +R W 1916 o - S 17 0 0 S +R W 1918 o - Ap 14 2 1 D +R W 1918 o - O 27 2 0 S +R W 1937 o - May 16 2 1 D +R W 1937 o - S 26 2 0 S +R W 1942 o - F 9 2 1 W +R W 1945 o - Au 14 23u 1 P +R W 1945 o - S lastSu 2 0 S +R W 1946 o - May 12 2 1 D +R W 1946 o - O 13 2 0 S +R W 1947 1949 - Ap lastSu 2 1 D +R W 1947 1949 - S lastSu 2 0 S +R W 1950 o - May 1 2 1 D +R W 1950 o - S 30 2 0 S +R W 1951 1960 - Ap lastSu 2 1 D +R W 1951 1958 - S lastSu 2 0 S +R W 1959 o - O lastSu 2 0 S +R W 1960 o - S lastSu 2 0 S +R W 1963 o - Ap lastSu 2 1 D +R W 1963 o - S 22 2 0 S +R W 1966 1986 - Ap lastSu 2s 1 D +R W 1966 2005 - O lastSu 2s 0 S +R W 1987 2005 - Ap Su>=1 2s 1 D +R r 1918 o - Ap 14 2 1 D +R r 1918 o - O 27 2 0 S +R r 1930 1934 - May Su>=1 0 1 D +R r 1930 1934 - O Su>=1 0 0 S +R r 1937 1941 - Ap Su>=8 0 1 D +R r 1937 o - O Su>=8 0 0 S +R r 1938 o - O Su>=1 0 0 S +R r 1939 1941 - O Su>=8 0 0 S +R r 1942 o - F 9 2 1 W +R r 1945 o - Au 14 23u 1 P +R r 1945 o - S lastSu 2 0 S +R r 1946 o - Ap Su>=8 2 1 D +R r 1946 o - O Su>=8 2 0 S +R r 1947 1957 - Ap lastSu 2 1 D +R r 1947 1957 - S lastSu 2 0 S +R r 1959 o - Ap lastSu 2 1 D +R r 1959 o - O lastSu 2 0 S +R Sw 1957 o - Ap lastSu 2 1 D +R Sw 1957 o - O lastSu 2 0 S +R Sw 1959 1961 - Ap lastSu 2 1 D +R Sw 1959 o - O lastSu 2 0 S +R Sw 1960 1961 - S lastSu 2 0 S +R Ed 1918 1919 - Ap Su>=8 2 1 D +R Ed 1918 o - O 27 2 0 S +R Ed 1919 o - May 27 2 0 S +R Ed 1920 1923 - Ap lastSu 2 1 D +R Ed 1920 o - O lastSu 2 0 S +R Ed 1921 1923 - S lastSu 2 0 S +R Ed 1942 o - F 9 2 1 W +R Ed 1945 o - Au 14 23u 1 P +R Ed 1945 o - S lastSu 2 0 S +R Ed 1947 o - Ap lastSu 2 1 D +R Ed 1947 o - S lastSu 2 0 S +R Ed 1972 1986 - Ap lastSu 2 1 D +R Ed 1972 2006 - O lastSu 2 0 S +R Va 1918 o - Ap 14 2 1 D +R Va 1918 o - O 27 2 0 S +R Va 1942 o - F 9 2 1 W +R Va 1945 o - Au 14 23u 1 P +R Va 1945 o - S 30 2 0 S +R Va 1946 1986 - Ap lastSu 2 1 D +R Va 1946 o - S 29 2 0 S +R Va 1947 1961 - S lastSu 2 0 S +R Va 1962 2006 - O lastSu 2 0 S +R Y 1918 o - Ap 14 2 1 D +R Y 1918 o - O 27 2 0 S +R Y 1919 o - May 25 2 1 D +R Y 1919 o - N 1 0 0 S +R Y 1942 o - F 9 2 1 W +R Y 1945 o - Au 14 23u 1 P +R Y 1945 o - S 30 2 0 S +R Y 1972 1986 - Ap lastSu 2 1 D +R Y 1972 2006 - O lastSu 2 0 S +R Y 1987 2006 - Ap Su>=1 2 1 D +R Yu 1965 o - Ap lastSu 0 2 DD +R Yu 1965 o - O lastSu 2 0 S +R m 1931 o - Ap 30 0 1 D +R m 1931 o - O 1 0 0 S +R m 1939 o - F 5 0 1 D +R m 1939 o - Jun 25 0 0 S +R m 1940 o - D 9 0 1 D +R m 1941 o - Ap 1 0 0 S +R m 1943 o - D 16 0 1 W +R m 1944 o - May 1 0 0 S +R m 1950 o - F 12 0 1 D +R m 1950 o - Jul 30 0 0 S +R m 1996 2000 - Ap Su>=1 2 1 D +R m 1996 2000 - O lastSu 2 0 S +R m 2001 o - May Su>=1 2 1 D +R m 2001 o - S lastSu 2 0 S +R m 2002 2022 - Ap Su>=1 2 1 D +R m 2002 2022 - O lastSu 2 0 S +R BB 1942 o - Ap 19 5u 1 D +R BB 1942 o - Au 31 6u 0 S +R BB 1943 o - May 2 5u 1 D +R BB 1943 o - S 5 6u 0 S +R BB 1944 o - Ap 10 5u 0:30 - +R BB 1944 o - S 10 6u 0 S +R BB 1977 o - Jun 12 2 1 D +R BB 1977 1978 - O Su>=1 2 0 S +R BB 1978 1980 - Ap Su>=15 2 1 D +R BB 1979 o - S 30 2 0 S +R BB 1980 o - S 25 2 0 S +R BZ 1918 1941 - O Sa>=1 24 0:30 -0530 +R BZ 1919 1942 - F Sa>=8 24 0 CST +R BZ 1942 o - Jun 27 24 1 CWT +R BZ 1945 o - Au 14 23u 1 CPT +R BZ 1945 o - D 15 24 0 CST +R BZ 1947 1967 - O Sa>=1 24 0:30 -0530 +R BZ 1948 1968 - F Sa>=8 24 0 CST +R BZ 1973 o - D 5 0 1 CDT +R BZ 1974 o - F 9 0 0 CST +R BZ 1982 o - D 18 0 1 CDT +R BZ 1983 o - F 12 0 0 CST +R Be 1917 o - Ap 5 24 1 - +R Be 1917 o - S 30 24 0 - +R Be 1918 o - Ap 13 24 1 - +R Be 1918 o - S 15 24 0 S +R Be 1942 o - Ja 11 2 1 D +R Be 1942 o - O 18 2 0 S +R Be 1943 o - Mar 21 2 1 D +R Be 1943 o - O 31 2 0 S +R Be 1944 1945 - Mar Su>=8 2 1 D +R Be 1944 1945 - N Su>=1 2 0 S +R Be 1947 o - May Su>=15 2 1 D +R Be 1947 o - S Su>=8 2 0 S +R Be 1948 1952 - May Su>=22 2 1 D +R Be 1948 1952 - S Su>=1 2 0 S +R Be 1956 o - May Su>=22 2 1 D +R Be 1956 o - O lastSu 2 0 S +R CR 1979 1980 - F lastSu 0 1 D +R CR 1979 1980 - Jun Su>=1 0 0 S +R CR 1991 1992 - Ja Sa>=15 0 1 D +R CR 1991 o - Jul 1 0 0 S +R CR 1992 o - Mar 15 0 0 S +R Q 1928 o - Jun 10 0 1 D +R Q 1928 o - O 10 0 0 S +R Q 1940 1942 - Jun Su>=1 0 1 D +R Q 1940 1942 - S Su>=1 0 0 S +R Q 1945 1946 - Jun Su>=1 0 1 D +R Q 1945 1946 - S Su>=1 0 0 S +R Q 1965 o - Jun 1 0 1 D +R Q 1965 o - S 30 0 0 S +R Q 1966 o - May 29 0 1 D +R Q 1966 o - O 2 0 0 S +R Q 1967 o - Ap 8 0 1 D +R Q 1967 1968 - S Su>=8 0 0 S +R Q 1968 o - Ap 14 0 1 D +R Q 1969 1977 - Ap lastSu 0 1 D +R Q 1969 1971 - O lastSu 0 0 S +R Q 1972 1974 - O 8 0 0 S +R Q 1975 1977 - O lastSu 0 0 S +R Q 1978 o - May 7 0 1 D +R Q 1978 1990 - O Su>=8 0 0 S +R Q 1979 1980 - Mar Su>=15 0 1 D +R Q 1981 1985 - May Su>=5 0 1 D +R Q 1986 1989 - Mar Su>=14 0 1 D +R Q 1990 1997 - Ap Su>=1 0 1 D +R Q 1991 1995 - O Su>=8 0s 0 S +R Q 1996 o - O 6 0s 0 S +R Q 1997 o - O 12 0s 0 S +R Q 1998 1999 - Mar lastSu 0s 1 D +R Q 1998 2003 - O lastSu 0s 0 S +R Q 2000 2003 - Ap Su>=1 0s 1 D +R Q 2004 o - Mar lastSu 0s 1 D +R Q 2006 2010 - O lastSu 0s 0 S +R Q 2007 o - Mar Su>=8 0s 1 D +R Q 2008 o - Mar Su>=15 0s 1 D +R Q 2009 2010 - Mar Su>=8 0s 1 D +R Q 2011 o - Mar Su>=15 0s 1 D +R Q 2011 o - N 13 0s 0 S +R Q 2012 o - Ap 1 0s 1 D +R Q 2012 ma - N Su>=1 0s 0 S +R Q 2013 ma - Mar Su>=8 0s 1 D +R DO 1966 o - O 30 0 1 EDT +R DO 1967 o - F 28 0 0 EST +R DO 1969 1973 - O lastSu 0 0:30 -0430 +R DO 1970 o - F 21 0 0 EST +R DO 1971 o - Ja 20 0 0 EST +R DO 1972 1974 - Ja 21 0 0 EST +R SV 1987 1988 - May Su>=1 0 1 D +R SV 1987 1988 - S lastSu 0 0 S +R GT 1973 o - N 25 0 1 D +R GT 1974 o - F 24 0 0 S +R GT 1983 o - May 21 0 1 D +R GT 1983 o - S 22 0 0 S +R GT 1991 o - Mar 23 0 1 D +R GT 1991 o - S 7 0 0 S +R GT 2006 o - Ap 30 0 1 D +R GT 2006 o - O 1 0 0 S +R HT 1983 o - May 8 0 1 D +R HT 1984 1987 - Ap lastSu 0 1 D +R HT 1983 1987 - O lastSu 0 0 S +R HT 1988 1997 - Ap Su>=1 1s 1 D +R HT 1988 1997 - O lastSu 1s 0 S +R HT 2005 2006 - Ap Su>=1 0 1 D +R HT 2005 2006 - O lastSu 0 0 S +R HT 2012 2015 - Mar Su>=8 2 1 D +R HT 2012 2015 - N Su>=1 2 0 S +R HT 2017 ma - Mar Su>=8 2 1 D +R HT 2017 ma - N Su>=1 2 0 S +R HN 1987 1988 - May Su>=1 0 1 D +R HN 1987 1988 - S lastSu 0 0 S +R HN 2006 o - May Su>=1 0 1 D +R HN 2006 o - Au M>=1 0 0 S +R NI 1979 1980 - Mar Su>=16 0 1 D +R NI 1979 1980 - Jun M>=23 0 0 S +R NI 2005 o - Ap 10 0 1 D +R NI 2005 o - O Su>=1 0 0 S +R NI 2006 o - Ap 30 2 1 D +R NI 2006 o - O Su>=1 1 0 S +R A 1930 o - D 1 0 1 - +R A 1931 o - Ap 1 0 0 - +R A 1931 o - O 15 0 1 - +R A 1932 1940 - Mar 1 0 0 - +R A 1932 1939 - N 1 0 1 - +R A 1940 o - Jul 1 0 1 - +R A 1941 o - Jun 15 0 0 - +R A 1941 o - O 15 0 1 - +R A 1943 o - Au 1 0 0 - +R A 1943 o - O 15 0 1 - +R A 1946 o - Mar 1 0 0 - +R A 1946 o - O 1 0 1 - +R A 1963 o - O 1 0 0 - +R A 1963 o - D 15 0 1 - +R A 1964 1966 - Mar 1 0 0 - +R A 1964 1966 - O 15 0 1 - +R A 1967 o - Ap 2 0 0 - +R A 1967 1968 - O Su>=1 0 1 - +R A 1968 1969 - Ap Su>=1 0 0 - +R A 1974 o - Ja 23 0 1 - +R A 1974 o - May 1 0 0 - +R A 1988 o - D 1 0 1 - +R A 1989 1993 - Mar Su>=1 0 0 - +R A 1989 1992 - O Su>=15 0 1 - +R A 1999 o - O Su>=1 0 1 - +R A 2000 o - Mar 3 0 0 - +R A 2007 o - D 30 0 1 - +R A 2008 2009 - Mar Su>=15 0 0 - +R A 2008 o - O Su>=15 0 1 - +R Sa 2008 2009 - Mar Su>=8 0 0 - +R Sa 2007 2008 - O Su>=8 0 1 - +R B 1931 o - O 3 11 1 - +R B 1932 1933 - Ap 1 0 0 - +R B 1932 o - O 3 0 1 - +R B 1949 1952 - D 1 0 1 - +R B 1950 o - Ap 16 1 0 - +R B 1951 1952 - Ap 1 0 0 - +R B 1953 o - Mar 1 0 0 - +R B 1963 o - D 9 0 1 - +R B 1964 o - Mar 1 0 0 - +R B 1965 o - Ja 31 0 1 - +R B 1965 o - Mar 31 0 0 - +R B 1965 o - D 1 0 1 - +R B 1966 1968 - Mar 1 0 0 - +R B 1966 1967 - N 1 0 1 - +R B 1985 o - N 2 0 1 - +R B 1986 o - Mar 15 0 0 - +R B 1986 o - O 25 0 1 - +R B 1987 o - F 14 0 0 - +R B 1987 o - O 25 0 1 - +R B 1988 o - F 7 0 0 - +R B 1988 o - O 16 0 1 - +R B 1989 o - Ja 29 0 0 - +R B 1989 o - O 15 0 1 - +R B 1990 o - F 11 0 0 - +R B 1990 o - O 21 0 1 - +R B 1991 o - F 17 0 0 - +R B 1991 o - O 20 0 1 - +R B 1992 o - F 9 0 0 - +R B 1992 o - O 25 0 1 - +R B 1993 o - Ja 31 0 0 - +R B 1993 1995 - O Su>=11 0 1 - +R B 1994 1995 - F Su>=15 0 0 - +R B 1996 o - F 11 0 0 - +R B 1996 o - O 6 0 1 - +R B 1997 o - F 16 0 0 - +R B 1997 o - O 6 0 1 - +R B 1998 o - Mar 1 0 0 - +R B 1998 o - O 11 0 1 - +R B 1999 o - F 21 0 0 - +R B 1999 o - O 3 0 1 - +R B 2000 o - F 27 0 0 - +R B 2000 2001 - O Su>=8 0 1 - +R B 2001 2006 - F Su>=15 0 0 - +R B 2002 o - N 3 0 1 - +R B 2003 o - O 19 0 1 - +R B 2004 o - N 2 0 1 - +R B 2005 o - O 16 0 1 - +R B 2006 o - N 5 0 1 - +R B 2007 o - F 25 0 0 - +R B 2007 o - O Su>=8 0 1 - +R B 2008 2017 - O Su>=15 0 1 - +R B 2008 2011 - F Su>=15 0 0 - +R B 2012 o - F Su>=22 0 0 - +R B 2013 2014 - F Su>=15 0 0 - +R B 2015 o - F Su>=22 0 0 - +R B 2016 2019 - F Su>=15 0 0 - +R B 2018 o - N Su>=1 0 1 - +R x 1927 1931 - S 1 0 1 - +R x 1928 1932 - Ap 1 0 0 - +R x 1968 o - N 3 4u 1 - +R x 1969 o - Mar 30 3u 0 - +R x 1969 o - N 23 4u 1 - +R x 1970 o - Mar 29 3u 0 - +R x 1971 o - Mar 14 3u 0 - +R x 1970 1972 - O Su>=9 4u 1 - +R x 1972 1986 - Mar Su>=9 3u 0 - +R x 1973 o - S 30 4u 1 - +R x 1974 1987 - O Su>=9 4u 1 - +R x 1987 o - Ap 12 3u 0 - +R x 1988 1990 - Mar Su>=9 3u 0 - +R x 1988 1989 - O Su>=9 4u 1 - +R x 1990 o - S 16 4u 1 - +R x 1991 1996 - Mar Su>=9 3u 0 - +R x 1991 1997 - O Su>=9 4u 1 - +R x 1997 o - Mar 30 3u 0 - +R x 1998 o - Mar Su>=9 3u 0 - +R x 1998 o - S 27 4u 1 - +R x 1999 o - Ap 4 3u 0 - +R x 1999 2010 - O Su>=9 4u 1 - +R x 2000 2007 - Mar Su>=9 3u 0 - +R x 2008 o - Mar 30 3u 0 - +R x 2009 o - Mar Su>=9 3u 0 - +R x 2010 o - Ap Su>=1 3u 0 - +R x 2011 o - May Su>=2 3u 0 - +R x 2011 o - Au Su>=16 4u 1 - +R x 2012 2014 - Ap Su>=23 3u 0 - +R x 2012 2014 - S Su>=2 4u 1 - +R x 2016 2018 - May Su>=9 3u 0 - +R x 2016 2018 - Au Su>=9 4u 1 - +R x 2019 ma - Ap Su>=2 3u 0 - +R x 2019 2021 - S Su>=2 4u 1 - +R x 2022 o - S Su>=9 4u 1 - +R x 2023 ma - S Su>=2 4u 1 - +R CO 1992 o - May 3 0 1 - +R CO 1993 o - F 6 24 0 - +R EC 1992 o - N 28 0 1 - +R EC 1993 o - F 5 0 0 - +R FK 1937 1938 - S lastSu 0 1 - +R FK 1938 1942 - Mar Su>=19 0 0 - +R FK 1939 o - O 1 0 1 - +R FK 1940 1942 - S lastSu 0 1 - +R FK 1943 o - Ja 1 0 0 - +R FK 1983 o - S lastSu 0 1 - +R FK 1984 1985 - Ap lastSu 0 0 - +R FK 1984 o - S 16 0 1 - +R FK 1985 2000 - S Su>=9 0 1 - +R FK 1986 2000 - Ap Su>=16 0 0 - +R FK 2001 2010 - Ap Su>=15 2 0 - +R FK 2001 2010 - S Su>=1 2 1 - +R y 1975 1988 - O 1 0 1 - +R y 1975 1978 - Mar 1 0 0 - +R y 1979 1991 - Ap 1 0 0 - +R y 1989 o - O 22 0 1 - +R y 1990 o - O 1 0 1 - +R y 1991 o - O 6 0 1 - +R y 1992 o - Mar 1 0 0 - +R y 1992 o - O 5 0 1 - +R y 1993 o - Mar 31 0 0 - +R y 1993 1995 - O 1 0 1 - +R y 1994 1995 - F lastSu 0 0 - +R y 1996 o - Mar 1 0 0 - +R y 1996 2001 - O Su>=1 0 1 - +R y 1997 o - F lastSu 0 0 - +R y 1998 2001 - Mar Su>=1 0 0 - +R y 2002 2004 - Ap Su>=1 0 0 - +R y 2002 2003 - S Su>=1 0 1 - +R y 2004 2009 - O Su>=15 0 1 - +R y 2005 2009 - Mar Su>=8 0 0 - +R y 2010 2024 - O Su>=1 0 1 - +R y 2010 2012 - Ap Su>=8 0 0 - +R y 2013 2024 - Mar Su>=22 0 0 - +R PE 1938 o - Ja 1 0 1 - +R PE 1938 o - Ap 1 0 0 - +R PE 1938 1939 - S lastSu 0 1 - +R PE 1939 1940 - Mar Su>=24 0 0 - +R PE 1986 1987 - Ja 1 0 1 - +R PE 1986 1987 - Ap 1 0 0 - +R PE 1990 o - Ja 1 0 1 - +R PE 1990 o - Ap 1 0 0 - +R PE 1994 o - Ja 1 0 1 - +R PE 1994 o - Ap 1 0 0 - +R U 1923 1925 - O 1 0 0:30 - +R U 1924 1926 - Ap 1 0 0 - +R U 1933 1938 - O lastSu 0 0:30 - +R U 1934 1941 - Mar lastSa 24 0 - +R U 1939 o - O 1 0 0:30 - +R U 1940 o - O 27 0 0:30 - +R U 1941 o - Au 1 0 0:30 - +R U 1942 o - D 14 0 0:30 - +R U 1943 o - Mar 14 0 0 - +R U 1959 o - May 24 0 0:30 - +R U 1959 o - N 15 0 0 - +R U 1960 o - Ja 17 0 1 - +R U 1960 o - Mar 6 0 0 - +R U 1965 o - Ap 4 0 1 - +R U 1965 o - S 26 0 0 - +R U 1968 o - May 27 0 0:30 - +R U 1968 o - D 1 0 0 - +R U 1970 o - Ap 25 0 1 - +R U 1970 o - Jun 14 0 0 - +R U 1972 o - Ap 23 0 1 - +R U 1972 o - Jul 16 0 0 - +R U 1974 o - Ja 13 0 1:30 - +R U 1974 o - Mar 10 0 0:30 - +R U 1974 o - S 1 0 0 - +R U 1974 o - D 22 0 1 - +R U 1975 o - Mar 30 0 0 - +R U 1976 o - D 19 0 1 - +R U 1977 o - Mar 6 0 0 - +R U 1977 o - D 4 0 1 - +R U 1978 1979 - Mar Su>=1 0 0 - +R U 1978 o - D 17 0 1 - +R U 1979 o - Ap 29 0 1 - +R U 1980 o - Mar 16 0 0 - +R U 1987 o - D 14 0 1 - +R U 1988 o - F 28 0 0 - +R U 1988 o - D 11 0 1 - +R U 1989 o - Mar 5 0 0 - +R U 1989 o - O 29 0 1 - +R U 1990 o - F 25 0 0 - +R U 1990 1991 - O Su>=21 0 1 - +R U 1991 1992 - Mar Su>=1 0 0 - +R U 1992 o - O 18 0 1 - +R U 1993 o - F 28 0 0 - +R U 2004 o - S 19 0 1 - +R U 2005 o - Mar 27 2 0 - +R U 2005 o - O 9 2 1 - +R U 2006 2015 - Mar Su>=8 2 0 - +R U 2006 2014 - O Su>=1 2 1 - +Z Africa/Abidjan -0:16:8 - LMT 1912 +0 - GMT +Z Africa/Algiers 0:12:12 - LMT 1891 Mar 16 +0:9:21 - PMT 1911 Mar 11 +0 d WE%sT 1940 F 25 2 +1 d CE%sT 1946 O 7 +0 - WET 1956 Ja 29 +1 - CET 1963 Ap 14 +0 d WE%sT 1977 O 21 +1 d CE%sT 1979 O 26 +0 d WE%sT 1981 May +1 - CET +Z Africa/Bissau -1:2:20 - LMT 1912 Ja 1 1u +-1 - %z 1975 +0 - GMT +Z Africa/Cairo 2:5:9 - LMT 1900 O +2 K EE%sT +Z Africa/Casablanca -0:30:20 - LMT 1913 O 26 +0 M %z 1984 Mar 16 +1 - %z 1986 +0 M %z 2018 O 28 3 +1 M %z +Z Africa/Ceuta -0:21:16 - LMT 1901 Ja 1 0u +0 - WET 1918 May 6 23 +0 1 WEST 1918 O 7 23 +0 - WET 1924 +0 s WE%sT 1929 +0 - WET 1967 +0 Sp WE%sT 1984 Mar 16 +1 - CET 1986 +1 E CE%sT +Z Africa/El_Aaiun -0:52:48 - LMT 1934 +-1 - %z 1976 Ap 14 +0 M %z 2018 O 28 3 +1 M %z +Z Africa/Johannesburg 1:52 - LMT 1892 F 8 +1:30 - SAST 1903 Mar +2 SA SAST +Z Africa/Juba 2:6:28 - LMT 1931 +2 SD CA%sT 2000 Ja 15 12 +3 - EAT 2021 F +2 - CAT +Z Africa/Khartoum 2:10:8 - LMT 1931 +2 SD CA%sT 2000 Ja 15 12 +3 - EAT 2017 N +2 - CAT +Z Africa/Lagos 0:13:35 - LMT 1905 Jul +0 - GMT 1908 Jul +0:13:35 - LMT 1914 +0:30 - %z 1919 S +1 - WAT +Z Africa/Maputo 2:10:18 - LMT 1909 +2 - CAT +Z Africa/Monrovia -0:43:8 - LMT 1882 +-0:43:8 - MMT 1919 Mar +-0:44:30 - MMT 1972 Ja 7 +0 - GMT +Z Africa/Nairobi 2:27:16 - LMT 1908 May +2:30 - %z 1928 Jun 30 24 +3 - EAT 1930 Ja 4 24 +2:30 - %z 1936 D 31 24 +2:45 - %z 1942 Jul 31 24 +3 - EAT +Z Africa/Ndjamena 1:0:12 - LMT 1912 +1 - WAT 1979 O 14 +1 1 WAST 1980 Mar 8 +1 - WAT +Z Africa/Sao_Tome 0:26:56 - LMT 1884 +-0:36:45 - LMT 1912 Ja 1 0u +0 - GMT 2018 Ja 1 1 +1 - WAT 2019 Ja 1 2 +0 - GMT +Z Africa/Tripoli 0:52:44 - LMT 1920 +1 L CE%sT 1959 +2 - EET 1982 +1 L CE%sT 1990 May 4 +2 - EET 1996 S 30 +1 L CE%sT 1997 O 4 +2 - EET 2012 N 10 2 +1 L CE%sT 2013 O 25 2 +2 - EET +Z Africa/Tunis 0:40:44 - LMT 1881 May 12 +0:9:21 - PMT 1911 Mar 11 +1 n CE%sT +Z Africa/Windhoek 1:8:24 - LMT 1892 F 8 +1:30 - %z 1903 Mar +2 - SAST 1942 S 20 2 +2 1 SAST 1943 Mar 21 2 +2 - SAST 1990 Mar 21 +2 NA %s +Z America/Adak 12:13:22 - LMT 1867 O 19 12:44:35 +-11:46:38 - LMT 1900 Au 20 12 +-11 - NST 1942 +-11 u N%sT 1946 +-11 - NST 1967 Ap +-11 - BST 1969 +-11 u B%sT 1983 O 30 2 +-10 u AH%sT 1983 N 30 +-10 u H%sT +Z America/Anchorage 14:0:24 - LMT 1867 O 19 14:31:37 +-9:59:36 - LMT 1900 Au 20 12 +-10 - AST 1942 +-10 u A%sT 1967 Ap +-10 - AHST 1969 +-10 u AH%sT 1983 O 30 2 +-9 u Y%sT 1983 N 30 +-9 u AK%sT +Z America/Araguaina -3:12:48 - LMT 1914 +-3 B %z 1990 S 17 +-3 - %z 1995 S 14 +-3 B %z 2003 S 24 +-3 - %z 2012 O 21 +-3 B %z 2013 S +-3 - %z +Z America/Argentina/Buenos_Aires -3:53:48 - LMT 1894 O 31 +-4:16:48 - CMT 1920 May +-4 - %z 1930 D +-4 A %z 1969 O 5 +-3 A %z 1999 O 3 +-4 A %z 2000 Mar 3 +-3 A %z +Z America/Argentina/Catamarca -4:23:8 - LMT 1894 O 31 +-4:16:48 - CMT 1920 May +-4 - %z 1930 D +-4 A %z 1969 O 5 +-3 A %z 1991 Mar 3 +-4 - %z 1991 O 20 +-3 A %z 1999 O 3 +-4 A %z 2000 Mar 3 +-3 - %z 2004 Jun +-4 - %z 2004 Jun 20 +-3 A %z 2008 O 18 +-3 - %z +Z America/Argentina/Cordoba -4:16:48 - LMT 1894 O 31 +-4:16:48 - CMT 1920 May +-4 - %z 1930 D +-4 A %z 1969 O 5 +-3 A %z 1991 Mar 3 +-4 - %z 1991 O 20 +-3 A %z 1999 O 3 +-4 A %z 2000 Mar 3 +-3 A %z +Z America/Argentina/Jujuy -4:21:12 - LMT 1894 O 31 +-4:16:48 - CMT 1920 May +-4 - %z 1930 D +-4 A %z 1969 O 5 +-3 A %z 1990 Mar 4 +-4 - %z 1990 O 28 +-4 1 %z 1991 Mar 17 +-4 - %z 1991 O 6 +-3 1 %z 1992 +-3 A %z 1999 O 3 +-4 A %z 2000 Mar 3 +-3 A %z 2008 O 18 +-3 - %z +Z America/Argentina/La_Rioja -4:27:24 - LMT 1894 O 31 +-4:16:48 - CMT 1920 May +-4 - %z 1930 D +-4 A %z 1969 O 5 +-3 A %z 1991 Mar +-4 - %z 1991 May 7 +-3 A %z 1999 O 3 +-4 A %z 2000 Mar 3 +-3 - %z 2004 Jun +-4 - %z 2004 Jun 20 +-3 A %z 2008 O 18 +-3 - %z +Z America/Argentina/Mendoza -4:35:16 - LMT 1894 O 31 +-4:16:48 - CMT 1920 May +-4 - %z 1930 D +-4 A %z 1969 O 5 +-3 A %z 1990 Mar 4 +-4 - %z 1990 O 15 +-4 1 %z 1991 Mar +-4 - %z 1991 O 15 +-4 1 %z 1992 Mar +-4 - %z 1992 O 18 +-3 A %z 1999 O 3 +-4 A %z 2000 Mar 3 +-3 - %z 2004 May 23 +-4 - %z 2004 S 26 +-3 A %z 2008 O 18 +-3 - %z +Z America/Argentina/Rio_Gallegos -4:36:52 - LMT 1894 O 31 +-4:16:48 - CMT 1920 May +-4 - %z 1930 D +-4 A %z 1969 O 5 +-3 A %z 1999 O 3 +-4 A %z 2000 Mar 3 +-3 - %z 2004 Jun +-4 - %z 2004 Jun 20 +-3 A %z 2008 O 18 +-3 - %z +Z America/Argentina/Salta -4:21:40 - LMT 1894 O 31 +-4:16:48 - CMT 1920 May +-4 - %z 1930 D +-4 A %z 1969 O 5 +-3 A %z 1991 Mar 3 +-4 - %z 1991 O 20 +-3 A %z 1999 O 3 +-4 A %z 2000 Mar 3 +-3 A %z 2008 O 18 +-3 - %z +Z America/Argentina/San_Juan -4:34:4 - LMT 1894 O 31 +-4:16:48 - CMT 1920 May +-4 - %z 1930 D +-4 A %z 1969 O 5 +-3 A %z 1991 Mar +-4 - %z 1991 May 7 +-3 A %z 1999 O 3 +-4 A %z 2000 Mar 3 +-3 - %z 2004 May 31 +-4 - %z 2004 Jul 25 +-3 A %z 2008 O 18 +-3 - %z +Z America/Argentina/San_Luis -4:25:24 - LMT 1894 O 31 +-4:16:48 - CMT 1920 May +-4 - %z 1930 D +-4 A %z 1969 O 5 +-3 A %z 1990 +-3 1 %z 1990 Mar 14 +-4 - %z 1990 O 15 +-4 1 %z 1991 Mar +-4 - %z 1991 Jun +-3 - %z 1999 O 3 +-4 1 %z 2000 Mar 3 +-3 - %z 2004 May 31 +-4 - %z 2004 Jul 25 +-3 A %z 2008 Ja 21 +-4 Sa %z 2009 O 11 +-3 - %z +Z America/Argentina/Tucuman -4:20:52 - LMT 1894 O 31 +-4:16:48 - CMT 1920 May +-4 - %z 1930 D +-4 A %z 1969 O 5 +-3 A %z 1991 Mar 3 +-4 - %z 1991 O 20 +-3 A %z 1999 O 3 +-4 A %z 2000 Mar 3 +-3 - %z 2004 Jun +-4 - %z 2004 Jun 13 +-3 A %z +Z America/Argentina/Ushuaia -4:33:12 - LMT 1894 O 31 +-4:16:48 - CMT 1920 May +-4 - %z 1930 D +-4 A %z 1969 O 5 +-3 A %z 1999 O 3 +-4 A %z 2000 Mar 3 +-3 - %z 2004 May 30 +-4 - %z 2004 Jun 20 +-3 A %z 2008 O 18 +-3 - %z +Z America/Asuncion -3:50:40 - LMT 1890 +-3:50:40 - AMT 1931 O 10 +-4 - %z 1972 O +-3 - %z 1974 Ap +-4 y %z 2024 O 15 +-3 - %z +Z America/Bahia -2:34:4 - LMT 1914 +-3 B %z 2003 S 24 +-3 - %z 2011 O 16 +-3 B %z 2012 O 21 +-3 - %z +Z America/Bahia_Banderas -7:1 - LMT 1922 Ja 1 7u +-7 - MST 1927 Jun 10 +-6 - CST 1930 N 15 +-7 m M%sT 1932 Ap +-6 - CST 1942 Ap 24 +-7 - MST 1970 +-7 m M%sT 2010 Ap 4 2 +-6 m C%sT +Z America/Barbados -3:58:29 - LMT 1911 Au 28 +-4 BB A%sT 1944 +-4 BB AST/-0330 1945 +-4 BB A%sT +Z America/Belem -3:13:56 - LMT 1914 +-3 B %z 1988 S 12 +-3 - %z +Z America/Belize -5:52:48 - LMT 1912 Ap +-6 BZ %s +Z America/Boa_Vista -4:2:40 - LMT 1914 +-4 B %z 1988 S 12 +-4 - %z 1999 S 30 +-4 B %z 2000 O 15 +-4 - %z +Z America/Bogota -4:56:16 - LMT 1884 Mar 13 +-4:56:16 - BMT 1914 N 23 +-5 CO %z +Z America/Boise -7:44:49 - LMT 1883 N 18 20u +-8 u P%sT 1923 May 13 2 +-7 u M%sT 1974 +-7 - MST 1974 F 3 2 +-7 u M%sT +Z America/Cambridge_Bay 0 - -00 1920 +-7 Y M%sT 1999 O 31 2 +-6 C C%sT 2000 O 29 2 +-5 - EST 2000 N 5 +-6 - CST 2001 Ap 1 3 +-7 C M%sT +Z America/Campo_Grande -3:38:28 - LMT 1914 +-4 B %z +Z America/Cancun -5:47:4 - LMT 1922 Ja 1 6u +-6 - CST 1981 D 26 2 +-5 - EST 1983 Ja 4 +-6 m C%sT 1997 O 26 2 +-5 m E%sT 1998 Au 2 2 +-6 m C%sT 2015 F 1 2 +-5 - EST +Z America/Caracas -4:27:44 - LMT 1890 +-4:27:40 - CMT 1912 F 12 +-4:30 - %z 1965 +-4 - %z 2007 D 9 3 +-4:30 - %z 2016 May 1 2:30 +-4 - %z +Z America/Cayenne -3:29:20 - LMT 1911 Jul +-4 - %z 1967 O +-3 - %z +Z America/Chicago -5:50:36 - LMT 1883 N 18 18u +-6 u C%sT 1920 +-6 Ch C%sT 1936 Mar 1 2 +-5 - EST 1936 N 15 2 +-6 Ch C%sT 1942 +-6 u C%sT 1946 +-6 Ch C%sT 1967 +-6 u C%sT +Z America/Chihuahua -7:4:20 - LMT 1922 Ja 1 7u +-7 - MST 1927 Jun 10 +-6 - CST 1930 N 15 +-7 m M%sT 1932 Ap +-6 - CST 1996 +-6 m C%sT 1998 +-6 - CST 1998 Ap Su>=1 3 +-7 m M%sT 2022 O 30 2 +-6 - CST +Z America/Ciudad_Juarez -7:5:56 - LMT 1922 Ja 1 7u +-7 - MST 1927 Jun 10 +-6 - CST 1930 N 15 +-7 m M%sT 1932 Ap +-6 - CST 1996 +-6 m C%sT 1998 +-6 - CST 1998 Ap Su>=1 3 +-7 m M%sT 2010 +-7 u M%sT 2022 O 30 2 +-6 - CST 2022 N 30 +-7 u M%sT +Z America/Costa_Rica -5:36:13 - LMT 1890 +-5:36:13 - SJMT 1921 Ja 15 +-6 CR C%sT +Z America/Coyhaique -4:48:16 - LMT 1890 +-4:42:45 - SMT 1910 Ja 10 +-5 - %z 1916 Jul +-4:42:45 - SMT 1918 S 10 +-4 - %z 1919 Jul +-4:42:45 - SMT 1927 S +-5 x %z 1932 S +-4 - %z 1942 Jun +-5 - %z 1942 Au +-4 - %z 1946 Au 28 24 +-5 1 %z 1947 Mar 31 24 +-5 - %z 1947 May 21 23 +-4 x %z 2025 Mar 20 +-3 - %z +Z America/Cuiaba -3:44:20 - LMT 1914 +-4 B %z 2003 S 24 +-4 - %z 2004 O +-4 B %z +Z America/Danmarkshavn -1:14:40 - LMT 1916 Jul 28 +-3 - %z 1980 Ap 6 2 +-3 E %z 1996 +0 - GMT +Z America/Dawson -9:17:40 - LMT 1900 Au 20 +-9 Y Y%sT 1965 +-9 Yu Y%sT 1973 O 28 +-8 - PST 1980 +-8 C P%sT 2020 N +-7 - MST +Z America/Dawson_Creek -8:0:56 - LMT 1884 +-8 C P%sT 1947 +-8 Va P%sT 1972 Au 30 2 +-7 - MST +Z America/Denver -6:59:56 - LMT 1883 N 18 19u +-7 u M%sT 1920 +-7 De M%sT 1942 +-7 u M%sT 1946 +-7 De M%sT 1967 +-7 u M%sT +Z America/Detroit -5:32:11 - LMT 1905 +-6 - CST 1915 May 15 2 +-5 - EST 1942 +-5 u E%sT 1946 +-5 Dt E%sT 1967 Jun 14 0:1 +-5 u E%sT 1969 +-5 - EST 1973 +-5 u E%sT 1975 +-5 - EST 1975 Ap 27 2 +-5 u E%sT +Z America/Edmonton -7:33:52 - LMT 1906 S +-7 Ed M%sT 1987 +-7 C M%sT +Z America/Eirunepe -4:39:28 - LMT 1914 +-5 B %z 1988 S 12 +-5 - %z 1993 S 28 +-5 B %z 1994 S 22 +-5 - %z 2008 Jun 24 +-4 - %z 2013 N 10 +-5 - %z +Z America/El_Salvador -5:56:48 - LMT 1921 +-6 SV C%sT +Z America/Fort_Nelson -8:10:47 - LMT 1884 +-8 Va P%sT 1946 +-8 - PST 1947 +-8 Va P%sT 1987 +-8 C P%sT 2015 Mar 8 2 +-7 - MST +Z America/Fortaleza -2:34 - LMT 1914 +-3 B %z 1990 S 17 +-3 - %z 1999 S 30 +-3 B %z 2000 O 22 +-3 - %z 2001 S 13 +-3 B %z 2002 O +-3 - %z +Z America/Glace_Bay -3:59:48 - LMT 1902 Jun 15 +-4 C A%sT 1953 +-4 H A%sT 1954 +-4 - AST 1972 +-4 H A%sT 1974 +-4 C A%sT +Z America/Goose_Bay -4:1:40 - LMT 1884 +-3:30:52 - NST 1918 +-3:30:52 C N%sT 1919 +-3:30:52 - NST 1935 Mar 30 +-3:30 - NST 1936 +-3:30 j N%sT 1942 May 11 +-3:30 C N%sT 1946 +-3:30 j N%sT 1966 Mar 15 2 +-4 j A%sT 2011 N +-4 C A%sT +Z America/Grand_Turk -4:44:32 - LMT 1890 +-5:7:10 - KMT 1912 F +-5 - EST 1979 +-5 u E%sT 2015 Mar 8 2 +-4 - AST 2018 Mar 11 3 +-5 u E%sT +Z America/Guatemala -6:2:4 - LMT 1918 O 5 +-6 GT C%sT +Z America/Guayaquil -5:19:20 - LMT 1890 +-5:14 - QMT 1931 +-5 EC %z +Z America/Guyana -3:52:39 - LMT 1911 Au +-4 - %z 1915 Mar +-3:45 - %z 1975 Au +-3 - %z 1992 Mar 29 1 +-4 - %z +Z America/Halifax -4:14:24 - LMT 1902 Jun 15 +-4 H A%sT 1918 +-4 C A%sT 1919 +-4 H A%sT 1942 F 9 2s +-4 C A%sT 1946 +-4 H A%sT 1974 +-4 C A%sT +Z America/Havana -5:29:28 - LMT 1890 +-5:29:36 - HMT 1925 Jul 19 12 +-5 Q C%sT +Z America/Hermosillo -7:23:52 - LMT 1922 Ja 1 7u +-7 - MST 1927 Jun 10 +-6 - CST 1930 N 15 +-7 m M%sT 1932 Ap +-6 - CST 1942 Ap 24 +-7 - MST 1996 +-7 m M%sT 1999 +-7 - MST +Z America/Indiana/Indianapolis -5:44:38 - LMT 1883 N 18 18u +-6 u C%sT 1920 +-6 In C%sT 1942 +-6 u C%sT 1946 +-6 In C%sT 1955 Ap 24 2 +-5 - EST 1957 S 29 2 +-6 - CST 1958 Ap 27 2 +-5 - EST 1969 +-5 u E%sT 1971 +-5 - EST 2006 +-5 u E%sT +Z America/Indiana/Knox -5:46:30 - LMT 1883 N 18 18u +-6 u C%sT 1947 +-6 St C%sT 1962 Ap 29 2 +-5 - EST 1963 O 27 2 +-6 u C%sT 1991 O 27 2 +-5 - EST 2006 Ap 2 2 +-6 u C%sT +Z America/Indiana/Marengo -5:45:23 - LMT 1883 N 18 18u +-6 u C%sT 1951 +-6 Ma C%sT 1961 Ap 30 2 +-5 - EST 1969 +-5 u E%sT 1974 Ja 6 2 +-6 1 CDT 1974 O 27 2 +-5 u E%sT 1976 +-5 - EST 2006 +-5 u E%sT +Z America/Indiana/Petersburg -5:49:7 - LMT 1883 N 18 18u +-6 u C%sT 1955 +-6 Pi C%sT 1965 Ap 25 2 +-5 - EST 1966 O 30 2 +-6 u C%sT 1977 O 30 2 +-5 - EST 2006 Ap 2 2 +-6 u C%sT 2007 N 4 2 +-5 u E%sT +Z America/Indiana/Tell_City -5:47:3 - LMT 1883 N 18 18u +-6 u C%sT 1946 +-6 Pe C%sT 1964 Ap 26 2 +-5 - EST 1967 O 29 2 +-6 u C%sT 1969 Ap 27 2 +-5 u E%sT 1971 +-5 - EST 2006 Ap 2 2 +-6 u C%sT +Z America/Indiana/Vevay -5:40:16 - LMT 1883 N 18 18u +-6 u C%sT 1954 Ap 25 2 +-5 - EST 1969 +-5 u E%sT 1973 +-5 - EST 2006 +-5 u E%sT +Z America/Indiana/Vincennes -5:50:7 - LMT 1883 N 18 18u +-6 u C%sT 1946 +-6 V C%sT 1964 Ap 26 2 +-5 - EST 1969 +-5 u E%sT 1971 +-5 - EST 2006 Ap 2 2 +-6 u C%sT 2007 N 4 2 +-5 u E%sT +Z America/Indiana/Winamac -5:46:25 - LMT 1883 N 18 18u +-6 u C%sT 1946 +-6 Pu C%sT 1961 Ap 30 2 +-5 - EST 1969 +-5 u E%sT 1971 +-5 - EST 2006 Ap 2 2 +-6 u C%sT 2007 Mar 11 2 +-5 u E%sT +Z America/Inuvik 0 - -00 1953 +-8 Y P%sT 1979 Ap lastSu 2 +-7 Y M%sT 1980 +-7 C M%sT +Z America/Iqaluit 0 - -00 1942 Au +-5 Y E%sT 1999 O 31 2 +-6 C C%sT 2000 O 29 2 +-5 C E%sT +Z America/Jamaica -5:7:10 - LMT 1890 +-5:7:10 - KMT 1912 F +-5 - EST 1974 +-5 u E%sT 1984 +-5 - EST +Z America/Juneau 15:2:19 - LMT 1867 O 19 15:33:32 +-8:57:41 - LMT 1900 Au 20 12 +-8 - PST 1942 +-8 u P%sT 1946 +-8 - PST 1969 +-8 u P%sT 1980 Ap 27 2 +-9 u Y%sT 1980 O 26 2 +-8 u P%sT 1983 O 30 2 +-9 u Y%sT 1983 N 30 +-9 u AK%sT +Z America/Kentucky/Louisville -5:43:2 - LMT 1883 N 18 18u +-6 u C%sT 1921 +-6 v C%sT 1942 +-6 u C%sT 1946 +-6 v C%sT 1961 Jul 23 2 +-5 - EST 1968 +-5 u E%sT 1974 Ja 6 2 +-6 1 CDT 1974 O 27 2 +-5 u E%sT +Z America/Kentucky/Monticello -5:39:24 - LMT 1883 N 18 18u +-6 u C%sT 1946 +-6 - CST 1968 +-6 u C%sT 2000 O 29 2 +-5 u E%sT +Z America/La_Paz -4:32:36 - LMT 1890 +-4:32:36 - CMT 1931 O 15 +-4:32:36 1 BST 1932 Mar 21 +-4 - %z +Z America/Lima -5:8:12 - LMT 1890 +-5:8:36 - LMT 1908 Jul 28 +-5 PE %z +Z America/Los_Angeles -7:52:58 - LMT 1883 N 18 20u +-8 u P%sT 1946 +-8 CA P%sT 1967 +-8 u P%sT +Z America/Maceio -2:22:52 - LMT 1914 +-3 B %z 1990 S 17 +-3 - %z 1995 O 13 +-3 B %z 1996 S 4 +-3 - %z 1999 S 30 +-3 B %z 2000 O 22 +-3 - %z 2001 S 13 +-3 B %z 2002 O +-3 - %z +Z America/Managua -5:45:8 - LMT 1890 +-5:45:12 - MMT 1934 Jun 23 +-6 - CST 1973 May +-5 - EST 1975 F 16 +-6 NI C%sT 1992 Ja 1 4 +-5 - EST 1992 S 24 +-6 - CST 1993 +-5 - EST 1997 +-6 NI C%sT +Z America/Manaus -4:0:4 - LMT 1914 +-4 B %z 1988 S 12 +-4 - %z 1993 S 28 +-4 B %z 1994 S 22 +-4 - %z +Z America/Martinique -4:4:20 - LMT 1890 +-4:4:20 - FFMT 1911 May +-4 - AST 1980 Ap 6 +-4 1 ADT 1980 S 28 +-4 - AST +Z America/Matamoros -6:30 - LMT 1922 Ja 1 6u +-6 - CST 1988 +-6 u C%sT 1989 +-6 m C%sT 2010 +-6 u C%sT +Z America/Mazatlan -7:5:40 - LMT 1922 Ja 1 7u +-7 - MST 1927 Jun 10 +-6 - CST 1930 N 15 +-7 m M%sT 1932 Ap +-6 - CST 1942 Ap 24 +-7 - MST 1970 +-7 m M%sT +Z America/Menominee -5:50:27 - LMT 1885 S 18 12 +-6 u C%sT 1946 +-6 Me C%sT 1969 Ap 27 2 +-5 - EST 1973 Ap 29 2 +-6 u C%sT +Z America/Merida -5:58:28 - LMT 1922 Ja 1 6u +-6 - CST 1981 D 26 2 +-5 - EST 1982 N 2 2 +-6 m C%sT +Z America/Metlakatla 15:13:42 - LMT 1867 O 19 15:44:55 +-8:46:18 - LMT 1900 Au 20 12 +-8 - PST 1942 +-8 u P%sT 1946 +-8 - PST 1969 +-8 u P%sT 1983 O 30 2 +-8 - PST 2015 N 1 2 +-9 u AK%sT 2018 N 4 2 +-8 - PST 2019 Ja 20 2 +-9 u AK%sT +Z America/Mexico_City -6:36:36 - LMT 1922 Ja 1 7u +-7 - MST 1927 Jun 10 +-6 - CST 1930 N 15 +-7 m M%sT 1932 Ap +-6 m C%sT 2001 S 30 2 +-6 - CST 2002 F 20 +-6 m C%sT +Z America/Miquelon -3:44:40 - LMT 1911 Jun 15 +-4 - AST 1980 May +-3 - %z 1987 +-3 C %z +Z America/Moncton -4:19:8 - LMT 1883 D 9 +-5 - EST 1902 Jun 15 +-4 C A%sT 1933 +-4 o A%sT 1942 +-4 C A%sT 1946 +-4 o A%sT 1973 +-4 C A%sT 1993 +-4 o A%sT 2007 +-4 C A%sT +Z America/Monterrey -6:41:16 - LMT 1922 Ja 1 6u +-7 - MST 1927 Jun 10 +-6 - CST 1930 N 15 +-7 m M%sT 1932 Ap +-6 - CST 1988 +-6 u C%sT 1989 +-6 m C%sT +Z America/Montevideo -3:44:51 - LMT 1908 Jun 10 +-3:44:51 - MMT 1920 May +-4 - %z 1923 O +-3:30 U %z 1942 D 14 +-3 U %z 1960 +-3 U %z 1968 +-3 U %z 1970 +-3 U %z 1974 +-3 U %z 1974 Mar 10 +-3 U %z 1974 D 22 +-3 U %z +Z America/New_York -4:56:2 - LMT 1883 N 18 17u +-5 u E%sT 1920 +-5 NY E%sT 1942 +-5 u E%sT 1946 +-5 NY E%sT 1967 +-5 u E%sT +Z America/Nome 12:58:22 - LMT 1867 O 19 13:29:35 +-11:1:38 - LMT 1900 Au 20 12 +-11 - NST 1942 +-11 u N%sT 1946 +-11 - NST 1967 Ap +-11 - BST 1969 +-11 u B%sT 1983 O 30 2 +-9 u Y%sT 1983 N 30 +-9 u AK%sT +Z America/Noronha -2:9:40 - LMT 1914 +-2 B %z 1990 S 17 +-2 - %z 1999 S 30 +-2 B %z 2000 O 15 +-2 - %z 2001 S 13 +-2 B %z 2002 O +-2 - %z +Z America/North_Dakota/Beulah -6:47:7 - LMT 1883 N 18 19u +-7 u M%sT 2010 N 7 2 +-6 u C%sT +Z America/North_Dakota/Center -6:45:12 - LMT 1883 N 18 19u +-7 u M%sT 1992 O 25 2 +-6 u C%sT +Z America/North_Dakota/New_Salem -6:45:39 - LMT 1883 N 18 19u +-7 u M%sT 2003 O 26 2 +-6 u C%sT +Z America/Nuuk -3:26:56 - LMT 1916 Jul 28 +-3 - %z 1980 Ap 6 2 +-3 E %z 2023 Mar 26 1u +-2 - %z 2023 O 29 1u +-2 E %z +Z America/Ojinaga -6:57:40 - LMT 1922 Ja 1 7u +-7 - MST 1927 Jun 10 +-6 - CST 1930 N 15 +-7 m M%sT 1932 Ap +-6 - CST 1996 +-6 m C%sT 1998 +-6 - CST 1998 Ap Su>=1 3 +-7 m M%sT 2010 +-7 u M%sT 2022 O 30 2 +-6 - CST 2022 N 30 +-6 u C%sT +Z America/Panama -5:18:8 - LMT 1890 +-5:19:36 - CMT 1908 Ap 22 +-5 - EST +Z America/Paramaribo -3:40:40 - LMT 1911 +-3:40:52 - PMT 1935 +-3:40:36 - PMT 1945 O +-3:30 - %z 1984 O +-3 - %z +Z America/Phoenix -7:28:18 - LMT 1883 N 18 19u +-7 u M%sT 1944 Ja 1 0:1 +-7 - MST 1944 Ap 1 0:1 +-7 u M%sT 1944 O 1 0:1 +-7 - MST 1967 +-7 u M%sT 1968 Mar 21 +-7 - MST +Z America/Port-au-Prince -4:49:20 - LMT 1890 +-4:49 - PPMT 1917 Ja 24 12 +-5 HT E%sT +Z America/Porto_Velho -4:15:36 - LMT 1914 +-4 B %z 1988 S 12 +-4 - %z +Z America/Puerto_Rico -4:24:25 - LMT 1899 Mar 28 12 +-4 - AST 1942 May 3 +-4 u A%sT 1946 +-4 - AST +Z America/Punta_Arenas -4:43:40 - LMT 1890 +-4:42:45 - SMT 1910 Ja 10 +-5 - %z 1916 Jul +-4:42:45 - SMT 1918 S 10 +-4 - %z 1919 Jul +-4:42:45 - SMT 1927 S +-5 x %z 1932 S +-4 - %z 1942 Jun +-5 - %z 1942 Au +-4 - %z 1946 Au 28 24 +-5 1 %z 1947 Mar 31 24 +-5 - %z 1947 May 21 23 +-4 x %z 2016 D 4 +-3 - %z +Z America/Rankin_Inlet 0 - -00 1957 +-6 Y C%sT 2000 O 29 2 +-5 - EST 2001 Ap 1 3 +-6 C C%sT +Z America/Recife -2:19:36 - LMT 1914 +-3 B %z 1990 S 17 +-3 - %z 1999 S 30 +-3 B %z 2000 O 15 +-3 - %z 2001 S 13 +-3 B %z 2002 O +-3 - %z +Z America/Regina -6:58:36 - LMT 1905 S +-7 r M%sT 1960 Ap lastSu 2 +-6 - CST +Z America/Resolute 0 - -00 1947 Au 31 +-6 Y C%sT 2000 O 29 2 +-5 - EST 2001 Ap 1 3 +-6 C C%sT 2006 O 29 2 +-5 - EST 2007 Mar 11 3 +-6 C C%sT +Z America/Rio_Branco -4:31:12 - LMT 1914 +-5 B %z 1988 S 12 +-5 - %z 2008 Jun 24 +-4 - %z 2013 N 10 +-5 - %z +Z America/Santarem -3:38:48 - LMT 1914 +-4 B %z 1988 S 12 +-4 - %z 2008 Jun 24 +-3 - %z +Z America/Santiago -4:42:45 - LMT 1890 +-4:42:45 - SMT 1910 Ja 10 +-5 - %z 1916 Jul +-4:42:45 - SMT 1918 S 10 +-4 - %z 1919 Jul +-4:42:45 - SMT 1927 S +-5 x %z 1932 S +-4 - %z 1942 Jun +-5 - %z 1942 Au +-4 - %z 1946 Jul 14 24 +-4 1 %z 1946 Au 28 24 +-5 1 %z 1947 Mar 31 24 +-5 - %z 1947 May 21 23 +-4 x %z +Z America/Santo_Domingo -4:39:36 - LMT 1890 +-4:40 - SDMT 1933 Ap 1 12 +-5 DO %s 1974 O 27 +-4 - AST 2000 O 29 2 +-5 u E%sT 2000 D 3 1 +-4 - AST +Z America/Sao_Paulo -3:6:28 - LMT 1914 +-3 B %z 1963 O 23 +-3 1 %z 1964 +-3 B %z +Z America/Scoresbysund -1:27:52 - LMT 1916 Jul 28 +-2 - %z 1980 Ap 6 2 +-2 c %z 1981 Mar 29 +-1 E %z 2024 Mar 31 +-2 E %z +Z America/Sitka 14:58:47 - LMT 1867 O 19 15:30 +-9:1:13 - LMT 1900 Au 20 12 +-8 - PST 1942 +-8 u P%sT 1946 +-8 - PST 1969 +-8 u P%sT 1983 O 30 2 +-9 u Y%sT 1983 N 30 +-9 u AK%sT +Z America/St_Johns -3:30:52 - LMT 1884 +-3:30:52 j N%sT 1918 +-3:30:52 C N%sT 1919 +-3:30:52 j N%sT 1935 Mar 30 +-3:30 j N%sT 1942 May 11 +-3:30 C N%sT 1946 +-3:30 j N%sT 2011 N +-3:30 C N%sT +Z America/Swift_Current -7:11:20 - LMT 1905 S +-7 C M%sT 1946 Ap lastSu 2 +-7 r M%sT 1950 +-7 Sw M%sT 1972 Ap lastSu 2 +-6 - CST +Z America/Tegucigalpa -5:48:52 - LMT 1921 Ap +-6 HN C%sT +Z America/Thule -4:35:8 - LMT 1916 Jul 28 +-4 Th A%sT +Z America/Tijuana -7:48:4 - LMT 1922 Ja 1 7u +-7 - MST 1924 +-8 - PST 1927 Jun 10 +-7 - MST 1930 N 15 +-8 - PST 1931 Ap +-8 1 PDT 1931 S 30 +-8 - PST 1942 Ap 24 +-8 1 PWT 1945 Au 14 23u +-8 1 PPT 1945 N 15 +-8 - PST 1948 Ap 5 +-8 1 PDT 1949 Ja 14 +-8 - PST 1950 May +-8 1 PDT 1950 S 24 +-8 - PST 1951 Ap 29 2 +-8 1 PDT 1951 S 30 2 +-8 - PST 1952 Ap 27 2 +-8 1 PDT 1952 S 28 2 +-8 CA P%sT 1967 +-8 u P%sT 1996 +-8 m P%sT 2001 +-8 u P%sT 2002 F 20 +-8 m P%sT 2010 +-8 u P%sT +Z America/Toronto -5:17:32 - LMT 1895 +-5 C E%sT 1919 +-5 t E%sT 1942 F 9 2s +-5 C E%sT 1946 +-5 t E%sT 1974 +-5 C E%sT +Z America/Vancouver -8:12:28 - LMT 1884 +-8 Va P%sT 1987 +-8 C P%sT +Z America/Whitehorse -9:0:12 - LMT 1900 Au 20 +-9 Y Y%sT 1965 +-9 Yu Y%sT 1966 F 27 +-8 - PST 1980 +-8 C P%sT 2020 N +-7 - MST +Z America/Winnipeg -6:28:36 - LMT 1887 Jul 16 +-6 W C%sT 2006 +-6 C C%sT +Z America/Yakutat 14:41:5 - LMT 1867 O 19 15:12:18 +-9:18:55 - LMT 1900 Au 20 12 +-9 - YST 1942 +-9 u Y%sT 1946 +-9 - YST 1969 +-9 u Y%sT 1983 N 30 +-9 u AK%sT +Z Antarctica/Casey 0 - -00 1969 +8 - %z 2009 O 18 2 +11 - %z 2010 Mar 5 2 +8 - %z 2011 O 28 2 +11 - %z 2012 F 21 17u +8 - %z 2016 O 22 +11 - %z 2018 Mar 11 4 +8 - %z 2018 O 7 4 +11 - %z 2019 Mar 17 3 +8 - %z 2019 O 4 3 +11 - %z 2020 Mar 8 3 +8 - %z 2020 O 4 0:1 +11 - %z 2021 Mar 14 +8 - %z 2021 O 3 0:1 +11 - %z 2022 Mar 13 +8 - %z 2022 O 2 0:1 +11 - %z 2023 Mar 9 3 +8 - %z +Z Antarctica/Davis 0 - -00 1957 Ja 13 +7 - %z 1964 N +0 - -00 1969 F +7 - %z 2009 O 18 2 +5 - %z 2010 Mar 10 20u +7 - %z 2011 O 28 2 +5 - %z 2012 F 21 20u +7 - %z +Z Antarctica/Macquarie 0 - -00 1899 N +10 - AEST 1916 O 1 2 +10 1 AEDT 1917 F +10 AU AE%sT 1919 Ap 1 0s +0 - -00 1948 Mar 25 +10 AU AE%sT 1967 +10 AT AE%sT 2010 +10 1 AEDT 2011 +10 AT AE%sT +Z Antarctica/Mawson 0 - -00 1954 F 13 +6 - %z 2009 O 18 2 +5 - %z +Z Antarctica/Palmer 0 - -00 1965 +-4 A %z 1969 O 5 +-3 A %z 1982 May +-4 x %z 2016 D 4 +-3 - %z +Z Antarctica/Rothera 0 - -00 1976 D +-3 - %z +Z Antarctica/Troll 0 - -00 2005 F 12 +0 Tr %s +Z Antarctica/Vostok 0 - -00 1957 D 16 +7 - %z 1994 F +0 - -00 1994 N +7 - %z 2023 D 18 2 +5 - %z +Z Asia/Almaty 5:7:48 - LMT 1924 May 2 +5 - %z 1930 Jun 21 +6 R %z 1991 Mar 31 2s +5 R %z 1992 Ja 19 2s +6 R %z 2004 O 31 2s +6 - %z 2024 Mar +5 - %z +Z Asia/Amman 2:23:44 - LMT 1931 +2 J EE%sT 2022 O 28 0s +3 - %z +Z Asia/Anadyr 11:49:56 - LMT 1924 May 2 +12 - %z 1930 Jun 21 +13 R %z 1982 Ap 1 0s +12 R %z 1991 Mar 31 2s +11 R %z 1992 Ja 19 2s +12 R %z 2010 Mar 28 2s +11 R %z 2011 Mar 27 2s +12 - %z +Z Asia/Aqtau 3:21:4 - LMT 1924 May 2 +4 - %z 1930 Jun 21 +5 - %z 1981 O +6 - %z 1982 Ap +5 R %z 1991 Mar 31 2s +4 R %z 1992 Ja 19 2s +5 R %z 1994 S 25 2s +4 R %z 2004 O 31 2s +5 - %z +Z Asia/Aqtobe 3:48:40 - LMT 1924 May 2 +4 - %z 1930 Jun 21 +5 - %z 1981 Ap +5 1 %z 1981 O +6 - %z 1982 Ap +5 R %z 1991 Mar 31 2s +4 R %z 1992 Ja 19 2s +5 R %z 2004 O 31 2s +5 - %z +Z Asia/Ashgabat 3:53:32 - LMT 1924 May 2 +4 - %z 1930 Jun 21 +5 R %z 1991 Mar 31 2 +4 R %z 1992 Ja 19 2 +5 - %z +Z Asia/Atyrau 3:27:44 - LMT 1924 May 2 +3 - %z 1930 Jun 21 +5 - %z 1981 O +6 - %z 1982 Ap +5 R %z 1991 Mar 31 2s +4 R %z 1992 Ja 19 2s +5 R %z 1999 Mar 28 2s +4 R %z 2004 O 31 2s +5 - %z +Z Asia/Baghdad 2:57:40 - LMT 1890 +2:57:36 - BMT 1918 +3 - %z 1982 May +3 IQ %z +Z Asia/Baku 3:19:24 - LMT 1924 May 2 +3 - %z 1957 Mar +4 R %z 1991 Mar 31 2s +3 R %z 1992 S lastSu 2s +4 - %z 1996 +4 E %z 1997 +4 AZ %z +Z Asia/Bangkok 6:42:4 - LMT 1880 +6:42:4 - BMT 1920 Ap +7 - %z +Z Asia/Barnaul 5:35 - LMT 1919 D 10 +6 - %z 1930 Jun 21 +7 R %z 1991 Mar 31 2s +6 R %z 1992 Ja 19 2s +7 R %z 1995 May 28 +6 R %z 2011 Mar 27 2s +7 - %z 2014 O 26 2s +6 - %z 2016 Mar 27 2s +7 - %z +Z Asia/Beirut 2:22 - LMT 1880 +2 l EE%sT +Z Asia/Bishkek 4:58:24 - LMT 1924 May 2 +5 - %z 1930 Jun 21 +6 R %z 1991 Mar 31 2s +5 R %z 1991 Au 31 2 +5 KG %z 2005 Au 12 +6 - %z +Z Asia/Chita 7:33:52 - LMT 1919 D 15 +8 - %z 1930 Jun 21 +9 R %z 1991 Mar 31 2s +8 R %z 1992 Ja 19 2s +9 R %z 2011 Mar 27 2s +10 - %z 2014 O 26 2s +8 - %z 2016 Mar 27 2 +9 - %z +Z Asia/Colombo 5:19:24 - LMT 1880 +5:19:32 - MMT 1906 +5:30 - %z 1942 Ja 5 +5:30 0:30 %z 1942 S +5:30 1 %z 1945 O 16 2 +5:30 - %z 1996 May 25 +6:30 - %z 1996 O 26 0:30 +6 - %z 2006 Ap 15 0:30 +5:30 - %z +Z Asia/Damascus 2:25:12 - LMT 1920 +2 S EE%sT 2022 O 28 +3 - %z +Z Asia/Dhaka 6:1:40 - LMT 1890 +5:53:20 - HMT 1941 O +6:30 - %z 1942 May 15 +5:30 - %z 1942 S +6:30 - %z 1951 S 30 +6 - %z 2009 +6 BD %z +Z Asia/Dili 8:22:20 - LMT 1911 D 31 16u +8 - %z 1942 F 21 23 +9 - %z 1976 May 3 +8 - %z 2000 S 17 +9 - %z +Z Asia/Dubai 3:41:12 - LMT 1920 +4 - %z +Z Asia/Dushanbe 4:35:12 - LMT 1924 May 2 +5 - %z 1930 Jun 21 +6 R %z 1991 Mar 31 2s +5 1 %z 1991 S 9 2s +5 - %z +Z Asia/Famagusta 2:15:48 - LMT 1921 N 14 +2 CY EE%sT 1998 S +2 E EE%sT 2016 S 8 +3 - %z 2017 O 29 1u +2 E EE%sT +Z Asia/Gaza 2:17:52 - LMT 1900 O +2 Z EET/EEST 1948 May 15 +2 K EE%sT 1967 Jun 5 +2 Z I%sT 1996 +2 J EE%sT 1999 +2 P EE%sT 2008 Au 29 +2 - EET 2008 S +2 P EE%sT 2010 +2 - EET 2010 Mar 27 0:1 +2 P EE%sT 2011 Au +2 - EET 2012 +2 P EE%sT +Z Asia/Hebron 2:20:23 - LMT 1900 O +2 Z EET/EEST 1948 May 15 +2 K EE%sT 1967 Jun 5 +2 Z I%sT 1996 +2 J EE%sT 1999 +2 P EE%sT +Z Asia/Ho_Chi_Minh 7:6:30 - LMT 1906 Jul +7:6:30 - PLMT 1911 May +7 - %z 1942 D 31 23 +8 - %z 1945 Mar 14 23 +9 - %z 1945 S 1 24 +7 - %z 1947 Ap +8 - %z 1955 Jul 1 1 +7 - %z 1959 D 31 23 +8 - %z 1975 Jun 13 +7 - %z +Z Asia/Hong_Kong 7:36:42 - LMT 1904 O 29 17u +8 - HKT 1941 Jun 15 3 +8 1 HKST 1941 O 1 4 +8 0:30 HKWT 1941 D 25 +9 - JST 1945 N 18 2 +8 HK HK%sT +Z Asia/Hovd 6:6:36 - LMT 1905 Au +6 - %z 1978 +7 X %z +Z Asia/Irkutsk 6:57:5 - LMT 1880 +6:57:5 - IMT 1920 Ja 25 +7 - %z 1930 Jun 21 +8 R %z 1991 Mar 31 2s +7 R %z 1992 Ja 19 2s +8 R %z 2011 Mar 27 2s +9 - %z 2014 O 26 2s +8 - %z +Z Asia/Jakarta 7:7:12 - LMT 1867 Au 10 +7:7:12 - BMT 1923 D 31 16:40u +7:20 - %z 1932 N +7:30 - %z 1942 Mar 23 +9 - %z 1945 S 23 +7:30 - %z 1948 May +8 - %z 1950 May +7:30 - %z 1964 +7 - WIB +Z Asia/Jayapura 9:22:48 - LMT 1932 N +9 - %z 1944 S +9:30 - %z 1964 +9 - WIT +Z Asia/Jerusalem 2:20:54 - LMT 1880 +2:20:40 - JMT 1918 +2 Z I%sT +Z Asia/Kabul 4:36:48 - LMT 1890 +4 - %z 1945 +4:30 - %z +Z Asia/Kamchatka 10:34:36 - LMT 1922 N 10 +11 - %z 1930 Jun 21 +12 R %z 1991 Mar 31 2s +11 R %z 1992 Ja 19 2s +12 R %z 2010 Mar 28 2s +11 R %z 2011 Mar 27 2s +12 - %z +Z Asia/Karachi 4:28:12 - LMT 1907 +5:30 - %z 1942 S +5:30 1 %z 1945 O 15 +5:30 - %z 1951 S 30 +5 - %z 1971 Mar 26 +5 PK PK%sT +Z Asia/Kathmandu 5:41:16 - LMT 1920 +5:30 - %z 1986 +5:45 - %z +Z Asia/Khandyga 9:2:13 - LMT 1919 D 15 +8 - %z 1930 Jun 21 +9 R %z 1991 Mar 31 2s +8 R %z 1992 Ja 19 2s +9 R %z 2004 +10 R %z 2011 Mar 27 2s +11 - %z 2011 S 13 0s +10 - %z 2014 O 26 2s +9 - %z +Z Asia/Kolkata 5:53:28 - LMT 1854 Jun 28 +5:53:20 - HMT 1870 +5:21:10 - MMT 1906 +5:30 - IST 1941 O +5:30 1 %z 1942 May 15 +5:30 - IST 1942 S +5:30 1 %z 1945 O 15 +5:30 - IST +Z Asia/Krasnoyarsk 6:11:26 - LMT 1920 Ja 6 +6 - %z 1930 Jun 21 +7 R %z 1991 Mar 31 2s +6 R %z 1992 Ja 19 2s +7 R %z 2011 Mar 27 2s +8 - %z 2014 O 26 2s +7 - %z +Z Asia/Kuching 7:21:20 - LMT 1926 Mar +7:30 - %z 1933 +8 NB %z 1942 F 16 +9 - %z 1945 S 12 +8 - %z +Z Asia/Macau 7:34:10 - LMT 1904 O 30 +8 - CST 1941 D 21 23 +9 _ %z 1945 S 30 24 +8 _ C%sT +Z Asia/Magadan 10:3:12 - LMT 1924 May 2 +10 - %z 1930 Jun 21 +11 R %z 1991 Mar 31 2s +10 R %z 1992 Ja 19 2s +11 R %z 2011 Mar 27 2s +12 - %z 2014 O 26 2s +10 - %z 2016 Ap 24 2s +11 - %z +Z Asia/Makassar 7:57:36 - LMT 1920 +7:57:36 - MMT 1932 N +8 - %z 1942 F 9 +9 - %z 1945 S 23 +8 - WITA +Z Asia/Manila -15:56:8 - LMT 1844 D 31 +8:3:52 - LMT 1899 S 6 4u +8 PH P%sT 1942 F 11 24 +9 - JST 1945 Mar 4 +8 PH P%sT +Z Asia/Nicosia 2:13:28 - LMT 1921 N 14 +2 CY EE%sT 1998 S +2 E EE%sT +Z Asia/Novokuznetsk 5:48:48 - LMT 1924 May +6 - %z 1930 Jun 21 +7 R %z 1991 Mar 31 2s +6 R %z 1992 Ja 19 2s +7 R %z 2010 Mar 28 2s +6 R %z 2011 Mar 27 2s +7 - %z +Z Asia/Novosibirsk 5:31:40 - LMT 1919 D 14 6 +6 - %z 1930 Jun 21 +7 R %z 1991 Mar 31 2s +6 R %z 1992 Ja 19 2s +7 R %z 1993 May 23 +6 R %z 2011 Mar 27 2s +7 - %z 2014 O 26 2s +6 - %z 2016 Jul 24 2s +7 - %z +Z Asia/Omsk 4:53:30 - LMT 1919 N 14 +5 - %z 1930 Jun 21 +6 R %z 1991 Mar 31 2s +5 R %z 1992 Ja 19 2s +6 R %z 2011 Mar 27 2s +7 - %z 2014 O 26 2s +6 - %z +Z Asia/Oral 3:25:24 - LMT 1924 May 2 +3 - %z 1930 Jun 21 +5 - %z 1981 Ap +5 1 %z 1981 O +6 - %z 1982 Ap +5 R %z 1989 Mar 26 2s +4 R %z 1992 Ja 19 2s +5 R %z 1992 Mar 29 2s +4 R %z 2004 O 31 2s +5 - %z +Z Asia/Pontianak 7:17:20 - LMT 1908 May +7:17:20 - PMT 1932 N +7:30 - %z 1942 Ja 29 +9 - %z 1945 S 23 +7:30 - %z 1948 May +8 - %z 1950 May +7:30 - %z 1964 +8 - WITA 1988 +7 - WIB +Z Asia/Pyongyang 8:23 - LMT 1908 Ap +8:30 - KST 1912 +9 - JST 1945 Au 24 +9 - KST 2015 Au 15 +8:30 - KST 2018 May 4 23:30 +9 - KST +Z Asia/Qatar 3:26:8 - LMT 1920 +4 - %z 1972 Jun +3 - %z +Z Asia/Qostanay 4:14:28 - LMT 1924 May 2 +4 - %z 1930 Jun 21 +5 - %z 1981 Ap +5 1 %z 1981 O +6 - %z 1982 Ap +5 R %z 1991 Mar 31 2s +4 R %z 1992 Ja 19 2s +5 R %z 2004 O 31 2s +6 - %z 2024 Mar +5 - %z +Z Asia/Qyzylorda 4:21:52 - LMT 1924 May 2 +4 - %z 1930 Jun 21 +5 - %z 1981 Ap +5 1 %z 1981 O +6 - %z 1982 Ap +5 R %z 1991 Mar 31 2s +4 R %z 1991 S 29 2s +5 R %z 1992 Ja 19 2s +6 R %z 1992 Mar 29 2s +5 R %z 2004 O 31 2s +6 - %z 2018 D 21 +5 - %z +Z Asia/Riyadh 3:6:52 - LMT 1947 Mar 14 +3 - %z +Z Asia/Sakhalin 9:30:48 - LMT 1905 Au 23 +9 - %z 1945 Au 25 +11 R %z 1991 Mar 31 2s +10 R %z 1992 Ja 19 2s +11 R %z 1997 Mar lastSu 2s +10 R %z 2011 Mar 27 2s +11 - %z 2014 O 26 2s +10 - %z 2016 Mar 27 2s +11 - %z +Z Asia/Samarkand 4:27:53 - LMT 1924 May 2 +4 - %z 1930 Jun 21 +5 - %z 1981 Ap +5 1 %z 1981 O +6 - %z 1982 Ap +5 R %z 1992 +5 - %z +Z Asia/Seoul 8:27:52 - LMT 1908 Ap +8:30 - KST 1912 +9 - JST 1945 S 8 +9 KR K%sT 1954 Mar 21 +8:30 KR K%sT 1961 Au 10 +9 KR K%sT +Z Asia/Shanghai 8:5:43 - LMT 1901 +8 Sh C%sT 1949 May 28 +8 CN C%sT +Z Asia/Singapore 6:55:25 - LMT 1901 +6:55:25 - SMT 1905 Jun +7 - %z 1933 +7 0:20 %z 1936 +7:20 - %z 1941 S +7:30 - %z 1942 F 16 +9 - %z 1945 S 12 +7:30 - %z 1981 D 31 16u +8 - %z +Z Asia/Srednekolymsk 10:14:52 - LMT 1924 May 2 +10 - %z 1930 Jun 21 +11 R %z 1991 Mar 31 2s +10 R %z 1992 Ja 19 2s +11 R %z 2011 Mar 27 2s +12 - %z 2014 O 26 2s +11 - %z +Z Asia/Taipei 8:6 - LMT 1896 +8 - CST 1937 O +9 - JST 1945 S 21 1 +8 f C%sT +Z Asia/Tashkent 4:37:11 - LMT 1924 May 2 +5 - %z 1930 Jun 21 +6 R %z 1991 Mar 31 2 +5 R %z 1992 +5 - %z +Z Asia/Tbilisi 2:59:11 - LMT 1880 +2:59:11 - TBMT 1924 May 2 +3 - %z 1957 Mar +4 R %z 1991 Mar 31 2s +3 R %z 1992 +3 e %z 1994 S lastSu +4 e %z 1996 O lastSu +4 1 %z 1997 Mar lastSu +4 e %z 2004 Jun 27 +3 R %z 2005 Mar lastSu 2 +4 - %z +Z Asia/Tehran 3:25:44 - LMT 1916 +3:25:44 - TMT 1935 Jun 13 +3:30 i %z 1977 O 20 24 +4 i %z 1978 N 10 24 +3:30 i %z +Z Asia/Thimphu 5:58:36 - LMT 1947 Au 15 +5:30 - %z 1987 O +6 - %z +Z Asia/Tokyo 9:18:59 - LMT 1887 D 31 15u +9 JP J%sT +Z Asia/Tomsk 5:39:51 - LMT 1919 D 22 +6 - %z 1930 Jun 21 +7 R %z 1991 Mar 31 2s +6 R %z 1992 Ja 19 2s +7 R %z 2002 May 1 3 +6 R %z 2011 Mar 27 2s +7 - %z 2014 O 26 2s +6 - %z 2016 May 29 2s +7 - %z +Z Asia/Ulaanbaatar 7:7:32 - LMT 1905 Au +7 - %z 1978 +8 X %z +Z Asia/Urumqi 5:50:20 - LMT 1928 +6 - %z +Z Asia/Ust-Nera 9:32:54 - LMT 1919 D 15 +8 - %z 1930 Jun 21 +9 R %z 1981 Ap +11 R %z 1991 Mar 31 2s +10 R %z 1992 Ja 19 2s +11 R %z 2011 Mar 27 2s +12 - %z 2011 S 13 0s +11 - %z 2014 O 26 2s +10 - %z +Z Asia/Vladivostok 8:47:31 - LMT 1922 N 15 +9 - %z 1930 Jun 21 +10 R %z 1991 Mar 31 2s +9 R %z 1992 Ja 19 2s +10 R %z 2011 Mar 27 2s +11 - %z 2014 O 26 2s +10 - %z +Z Asia/Yakutsk 8:38:58 - LMT 1919 D 15 +8 - %z 1930 Jun 21 +9 R %z 1991 Mar 31 2s +8 R %z 1992 Ja 19 2s +9 R %z 2011 Mar 27 2s +10 - %z 2014 O 26 2s +9 - %z +Z Asia/Yangon 6:24:47 - LMT 1880 +6:24:47 - RMT 1920 +6:30 - %z 1942 May +9 - %z 1945 May 3 +6:30 - %z +Z Asia/Yekaterinburg 4:2:33 - LMT 1916 Jul 3 +3:45:5 - PMT 1919 Jul 15 4 +4 - %z 1930 Jun 21 +5 R %z 1991 Mar 31 2s +4 R %z 1992 Ja 19 2s +5 R %z 2011 Mar 27 2s +6 - %z 2014 O 26 2s +5 - %z +Z Asia/Yerevan 2:58 - LMT 1924 May 2 +3 - %z 1957 Mar +4 R %z 1991 Mar 31 2s +3 R %z 1995 S 24 2s +4 - %z 1997 +4 R %z 2011 +4 AM %z +Z Atlantic/Azores -1:42:40 - LMT 1884 +-1:54:32 - HMT 1912 Ja 1 2u +-2 p %z 1966 O 2 2s +-1 - %z 1982 Mar 28 0s +-1 p %z 1986 +-1 E %z 1992 D 27 1s +0 E WE%sT 1993 Jun 17 1u +-1 E %z +Z Atlantic/Bermuda -4:19:18 - LMT 1890 +-4:19:18 Be BMT/BST 1930 Ja 1 2 +-4 Be A%sT 1974 Ap 28 2 +-4 C A%sT 1976 +-4 u A%sT +Z Atlantic/Canary -1:1:36 - LMT 1922 Mar +-1 - %z 1946 S 30 1 +0 - WET 1980 Ap 6 0s +0 1 WEST 1980 S 28 1u +0 E WE%sT +Z Atlantic/Cape_Verde -1:34:4 - LMT 1912 Ja 1 2u +-2 - %z 1942 S +-2 1 %z 1945 O 15 +-2 - %z 1975 N 25 2 +-1 - %z +Z Atlantic/Faroe -0:27:4 - LMT 1908 Ja 11 +0 - WET 1981 +0 E WE%sT +Z Atlantic/Madeira -1:7:36 - LMT 1884 +-1:7:36 - FMT 1912 Ja 1 1u +-1 p %z 1966 O 2 2s +0 - WET 1982 Ap 4 +0 p WE%sT 1986 Jul 31 +0 E WE%sT +Z Atlantic/South_Georgia -2:26:8 - LMT 1890 +-2 - %z +Z Atlantic/Stanley -3:51:24 - LMT 1890 +-3:51:24 - SMT 1912 Mar 12 +-4 FK %z 1983 May +-3 FK %z 1985 S 15 +-4 FK %z 2010 S 5 2 +-3 - %z +Z Australia/Adelaide 9:14:20 - LMT 1895 F +9 - ACST 1899 May +9:30 AU AC%sT 1971 +9:30 AS AC%sT +Z Australia/Brisbane 10:12:8 - LMT 1895 +10 AU AE%sT 1971 +10 AQ AE%sT +Z Australia/Broken_Hill 9:25:48 - LMT 1895 F +10 - AEST 1896 Au 23 +9 - ACST 1899 May +9:30 AU AC%sT 1971 +9:30 AN AC%sT 2000 +9:30 AS AC%sT +Z Australia/Darwin 8:43:20 - LMT 1895 F +9 - ACST 1899 May +9:30 AU AC%sT +Z Australia/Eucla 8:35:28 - LMT 1895 D +8:45 AU %z 1943 Jul +8:45 AW %z +Z Australia/Hobart 9:49:16 - LMT 1895 S +10 AT AE%sT 1919 O 24 +10 AU AE%sT 1967 +10 AT AE%sT +Z Australia/Lindeman 9:55:56 - LMT 1895 +10 AU AE%sT 1971 +10 AQ AE%sT 1992 Jul +10 Ho AE%sT +Z Australia/Lord_Howe 10:36:20 - LMT 1895 F +10 - AEST 1981 Mar +10:30 LH %z 1985 Jul +10:30 LH %z +Z Australia/Melbourne 9:39:52 - LMT 1895 F +10 AU AE%sT 1971 +10 AV AE%sT +Z Australia/Perth 7:43:24 - LMT 1895 D +8 AU AW%sT 1943 Jul +8 AW AW%sT +Z Australia/Sydney 10:4:52 - LMT 1895 F +10 AU AE%sT 1971 +10 AN AE%sT +Z Etc/GMT 0 - GMT +Z Etc/GMT+1 -1 - %z +Z Etc/GMT+10 -10 - %z +Z Etc/GMT+11 -11 - %z +Z Etc/GMT+12 -12 - %z +Z Etc/GMT+2 -2 - %z +Z Etc/GMT+3 -3 - %z +Z Etc/GMT+4 -4 - %z +Z Etc/GMT+5 -5 - %z +Z Etc/GMT+6 -6 - %z +Z Etc/GMT+7 -7 - %z +Z Etc/GMT+8 -8 - %z +Z Etc/GMT+9 -9 - %z +Z Etc/GMT-1 1 - %z +Z Etc/GMT-10 10 - %z +Z Etc/GMT-11 11 - %z +Z Etc/GMT-12 12 - %z +Z Etc/GMT-13 13 - %z +Z Etc/GMT-14 14 - %z +Z Etc/GMT-2 2 - %z +Z Etc/GMT-3 3 - %z +Z Etc/GMT-4 4 - %z +Z Etc/GMT-5 5 - %z +Z Etc/GMT-6 6 - %z +Z Etc/GMT-7 7 - %z +Z Etc/GMT-8 8 - %z +Z Etc/GMT-9 9 - %z +Z Etc/UTC 0 - UTC +Z Europe/Andorra 0:6:4 - LMT 1901 +0 - WET 1946 S 30 +1 - CET 1985 Mar 31 2 +1 E CE%sT +Z Europe/Astrakhan 3:12:12 - LMT 1924 May +3 - %z 1930 Jun 21 +4 R %z 1989 Mar 26 2s +3 R %z 1991 Mar 31 2s +4 - %z 1992 Mar 29 2s +3 R %z 2011 Mar 27 2s +4 - %z 2014 O 26 2s +3 - %z 2016 Mar 27 2s +4 - %z +Z Europe/Athens 1:34:52 - LMT 1895 S 14 +1:34:52 - AMT 1916 Jul 28 0:1 +2 g EE%sT 1941 Ap 30 +1 g CE%sT 1944 Ap 4 +2 g EE%sT 1981 +2 E EE%sT +Z Europe/Belgrade 1:22 - LMT 1884 +1 - CET 1941 Ap 18 23 +1 c CE%sT 1945 +1 - CET 1945 May 8 2s +1 1 CEST 1945 S 16 2s +1 - CET 1982 N 27 +1 E CE%sT +Z Europe/Berlin 0:53:28 - LMT 1893 Ap +1 c CE%sT 1945 May 24 2 +1 So CE%sT 1946 +1 DE CE%sT 1980 +1 E CE%sT +Z Europe/Brussels 0:17:30 - LMT 1880 +0:17:30 - BMT 1892 May 1 0:17:30 +0 - WET 1914 N 8 +1 - CET 1916 May +1 c CE%sT 1918 N 11 11u +0 b WE%sT 1940 May 20 2s +1 c CE%sT 1944 S 3 +1 b CE%sT 1977 +1 E CE%sT +Z Europe/Bucharest 1:44:24 - LMT 1891 O +1:44:24 - BMT 1931 Jul 24 +2 z EE%sT 1981 Mar 29 2s +2 c EE%sT 1991 +2 z EE%sT 1994 +2 e EE%sT 1997 +2 E EE%sT +Z Europe/Budapest 1:16:20 - LMT 1890 N +1 c CE%sT 1918 +1 h CE%sT 1941 Ap 7 23 +1 c CE%sT 1945 +1 h CE%sT 1984 +1 E CE%sT +Z Europe/Chisinau 1:55:20 - LMT 1880 +1:55 - CMT 1918 F 15 +1:44:24 - BMT 1931 Jul 24 +2 z EE%sT 1940 Au 15 +2 1 EEST 1941 Jul 17 +1 c CE%sT 1944 Au 24 +3 R MSK/MSD 1990 May 6 2 +2 R EE%sT 1992 +2 e EE%sT 1997 +2 MD EE%sT +Z Europe/Dublin -0:25:21 - LMT 1880 Au 2 +-0:25:21 - DMT 1916 May 21 2s +-0:25:21 1 IST 1916 O 1 2s +0 G %s 1921 D 6 +0 G GMT/IST 1940 F 25 2s +0 1 IST 1946 O 6 2s +0 - GMT 1947 Mar 16 2s +0 1 IST 1947 N 2 2s +0 - GMT 1948 Ap 18 2s +0 G GMT/IST 1968 O 27 +1 IE IST/GMT +Z Europe/Gibraltar -0:21:24 - LMT 1880 Au 2 +0 G %s 1957 Ap 14 2 +1 - CET 1982 +1 E CE%sT +Z Europe/Helsinki 1:39:49 - LMT 1878 May 31 +1:39:49 - HMT 1921 May +2 FI EE%sT 1983 +2 E EE%sT +Z Europe/Istanbul 1:55:52 - LMT 1880 +1:56:56 - IMT 1910 O +2 T EE%sT 1978 Jun 29 +3 T %z 1984 N 1 2 +2 T EE%sT 2007 +2 E EE%sT 2011 Mar 27 1u +2 - EET 2011 Mar 28 1u +2 E EE%sT 2014 Mar 30 1u +2 - EET 2014 Mar 31 1u +2 E EE%sT 2015 O 25 1u +2 1 EEST 2015 N 8 1u +2 E EE%sT 2016 S 7 +3 - %z +Z Europe/Kaliningrad 1:22 - LMT 1893 Ap +1 c CE%sT 1945 Ap 10 +2 O EE%sT 1946 Ap 7 +3 R MSK/MSD 1989 Mar 26 2s +2 R EE%sT 2011 Mar 27 2s +3 - %z 2014 O 26 2s +2 - EET +Z Europe/Kirov 3:18:48 - LMT 1919 Jul 1 0u +3 - %z 1930 Jun 21 +4 R %z 1989 Mar 26 2s +3 R MSK/MSD 1991 Mar 31 2s +4 - %z 1992 Mar 29 2s +3 R MSK/MSD 2011 Mar 27 2s +4 - MSK 2014 O 26 2s +3 - MSK +Z Europe/Kyiv 2:2:4 - LMT 1880 +2:2:4 - KMT 1924 May 2 +2 - EET 1930 Jun 21 +3 - MSK 1941 S 20 +1 c CE%sT 1943 N 6 +3 R MSK/MSD 1990 Jul 1 2 +2 1 EEST 1991 S 29 3 +2 c EE%sT 1996 May 13 +2 E EE%sT +Z Europe/Lisbon -0:36:45 - LMT 1884 +-0:36:45 - LMT 1912 Ja 1 0u +0 p WE%sT 1966 O 2 2s +1 - CET 1976 S 26 1 +0 p WE%sT 1986 +0 E WE%sT 1992 S 27 1u +1 E CE%sT 1996 Mar 31 1u +0 E WE%sT +Z Europe/London -0:1:15 - LMT 1847 D +0 G %s 1968 O 27 +1 - BST 1971 O 31 2u +0 G %s 1996 +0 E GMT/BST +Z Europe/Madrid -0:14:44 - LMT 1901 Ja 1 0u +0 s WE%sT 1940 Mar 16 23 +1 s CE%sT 1979 +1 E CE%sT +Z Europe/Malta 0:58:4 - LMT 1893 N 2 +1 I CE%sT 1973 Mar 31 +1 MT CE%sT 1981 +1 E CE%sT +Z Europe/Minsk 1:50:16 - LMT 1880 +1:50 - MMT 1924 May 2 +2 - EET 1930 Jun 21 +3 - MSK 1941 Jun 28 +1 c CE%sT 1944 Jul 3 +3 R MSK/MSD 1990 +3 - MSK 1991 Mar 31 2s +2 R EE%sT 2011 Mar 27 2s +3 - %z +Z Europe/Moscow 2:30:17 - LMT 1880 +2:30:17 - MMT 1916 Jul 3 +2:31:19 R %s 1919 Jul 1 0u +3 R %s 1921 O +3 R MSK/MSD 1922 O +2 - EET 1930 Jun 21 +3 R MSK/MSD 1991 Mar 31 2s +2 R EE%sT 1992 Ja 19 2s +3 R MSK/MSD 2011 Mar 27 2s +4 - MSK 2014 O 26 2s +3 - MSK +Z Europe/Paris 0:9:21 - LMT 1891 Mar 16 +0:9:21 - PMT 1911 Mar 11 +0 F WE%sT 1940 Jun 14 23 +1 c CE%sT 1944 Au 25 +0 F WE%sT 1945 S 16 3 +1 F CE%sT 1977 +1 E CE%sT +Z Europe/Prague 0:57:44 - LMT 1850 +0:57:44 - PMT 1891 O +1 c CE%sT 1945 May 9 +1 CZ CE%sT 1946 D 1 3 +1 -1 GMT 1947 F 23 2 +1 CZ CE%sT 1979 +1 E CE%sT +Z Europe/Riga 1:36:34 - LMT 1880 +1:36:34 - RMT 1918 Ap 15 2 +1:36:34 1 LST 1918 S 16 3 +1:36:34 - RMT 1919 Ap 1 2 +1:36:34 1 LST 1919 May 22 3 +1:36:34 - RMT 1926 May 11 +2 - EET 1940 Au 5 +3 - MSK 1941 Jul +1 c CE%sT 1944 O 13 +3 R MSK/MSD 1989 Mar lastSu 2s +2 1 EEST 1989 S lastSu 2s +2 LV EE%sT 1997 Ja 21 +2 E EE%sT 2000 F 29 +2 - EET 2001 Ja 2 +2 E EE%sT +Z Europe/Rome 0:49:56 - LMT 1866 D 12 +0:49:56 - RMT 1893 O 31 23u +1 I CE%sT 1943 S 10 +1 c CE%sT 1944 Jun 4 +1 I CE%sT 1980 +1 E CE%sT +Z Europe/Samara 3:20:20 - LMT 1919 Jul 1 0u +3 - %z 1930 Jun 21 +4 - %z 1935 Ja 27 +4 R %z 1989 Mar 26 2s +3 R %z 1991 Mar 31 2s +2 R %z 1991 S 29 2s +3 - %z 1991 O 20 3 +4 R %z 2010 Mar 28 2s +3 R %z 2011 Mar 27 2s +4 - %z +Z Europe/Saratov 3:4:18 - LMT 1919 Jul 1 0u +3 - %z 1930 Jun 21 +4 R %z 1988 Mar 27 2s +3 R %z 1991 Mar 31 2s +4 - %z 1992 Mar 29 2s +3 R %z 2011 Mar 27 2s +4 - %z 2014 O 26 2s +3 - %z 2016 D 4 2s +4 - %z +Z Europe/Simferopol 2:16:24 - LMT 1880 +2:16 - SMT 1924 May 2 +2 - EET 1930 Jun 21 +3 - MSK 1941 N +1 c CE%sT 1944 Ap 13 +3 R MSK/MSD 1990 +3 - MSK 1990 Jul 1 2 +2 - EET 1992 Mar 20 +2 c EE%sT 1994 May +3 c MSK/MSD 1996 Mar 31 0s +3 1 MSD 1996 O 27 3s +3 - MSK 1997 Mar lastSu 1u +2 E EE%sT 2014 Mar 30 2 +4 - MSK 2014 O 26 2s +3 - MSK +Z Europe/Sofia 1:33:16 - LMT 1880 +1:56:56 - IMT 1894 N 30 +2 - EET 1942 N 2 3 +1 c CE%sT 1945 +1 - CET 1945 Ap 2 3 +2 - EET 1979 Mar 31 23 +2 BG EE%sT 1982 S 26 3 +2 c EE%sT 1991 +2 e EE%sT 1997 +2 E EE%sT +Z Europe/Tallinn 1:39 - LMT 1880 +1:39 - TMT 1918 F +1 c CE%sT 1919 Jul +1:39 - TMT 1921 May +2 - EET 1940 Au 6 +3 - MSK 1941 S 15 +1 c CE%sT 1944 S 22 +3 R MSK/MSD 1989 Mar 26 2s +2 1 EEST 1989 S 24 2s +2 c EE%sT 1998 S 22 +2 E EE%sT 1999 O 31 4 +2 - EET 2002 F 21 +2 E EE%sT +Z Europe/Tirane 1:19:20 - LMT 1914 +1 - CET 1940 Jun 16 +1 q CE%sT 1984 Jul +1 E CE%sT +Z Europe/Ulyanovsk 3:13:36 - LMT 1919 Jul 1 0u +3 - %z 1930 Jun 21 +4 R %z 1989 Mar 26 2s +3 R %z 1991 Mar 31 2s +2 R %z 1992 Ja 19 2s +3 R %z 2011 Mar 27 2s +4 - %z 2014 O 26 2s +3 - %z 2016 Mar 27 2s +4 - %z +Z Europe/Vienna 1:5:21 - LMT 1893 Ap +1 c CE%sT 1920 +1 a CE%sT 1940 Ap 1 2s +1 c CE%sT 1945 Ap 2 2s +1 1 CEST 1945 Ap 12 2s +1 - CET 1946 +1 a CE%sT 1981 +1 E CE%sT +Z Europe/Vilnius 1:41:16 - LMT 1880 +1:24 - WMT 1917 +1:35:36 - KMT 1919 O 10 +1 - CET 1920 Jul 12 +2 - EET 1920 O 9 +1 - CET 1940 Au 3 +3 - MSK 1941 Jun 24 +1 c CE%sT 1944 Au +3 R MSK/MSD 1989 Mar 26 2s +2 R EE%sT 1991 S 29 2s +2 c EE%sT 1998 +2 - EET 1998 Mar 29 1u +1 E CE%sT 1999 O 31 1u +2 - EET 2003 +2 E EE%sT +Z Europe/Volgograd 2:57:40 - LMT 1920 Ja 3 +3 - %z 1930 Jun 21 +4 - %z 1961 N 11 +4 R %z 1988 Mar 27 2s +3 R MSK/MSD 1991 Mar 31 2s +4 - %z 1992 Mar 29 2s +3 R MSK/MSD 2011 Mar 27 2s +4 - MSK 2014 O 26 2s +3 - MSK 2018 O 28 2s +4 - %z 2020 D 27 2s +3 - MSK +Z Europe/Warsaw 1:24 - LMT 1880 +1:24 - WMT 1915 Au 5 +1 c CE%sT 1918 S 16 3 +2 O EE%sT 1922 Jun +1 O CE%sT 1940 Jun 23 2 +1 c CE%sT 1944 O +1 O CE%sT 1977 +1 W- CE%sT 1988 +1 E CE%sT +Z Europe/Zurich 0:34:8 - LMT 1853 Jul 16 +0:29:46 - BMT 1894 Jun +1 CH CE%sT 1981 +1 E CE%sT +Z Factory 0 - -00 +Z Indian/Chagos 4:49:40 - LMT 1907 +5 - %z 1996 +6 - %z +Z Indian/Maldives 4:54 - LMT 1880 +4:54 - MMT 1960 +5 - %z +Z Indian/Mauritius 3:50 - LMT 1907 +4 MU %z +Z Pacific/Apia 12:33:4 - LMT 1892 Jul 5 +-11:26:56 - LMT 1911 +-11:30 - %z 1950 +-11 WS %z 2011 D 29 24 +13 WS %z +Z Pacific/Auckland 11:39:4 - LMT 1868 N 2 +11:30 NZ NZ%sT 1946 +12 NZ NZ%sT +Z Pacific/Bougainville 10:22:16 - LMT 1880 +9:48:32 - PMMT 1895 +10 - %z 1942 Jul +9 - %z 1945 Au 21 +10 - %z 2014 D 28 2 +11 - %z +Z Pacific/Chatham 12:13:48 - LMT 1868 N 2 +12:15 - %z 1946 +12:45 k %z +Z Pacific/Easter -7:17:28 - LMT 1890 +-7:17:28 - EMT 1932 S +-7 x %z 1982 Mar 14 3u +-6 x %z +Z Pacific/Efate 11:13:16 - LMT 1912 Ja 13 +11 VU %z +Z Pacific/Fakaofo -11:24:56 - LMT 1901 +-11 - %z 2011 D 30 +13 - %z +Z Pacific/Fiji 11:55:44 - LMT 1915 O 26 +12 FJ %z +Z Pacific/Galapagos -5:58:24 - LMT 1931 +-5 - %z 1986 +-6 EC %z +Z Pacific/Gambier -8:59:48 - LMT 1912 O +-9 - %z +Z Pacific/Guadalcanal 10:39:48 - LMT 1912 O +11 - %z +Z Pacific/Guam -14:21 - LMT 1844 D 31 +9:39 - LMT 1901 +10 - GST 1941 D 10 +9 - %z 1944 Jul 31 +10 Gu G%sT 2000 D 23 +10 - ChST +Z Pacific/Honolulu -10:31:26 - LMT 1896 Ja 13 12 +-10:30 - HST 1933 Ap 30 2 +-10:30 1 HDT 1933 May 21 12 +-10:30 u H%sT 1947 Jun 8 2 +-10 - HST +Z Pacific/Kanton 0 - -00 1937 Au 31 +-12 - %z 1979 O +-11 - %z 1994 D 31 +13 - %z +Z Pacific/Kiritimati -10:29:20 - LMT 1901 +-10:40 - %z 1979 O +-10 - %z 1994 D 31 +14 - %z +Z Pacific/Kosrae -13:8:4 - LMT 1844 D 31 +10:51:56 - LMT 1901 +11 - %z 1914 O +9 - %z 1919 F +11 - %z 1937 +10 - %z 1941 Ap +9 - %z 1945 Au +11 - %z 1969 O +12 - %z 1999 +11 - %z +Z Pacific/Kwajalein 11:9:20 - LMT 1901 +11 - %z 1937 +10 - %z 1941 Ap +9 - %z 1944 F 6 +11 - %z 1969 O +-12 - %z 1993 Au 20 24 +12 - %z +Z Pacific/Marquesas -9:18 - LMT 1912 O +-9:30 - %z +Z Pacific/Nauru 11:7:40 - LMT 1921 Ja 15 +11:30 - %z 1942 Au 29 +9 - %z 1945 S 8 +11:30 - %z 1979 F 10 2 +12 - %z +Z Pacific/Niue -11:19:40 - LMT 1952 O 16 +-11:20 - %z 1964 Jul +-11 - %z +Z Pacific/Norfolk 11:11:52 - LMT 1901 +11:12 - %z 1951 +11:30 - %z 1974 O 27 2s +11:30 1 %z 1975 Mar 2 2s +11:30 - %z 2015 O 4 2s +11 - %z 2019 Jul +11 AN %z +Z Pacific/Noumea 11:5:48 - LMT 1912 Ja 13 +11 NC %z +Z Pacific/Pago_Pago 12:37:12 - LMT 1892 Jul 5 +-11:22:48 - LMT 1911 +-11 - SST +Z Pacific/Palau -15:2:4 - LMT 1844 D 31 +8:57:56 - LMT 1901 +9 - %z +Z Pacific/Pitcairn -8:40:20 - LMT 1901 +-8:30 - %z 1998 Ap 27 +-8 - %z +Z Pacific/Port_Moresby 9:48:40 - LMT 1880 +9:48:32 - PMMT 1895 +10 - %z +Z Pacific/Rarotonga 13:20:56 - LMT 1899 D 26 +-10:39:4 - LMT 1952 O 16 +-10:30 - %z 1978 N 12 +-10 CK %z +Z Pacific/Tahiti -9:58:16 - LMT 1912 O +-10 - %z +Z Pacific/Tarawa 11:32:4 - LMT 1901 +12 - %z +Z Pacific/Tongatapu 12:19:12 - LMT 1945 S 10 +12:20 - %z 1961 +13 - %z 1999 +13 TO %z +L Etc/GMT GMT +L Australia/Sydney Australia/ACT +L Australia/Lord_Howe Australia/LHI +L Australia/Sydney Australia/NSW +L Australia/Darwin Australia/North +L Australia/Brisbane Australia/Queensland +L Australia/Adelaide Australia/South +L Australia/Hobart Australia/Tasmania +L Australia/Melbourne Australia/Victoria +L Australia/Perth Australia/West +L Australia/Broken_Hill Australia/Yancowinna +L America/Rio_Branco Brazil/Acre +L America/Noronha Brazil/DeNoronha +L America/Sao_Paulo Brazil/East +L America/Manaus Brazil/West +L Europe/Brussels CET +L America/Chicago CST6CDT +L America/Halifax Canada/Atlantic +L America/Winnipeg Canada/Central +L America/Toronto Canada/Eastern +L America/Edmonton Canada/Mountain +L America/St_Johns Canada/Newfoundland +L America/Vancouver Canada/Pacific +L America/Regina Canada/Saskatchewan +L America/Whitehorse Canada/Yukon +L America/Santiago Chile/Continental +L Pacific/Easter Chile/EasterIsland +L America/Havana Cuba +L Europe/Athens EET +L America/Panama EST +L America/New_York EST5EDT +L Africa/Cairo Egypt +L Europe/Dublin Eire +L Etc/GMT Etc/GMT+0 +L Etc/GMT Etc/GMT-0 +L Etc/GMT Etc/GMT0 +L Etc/GMT Etc/Greenwich +L Etc/UTC Etc/UCT +L Etc/UTC Etc/Universal +L Etc/UTC Etc/Zulu +L Europe/London GB +L Europe/London GB-Eire +L Etc/GMT GMT+0 +L Etc/GMT GMT-0 +L Etc/GMT GMT0 +L Etc/GMT Greenwich +L Asia/Hong_Kong Hongkong +L Africa/Abidjan Iceland +L Asia/Tehran Iran +L Asia/Jerusalem Israel +L America/Jamaica Jamaica +L Asia/Tokyo Japan +L Pacific/Kwajalein Kwajalein +L Africa/Tripoli Libya +L Europe/Brussels MET +L America/Phoenix MST +L America/Denver MST7MDT +L America/Tijuana Mexico/BajaNorte +L America/Mazatlan Mexico/BajaSur +L America/Mexico_City Mexico/General +L Pacific/Auckland NZ +L Pacific/Chatham NZ-CHAT +L America/Denver Navajo +L Asia/Shanghai PRC +L Europe/Warsaw Poland +L Europe/Lisbon Portugal +L Asia/Taipei ROC +L Asia/Seoul ROK +L Asia/Singapore Singapore +L Europe/Istanbul Turkey +L Etc/UTC UCT +L America/Anchorage US/Alaska +L America/Adak US/Aleutian +L America/Phoenix US/Arizona +L America/Chicago US/Central +L America/Indiana/Indianapolis US/East-Indiana +L America/New_York US/Eastern +L Pacific/Honolulu US/Hawaii +L America/Indiana/Knox US/Indiana-Starke +L America/Detroit US/Michigan +L America/Denver US/Mountain +L America/Los_Angeles US/Pacific +L Pacific/Pago_Pago US/Samoa +L Etc/UTC UTC +L Etc/UTC Universal +L Europe/Moscow W-SU +L Etc/UTC Zulu +L America/Argentina/Buenos_Aires America/Buenos_Aires +L America/Argentina/Catamarca America/Catamarca +L America/Argentina/Cordoba America/Cordoba +L America/Indiana/Indianapolis America/Indianapolis +L America/Argentina/Jujuy America/Jujuy +L America/Indiana/Knox America/Knox_IN +L America/Kentucky/Louisville America/Louisville +L America/Argentina/Mendoza America/Mendoza +L America/Puerto_Rico America/Virgin +L Pacific/Pago_Pago Pacific/Samoa +L Africa/Abidjan Africa/Accra +L Africa/Nairobi Africa/Addis_Ababa +L Africa/Nairobi Africa/Asmara +L Africa/Abidjan Africa/Bamako +L Africa/Lagos Africa/Bangui +L Africa/Abidjan Africa/Banjul +L Africa/Maputo Africa/Blantyre +L Africa/Lagos Africa/Brazzaville +L Africa/Maputo Africa/Bujumbura +L Africa/Abidjan Africa/Conakry +L Africa/Abidjan Africa/Dakar +L Africa/Nairobi Africa/Dar_es_Salaam +L Africa/Nairobi Africa/Djibouti +L Africa/Lagos Africa/Douala +L Africa/Abidjan Africa/Freetown +L Africa/Maputo Africa/Gaborone +L Africa/Maputo Africa/Harare +L Africa/Nairobi Africa/Kampala +L Africa/Maputo Africa/Kigali +L Africa/Lagos Africa/Kinshasa +L Africa/Lagos Africa/Libreville +L Africa/Abidjan Africa/Lome +L Africa/Lagos Africa/Luanda +L Africa/Maputo Africa/Lubumbashi +L Africa/Maputo Africa/Lusaka +L Africa/Lagos Africa/Malabo +L Africa/Johannesburg Africa/Maseru +L Africa/Johannesburg Africa/Mbabane +L Africa/Nairobi Africa/Mogadishu +L Africa/Lagos Africa/Niamey +L Africa/Abidjan Africa/Nouakchott +L Africa/Abidjan Africa/Ouagadougou +L Africa/Lagos Africa/Porto-Novo +L America/Puerto_Rico America/Anguilla +L America/Puerto_Rico America/Antigua +L America/Puerto_Rico America/Aruba +L America/Panama America/Atikokan +L America/Puerto_Rico America/Blanc-Sablon +L America/Panama America/Cayman +L America/Phoenix America/Creston +L America/Puerto_Rico America/Curacao +L America/Puerto_Rico America/Dominica +L America/Puerto_Rico America/Grenada +L America/Puerto_Rico America/Guadeloupe +L America/Puerto_Rico America/Kralendijk +L America/Puerto_Rico America/Lower_Princes +L America/Puerto_Rico America/Marigot +L America/Puerto_Rico America/Montserrat +L America/Toronto America/Nassau +L America/Puerto_Rico America/Port_of_Spain +L America/Puerto_Rico America/St_Barthelemy +L America/Puerto_Rico America/St_Kitts +L America/Puerto_Rico America/St_Lucia +L America/Puerto_Rico America/St_Thomas +L America/Puerto_Rico America/St_Vincent +L America/Puerto_Rico America/Tortola +L Pacific/Port_Moresby Antarctica/DumontDUrville +L Pacific/Auckland Antarctica/McMurdo +L Asia/Riyadh Antarctica/Syowa +L Europe/Berlin Arctic/Longyearbyen +L Asia/Riyadh Asia/Aden +L Asia/Qatar Asia/Bahrain +L Asia/Kuching Asia/Brunei +L Asia/Singapore Asia/Kuala_Lumpur +L Asia/Riyadh Asia/Kuwait +L Asia/Dubai Asia/Muscat +L Asia/Bangkok Asia/Phnom_Penh +L Asia/Bangkok Asia/Vientiane +L Africa/Abidjan Atlantic/Reykjavik +L Africa/Abidjan Atlantic/St_Helena +L Europe/Brussels Europe/Amsterdam +L Europe/Prague Europe/Bratislava +L Europe/Zurich Europe/Busingen +L Europe/Berlin Europe/Copenhagen +L Europe/London Europe/Guernsey +L Europe/London Europe/Isle_of_Man +L Europe/London Europe/Jersey +L Europe/Belgrade Europe/Ljubljana +L Europe/Brussels Europe/Luxembourg +L Europe/Helsinki Europe/Mariehamn +L Europe/Paris Europe/Monaco +L Europe/Berlin Europe/Oslo +L Europe/Belgrade Europe/Podgorica +L Europe/Rome Europe/San_Marino +L Europe/Belgrade Europe/Sarajevo +L Europe/Belgrade Europe/Skopje +L Europe/Berlin Europe/Stockholm +L Europe/Zurich Europe/Vaduz +L Europe/Rome Europe/Vatican +L Europe/Belgrade Europe/Zagreb +L Africa/Nairobi Indian/Antananarivo +L Asia/Bangkok Indian/Christmas +L Asia/Yangon Indian/Cocos +L Africa/Nairobi Indian/Comoro +L Indian/Maldives Indian/Kerguelen +L Asia/Dubai Indian/Mahe +L Africa/Nairobi Indian/Mayotte +L Asia/Dubai Indian/Reunion +L Pacific/Port_Moresby Pacific/Chuuk +L Pacific/Tarawa Pacific/Funafuti +L Pacific/Tarawa Pacific/Majuro +L Pacific/Pago_Pago Pacific/Midway +L Pacific/Guadalcanal Pacific/Pohnpei +L Pacific/Guam Pacific/Saipan +L Pacific/Tarawa Pacific/Wake +L Pacific/Tarawa Pacific/Wallis +L Africa/Abidjan Africa/Timbuktu +L America/Argentina/Catamarca America/Argentina/ComodRivadavia +L America/Adak America/Atka +L America/Panama America/Coral_Harbour +L America/Tijuana America/Ensenada +L America/Indiana/Indianapolis America/Fort_Wayne +L America/Toronto America/Montreal +L America/Toronto America/Nipigon +L America/Iqaluit America/Pangnirtung +L America/Rio_Branco America/Porto_Acre +L America/Winnipeg America/Rainy_River +L America/Argentina/Cordoba America/Rosario +L America/Tijuana America/Santa_Isabel +L America/Denver America/Shiprock +L America/Toronto America/Thunder_Bay +L America/Edmonton America/Yellowknife +L Pacific/Auckland Antarctica/South_Pole +L Asia/Ulaanbaatar Asia/Choibalsan +L Asia/Shanghai Asia/Chongqing +L Asia/Shanghai Asia/Harbin +L Asia/Urumqi Asia/Kashgar +L Asia/Jerusalem Asia/Tel_Aviv +L Europe/Berlin Atlantic/Jan_Mayen +L Australia/Sydney Australia/Canberra +L Australia/Hobart Australia/Currie +L Europe/London Europe/Belfast +L Europe/Chisinau Europe/Tiraspol +L Europe/Kyiv Europe/Uzhgorod +L Europe/Kyiv Europe/Zaporozhye +L Pacific/Kanton Pacific/Enderbury +L Pacific/Honolulu Pacific/Johnston +L Pacific/Port_Moresby Pacific/Yap +L Europe/Lisbon WET +L Africa/Nairobi Africa/Asmera +L America/Nuuk America/Godthab +L Asia/Ashgabat Asia/Ashkhabad +L Asia/Kolkata Asia/Calcutta +L Asia/Shanghai Asia/Chungking +L Asia/Dhaka Asia/Dacca +L Europe/Istanbul Asia/Istanbul +L Asia/Kathmandu Asia/Katmandu +L Asia/Macau Asia/Macao +L Asia/Yangon Asia/Rangoon +L Asia/Ho_Chi_Minh Asia/Saigon +L Asia/Thimphu Asia/Thimbu +L Asia/Makassar Asia/Ujung_Pandang +L Asia/Ulaanbaatar Asia/Ulan_Bator +L Atlantic/Faroe Atlantic/Faeroe +L Europe/Kyiv Europe/Kiev +L Asia/Nicosia Europe/Nicosia +L Pacific/Honolulu HST +L America/Los_Angeles PST8PDT +L Pacific/Guadalcanal Pacific/Ponape +L Pacific/Port_Moresby Pacific/Truk diff --git a/lib/python3.12/site-packages/tzdata/zoneinfo/zone.tab b/lib/python3.12/site-packages/tzdata/zoneinfo/zone.tab new file mode 100644 index 0000000000000000000000000000000000000000..2626b0550341a0605087f33cac6952d5fbb24e67 --- /dev/null +++ b/lib/python3.12/site-packages/tzdata/zoneinfo/zone.tab @@ -0,0 +1,448 @@ +# tzdb timezone descriptions (deprecated version) +# +# This file is in the public domain, so clarified as of +# 2009-05-17 by Arthur David Olson. +# +# From Paul Eggert (2021-09-20): +# This file is intended as a backward-compatibility aid for older programs. +# New programs should use zone1970.tab. This file is like zone1970.tab (see +# zone1970.tab's comments), but with the following additional restrictions: +# +# 1. This file contains only ASCII characters. +# 2. The first data column contains exactly one country code. +# +# Because of (2), each row stands for an area that is the intersection +# of a region identified by a country code and of a timezone where civil +# clocks have agreed since 1970; this is a narrower definition than +# that of zone1970.tab. +# +# Unlike zone1970.tab, a row's third column can be a Link from +# 'backward' instead of a Zone. +# +# This table is intended as an aid for users, to help them select timezones +# appropriate for their practical needs. It is not intended to take or +# endorse any position on legal or territorial claims. +# +#country- +#code coordinates TZ comments +AD +4230+00131 Europe/Andorra +AE +2518+05518 Asia/Dubai +AF +3431+06912 Asia/Kabul +AG +1703-06148 America/Antigua +AI +1812-06304 America/Anguilla +AL +4120+01950 Europe/Tirane +AM +4011+04430 Asia/Yerevan +AO -0848+01314 Africa/Luanda +AQ -7750+16636 Antarctica/McMurdo New Zealand time - McMurdo, South Pole +AQ -6617+11031 Antarctica/Casey Casey +AQ -6835+07758 Antarctica/Davis Davis +AQ -6640+14001 Antarctica/DumontDUrville Dumont-d'Urville +AQ -6736+06253 Antarctica/Mawson Mawson +AQ -6448-06406 Antarctica/Palmer Palmer +AQ -6734-06808 Antarctica/Rothera Rothera +AQ -690022+0393524 Antarctica/Syowa Syowa +AQ -720041+0023206 Antarctica/Troll Troll +AQ -7824+10654 Antarctica/Vostok Vostok +AR -3436-05827 America/Argentina/Buenos_Aires Buenos Aires (BA, CF) +AR -3124-06411 America/Argentina/Cordoba Argentina (most areas: CB, CC, CN, ER, FM, MN, SE, SF) +AR -2447-06525 America/Argentina/Salta Salta (SA, LP, NQ, RN) +AR -2411-06518 America/Argentina/Jujuy Jujuy (JY) +AR -2649-06513 America/Argentina/Tucuman Tucuman (TM) +AR -2828-06547 America/Argentina/Catamarca Catamarca (CT), Chubut (CH) +AR -2926-06651 America/Argentina/La_Rioja La Rioja (LR) +AR -3132-06831 America/Argentina/San_Juan San Juan (SJ) +AR -3253-06849 America/Argentina/Mendoza Mendoza (MZ) +AR -3319-06621 America/Argentina/San_Luis San Luis (SL) +AR -5138-06913 America/Argentina/Rio_Gallegos Santa Cruz (SC) +AR -5448-06818 America/Argentina/Ushuaia Tierra del Fuego (TF) +AS -1416-17042 Pacific/Pago_Pago +AT +4813+01620 Europe/Vienna +AU -3133+15905 Australia/Lord_Howe Lord Howe Island +AU -5430+15857 Antarctica/Macquarie Macquarie Island +AU -4253+14719 Australia/Hobart Tasmania +AU -3749+14458 Australia/Melbourne Victoria +AU -3352+15113 Australia/Sydney New South Wales (most areas) +AU -3157+14127 Australia/Broken_Hill New South Wales (Yancowinna) +AU -2728+15302 Australia/Brisbane Queensland (most areas) +AU -2016+14900 Australia/Lindeman Queensland (Whitsunday Islands) +AU -3455+13835 Australia/Adelaide South Australia +AU -1228+13050 Australia/Darwin Northern Territory +AU -3157+11551 Australia/Perth Western Australia (most areas) +AU -3143+12852 Australia/Eucla Western Australia (Eucla) +AW +1230-06958 America/Aruba +AX +6006+01957 Europe/Mariehamn +AZ +4023+04951 Asia/Baku +BA +4352+01825 Europe/Sarajevo +BB +1306-05937 America/Barbados +BD +2343+09025 Asia/Dhaka +BE +5050+00420 Europe/Brussels +BF +1222-00131 Africa/Ouagadougou +BG +4241+02319 Europe/Sofia +BH +2623+05035 Asia/Bahrain +BI -0323+02922 Africa/Bujumbura +BJ +0629+00237 Africa/Porto-Novo +BL +1753-06251 America/St_Barthelemy +BM +3217-06446 Atlantic/Bermuda +BN +0456+11455 Asia/Brunei +BO -1630-06809 America/La_Paz +BQ +120903-0681636 America/Kralendijk +BR -0351-03225 America/Noronha Atlantic islands +BR -0127-04829 America/Belem Para (east), Amapa +BR -0343-03830 America/Fortaleza Brazil (northeast: MA, PI, CE, RN, PB) +BR -0803-03454 America/Recife Pernambuco +BR -0712-04812 America/Araguaina Tocantins +BR -0940-03543 America/Maceio Alagoas, Sergipe +BR -1259-03831 America/Bahia Bahia +BR -2332-04637 America/Sao_Paulo Brazil (southeast: GO, DF, MG, ES, RJ, SP, PR, SC, RS) +BR -2027-05437 America/Campo_Grande Mato Grosso do Sul +BR -1535-05605 America/Cuiaba Mato Grosso +BR -0226-05452 America/Santarem Para (west) +BR -0846-06354 America/Porto_Velho Rondonia +BR +0249-06040 America/Boa_Vista Roraima +BR -0308-06001 America/Manaus Amazonas (east) +BR -0640-06952 America/Eirunepe Amazonas (west) +BR -0958-06748 America/Rio_Branco Acre +BS +2505-07721 America/Nassau +BT +2728+08939 Asia/Thimphu +BW -2439+02555 Africa/Gaborone +BY +5354+02734 Europe/Minsk +BZ +1730-08812 America/Belize +CA +4734-05243 America/St_Johns Newfoundland, Labrador (SE) +CA +4439-06336 America/Halifax Atlantic - NS (most areas), PE +CA +4612-05957 America/Glace_Bay Atlantic - NS (Cape Breton) +CA +4606-06447 America/Moncton Atlantic - New Brunswick +CA +5320-06025 America/Goose_Bay Atlantic - Labrador (most areas) +CA +5125-05707 America/Blanc-Sablon AST - QC (Lower North Shore) +CA +4339-07923 America/Toronto Eastern - ON & QC (most areas) +CA +6344-06828 America/Iqaluit Eastern - NU (most areas) +CA +484531-0913718 America/Atikokan EST - ON (Atikokan), NU (Coral H) +CA +4953-09709 America/Winnipeg Central - ON (west), Manitoba +CA +744144-0944945 America/Resolute Central - NU (Resolute) +CA +624900-0920459 America/Rankin_Inlet Central - NU (central) +CA +5024-10439 America/Regina CST - SK (most areas) +CA +5017-10750 America/Swift_Current CST - SK (midwest) +CA +5333-11328 America/Edmonton Mountain - AB, BC(E), NT(E), SK(W) +CA +690650-1050310 America/Cambridge_Bay Mountain - NU (west) +CA +682059-1334300 America/Inuvik Mountain - NT (west) +CA +4906-11631 America/Creston MST - BC (Creston) +CA +5546-12014 America/Dawson_Creek MST - BC (Dawson Cr, Ft St John) +CA +5848-12242 America/Fort_Nelson MST - BC (Ft Nelson) +CA +6043-13503 America/Whitehorse MST - Yukon (east) +CA +6404-13925 America/Dawson MST - Yukon (west) +CA +4916-12307 America/Vancouver Pacific - BC (most areas) +CC -1210+09655 Indian/Cocos +CD -0418+01518 Africa/Kinshasa Dem. Rep. of Congo (west) +CD -1140+02728 Africa/Lubumbashi Dem. Rep. of Congo (east) +CF +0422+01835 Africa/Bangui +CG -0416+01517 Africa/Brazzaville +CH +4723+00832 Europe/Zurich +CI +0519-00402 Africa/Abidjan +CK -2114-15946 Pacific/Rarotonga +CL -3327-07040 America/Santiago most of Chile +CL -4534-07204 America/Coyhaique Aysen Region +CL -5309-07055 America/Punta_Arenas Magallanes Region +CL -2709-10926 Pacific/Easter Easter Island +CM +0403+00942 Africa/Douala +CN +3114+12128 Asia/Shanghai Beijing Time +CN +4348+08735 Asia/Urumqi Xinjiang Time +CO +0436-07405 America/Bogota +CR +0956-08405 America/Costa_Rica +CU +2308-08222 America/Havana +CV +1455-02331 Atlantic/Cape_Verde +CW +1211-06900 America/Curacao +CX -1025+10543 Indian/Christmas +CY +3510+03322 Asia/Nicosia most of Cyprus +CY +3507+03357 Asia/Famagusta Northern Cyprus +CZ +5005+01426 Europe/Prague +DE +5230+01322 Europe/Berlin most of Germany +DE +4742+00841 Europe/Busingen Busingen +DJ +1136+04309 Africa/Djibouti +DK +5540+01235 Europe/Copenhagen +DM +1518-06124 America/Dominica +DO +1828-06954 America/Santo_Domingo +DZ +3647+00303 Africa/Algiers +EC -0210-07950 America/Guayaquil Ecuador (mainland) +EC -0054-08936 Pacific/Galapagos Galapagos Islands +EE +5925+02445 Europe/Tallinn +EG +3003+03115 Africa/Cairo +EH +2709-01312 Africa/El_Aaiun +ER +1520+03853 Africa/Asmara +ES +4024-00341 Europe/Madrid Spain (mainland) +ES +3553-00519 Africa/Ceuta Ceuta, Melilla +ES +2806-01524 Atlantic/Canary Canary Islands +ET +0902+03842 Africa/Addis_Ababa +FI +6010+02458 Europe/Helsinki +FJ -1808+17825 Pacific/Fiji +FK -5142-05751 Atlantic/Stanley +FM +0725+15147 Pacific/Chuuk Chuuk/Truk, Yap +FM +0658+15813 Pacific/Pohnpei Pohnpei/Ponape +FM +0519+16259 Pacific/Kosrae Kosrae +FO +6201-00646 Atlantic/Faroe +FR +4852+00220 Europe/Paris +GA +0023+00927 Africa/Libreville +GB +513030-0000731 Europe/London +GD +1203-06145 America/Grenada +GE +4143+04449 Asia/Tbilisi +GF +0456-05220 America/Cayenne +GG +492717-0023210 Europe/Guernsey +GH +0533-00013 Africa/Accra +GI +3608-00521 Europe/Gibraltar +GL +6411-05144 America/Nuuk most of Greenland +GL +7646-01840 America/Danmarkshavn National Park (east coast) +GL +7029-02158 America/Scoresbysund Scoresbysund/Ittoqqortoormiit +GL +7634-06847 America/Thule Thule/Pituffik +GM +1328-01639 Africa/Banjul +GN +0931-01343 Africa/Conakry +GP +1614-06132 America/Guadeloupe +GQ +0345+00847 Africa/Malabo +GR +3758+02343 Europe/Athens +GS -5416-03632 Atlantic/South_Georgia +GT +1438-09031 America/Guatemala +GU +1328+14445 Pacific/Guam +GW +1151-01535 Africa/Bissau +GY +0648-05810 America/Guyana +HK +2217+11409 Asia/Hong_Kong +HN +1406-08713 America/Tegucigalpa +HR +4548+01558 Europe/Zagreb +HT +1832-07220 America/Port-au-Prince +HU +4730+01905 Europe/Budapest +ID -0610+10648 Asia/Jakarta Java, Sumatra +ID -0002+10920 Asia/Pontianak Borneo (west, central) +ID -0507+11924 Asia/Makassar Borneo (east, south), Sulawesi/Celebes, Bali, Nusa Tengarra, Timor (west) +ID -0232+14042 Asia/Jayapura New Guinea (West Papua / Irian Jaya), Malukus/Moluccas +IE +5320-00615 Europe/Dublin +IL +314650+0351326 Asia/Jerusalem +IM +5409-00428 Europe/Isle_of_Man +IN +2232+08822 Asia/Kolkata +IO -0720+07225 Indian/Chagos +IQ +3321+04425 Asia/Baghdad +IR +3540+05126 Asia/Tehran +IS +6409-02151 Atlantic/Reykjavik +IT +4154+01229 Europe/Rome +JE +491101-0020624 Europe/Jersey +JM +175805-0764736 America/Jamaica +JO +3157+03556 Asia/Amman +JP +353916+1394441 Asia/Tokyo +KE -0117+03649 Africa/Nairobi +KG +4254+07436 Asia/Bishkek +KH +1133+10455 Asia/Phnom_Penh +KI +0125+17300 Pacific/Tarawa Gilbert Islands +KI -0247-17143 Pacific/Kanton Phoenix Islands +KI +0152-15720 Pacific/Kiritimati Line Islands +KM -1141+04316 Indian/Comoro +KN +1718-06243 America/St_Kitts +KP +3901+12545 Asia/Pyongyang +KR +3733+12658 Asia/Seoul +KW +2920+04759 Asia/Kuwait +KY +1918-08123 America/Cayman +KZ +4315+07657 Asia/Almaty most of Kazakhstan +KZ +4448+06528 Asia/Qyzylorda Qyzylorda/Kyzylorda/Kzyl-Orda +KZ +5312+06337 Asia/Qostanay Qostanay/Kostanay/Kustanay +KZ +5017+05710 Asia/Aqtobe Aqtobe/Aktobe +KZ +4431+05016 Asia/Aqtau Mangghystau/Mankistau +KZ +4707+05156 Asia/Atyrau Atyrau/Atirau/Gur'yev +KZ +5113+05121 Asia/Oral West Kazakhstan +LA +1758+10236 Asia/Vientiane +LB +3353+03530 Asia/Beirut +LC +1401-06100 America/St_Lucia +LI +4709+00931 Europe/Vaduz +LK +0656+07951 Asia/Colombo +LR +0618-01047 Africa/Monrovia +LS -2928+02730 Africa/Maseru +LT +5441+02519 Europe/Vilnius +LU +4936+00609 Europe/Luxembourg +LV +5657+02406 Europe/Riga +LY +3254+01311 Africa/Tripoli +MA +3339-00735 Africa/Casablanca +MC +4342+00723 Europe/Monaco +MD +4700+02850 Europe/Chisinau +ME +4226+01916 Europe/Podgorica +MF +1804-06305 America/Marigot +MG -1855+04731 Indian/Antananarivo +MH +0709+17112 Pacific/Majuro most of Marshall Islands +MH +0905+16720 Pacific/Kwajalein Kwajalein +MK +4159+02126 Europe/Skopje +ML +1239-00800 Africa/Bamako +MM +1647+09610 Asia/Yangon +MN +4755+10653 Asia/Ulaanbaatar most of Mongolia +MN +4801+09139 Asia/Hovd Bayan-Olgii, Hovd, Uvs +MO +221150+1133230 Asia/Macau +MP +1512+14545 Pacific/Saipan +MQ +1436-06105 America/Martinique +MR +1806-01557 Africa/Nouakchott +MS +1643-06213 America/Montserrat +MT +3554+01431 Europe/Malta +MU -2010+05730 Indian/Mauritius +MV +0410+07330 Indian/Maldives +MW -1547+03500 Africa/Blantyre +MX +1924-09909 America/Mexico_City Central Mexico +MX +2105-08646 America/Cancun Quintana Roo +MX +2058-08937 America/Merida Campeche, Yucatan +MX +2540-10019 America/Monterrey Durango; Coahuila, Nuevo Leon, Tamaulipas (most areas) +MX +2550-09730 America/Matamoros Coahuila, Nuevo Leon, Tamaulipas (US border) +MX +2838-10605 America/Chihuahua Chihuahua (most areas) +MX +3144-10629 America/Ciudad_Juarez Chihuahua (US border - west) +MX +2934-10425 America/Ojinaga Chihuahua (US border - east) +MX +2313-10625 America/Mazatlan Baja California Sur, Nayarit (most areas), Sinaloa +MX +2048-10515 America/Bahia_Banderas Bahia de Banderas +MX +2904-11058 America/Hermosillo Sonora +MX +3232-11701 America/Tijuana Baja California +MY +0310+10142 Asia/Kuala_Lumpur Malaysia (peninsula) +MY +0133+11020 Asia/Kuching Sabah, Sarawak +MZ -2558+03235 Africa/Maputo +NA -2234+01706 Africa/Windhoek +NC -2216+16627 Pacific/Noumea +NE +1331+00207 Africa/Niamey +NF -2903+16758 Pacific/Norfolk +NG +0627+00324 Africa/Lagos +NI +1209-08617 America/Managua +NL +5222+00454 Europe/Amsterdam +NO +5955+01045 Europe/Oslo +NP +2743+08519 Asia/Kathmandu +NR -0031+16655 Pacific/Nauru +NU -1901-16955 Pacific/Niue +NZ -3652+17446 Pacific/Auckland most of New Zealand +NZ -4357-17633 Pacific/Chatham Chatham Islands +OM +2336+05835 Asia/Muscat +PA +0858-07932 America/Panama +PE -1203-07703 America/Lima +PF -1732-14934 Pacific/Tahiti Society Islands +PF -0900-13930 Pacific/Marquesas Marquesas Islands +PF -2308-13457 Pacific/Gambier Gambier Islands +PG -0930+14710 Pacific/Port_Moresby most of Papua New Guinea +PG -0613+15534 Pacific/Bougainville Bougainville +PH +143512+1205804 Asia/Manila +PK +2452+06703 Asia/Karachi +PL +5215+02100 Europe/Warsaw +PM +4703-05620 America/Miquelon +PN -2504-13005 Pacific/Pitcairn +PR +182806-0660622 America/Puerto_Rico +PS +3130+03428 Asia/Gaza Gaza Strip +PS +313200+0350542 Asia/Hebron West Bank +PT +3843-00908 Europe/Lisbon Portugal (mainland) +PT +3238-01654 Atlantic/Madeira Madeira Islands +PT +3744-02540 Atlantic/Azores Azores +PW +0720+13429 Pacific/Palau +PY -2516-05740 America/Asuncion +QA +2517+05132 Asia/Qatar +RE -2052+05528 Indian/Reunion +RO +4426+02606 Europe/Bucharest +RS +4450+02030 Europe/Belgrade +RU +5443+02030 Europe/Kaliningrad MSK-01 - Kaliningrad +RU +554521+0373704 Europe/Moscow MSK+00 - Moscow area +# The obsolescent zone.tab format cannot represent Europe/Simferopol well. +# Put it in RU section and list as UA. See "territorial claims" above. +# Programs should use zone1970.tab instead; see above. +UA +4457+03406 Europe/Simferopol Crimea +RU +5836+04939 Europe/Kirov MSK+00 - Kirov +RU +4844+04425 Europe/Volgograd MSK+00 - Volgograd +RU +4621+04803 Europe/Astrakhan MSK+01 - Astrakhan +RU +5134+04602 Europe/Saratov MSK+01 - Saratov +RU +5420+04824 Europe/Ulyanovsk MSK+01 - Ulyanovsk +RU +5312+05009 Europe/Samara MSK+01 - Samara, Udmurtia +RU +5651+06036 Asia/Yekaterinburg MSK+02 - Urals +RU +5500+07324 Asia/Omsk MSK+03 - Omsk +RU +5502+08255 Asia/Novosibirsk MSK+04 - Novosibirsk +RU +5322+08345 Asia/Barnaul MSK+04 - Altai +RU +5630+08458 Asia/Tomsk MSK+04 - Tomsk +RU +5345+08707 Asia/Novokuznetsk MSK+04 - Kemerovo +RU +5601+09250 Asia/Krasnoyarsk MSK+04 - Krasnoyarsk area +RU +5216+10420 Asia/Irkutsk MSK+05 - Irkutsk, Buryatia +RU +5203+11328 Asia/Chita MSK+06 - Zabaykalsky +RU +6200+12940 Asia/Yakutsk MSK+06 - Lena River +RU +623923+1353314 Asia/Khandyga MSK+06 - Tomponsky, Ust-Maysky +RU +4310+13156 Asia/Vladivostok MSK+07 - Amur River +RU +643337+1431336 Asia/Ust-Nera MSK+07 - Oymyakonsky +RU +5934+15048 Asia/Magadan MSK+08 - Magadan +RU +4658+14242 Asia/Sakhalin MSK+08 - Sakhalin Island +RU +6728+15343 Asia/Srednekolymsk MSK+08 - Sakha (E), N Kuril Is +RU +5301+15839 Asia/Kamchatka MSK+09 - Kamchatka +RU +6445+17729 Asia/Anadyr MSK+09 - Bering Sea +RW -0157+03004 Africa/Kigali +SA +2438+04643 Asia/Riyadh +SB -0932+16012 Pacific/Guadalcanal +SC -0440+05528 Indian/Mahe +SD +1536+03232 Africa/Khartoum +SE +5920+01803 Europe/Stockholm +SG +0117+10351 Asia/Singapore +SH -1555-00542 Atlantic/St_Helena +SI +4603+01431 Europe/Ljubljana +SJ +7800+01600 Arctic/Longyearbyen +SK +4809+01707 Europe/Bratislava +SL +0830-01315 Africa/Freetown +SM +4355+01228 Europe/San_Marino +SN +1440-01726 Africa/Dakar +SO +0204+04522 Africa/Mogadishu +SR +0550-05510 America/Paramaribo +SS +0451+03137 Africa/Juba +ST +0020+00644 Africa/Sao_Tome +SV +1342-08912 America/El_Salvador +SX +180305-0630250 America/Lower_Princes +SY +3330+03618 Asia/Damascus +SZ -2618+03106 Africa/Mbabane +TC +2128-07108 America/Grand_Turk +TD +1207+01503 Africa/Ndjamena +TF -492110+0701303 Indian/Kerguelen +TG +0608+00113 Africa/Lome +TH +1345+10031 Asia/Bangkok +TJ +3835+06848 Asia/Dushanbe +TK -0922-17114 Pacific/Fakaofo +TL -0833+12535 Asia/Dili +TM +3757+05823 Asia/Ashgabat +TN +3648+01011 Africa/Tunis +TO -210800-1751200 Pacific/Tongatapu +TR +4101+02858 Europe/Istanbul +TT +1039-06131 America/Port_of_Spain +TV -0831+17913 Pacific/Funafuti +TW +2503+12130 Asia/Taipei +TZ -0648+03917 Africa/Dar_es_Salaam +UA +5026+03031 Europe/Kyiv most of Ukraine +UG +0019+03225 Africa/Kampala +UM +2813-17722 Pacific/Midway Midway Islands +UM +1917+16637 Pacific/Wake Wake Island +US +404251-0740023 America/New_York Eastern (most areas) +US +421953-0830245 America/Detroit Eastern - MI (most areas) +US +381515-0854534 America/Kentucky/Louisville Eastern - KY (Louisville area) +US +364947-0845057 America/Kentucky/Monticello Eastern - KY (Wayne) +US +394606-0860929 America/Indiana/Indianapolis Eastern - IN (most areas) +US +384038-0873143 America/Indiana/Vincennes Eastern - IN (Da, Du, K, Mn) +US +410305-0863611 America/Indiana/Winamac Eastern - IN (Pulaski) +US +382232-0862041 America/Indiana/Marengo Eastern - IN (Crawford) +US +382931-0871643 America/Indiana/Petersburg Eastern - IN (Pike) +US +384452-0850402 America/Indiana/Vevay Eastern - IN (Switzerland) +US +415100-0873900 America/Chicago Central (most areas) +US +375711-0864541 America/Indiana/Tell_City Central - IN (Perry) +US +411745-0863730 America/Indiana/Knox Central - IN (Starke) +US +450628-0873651 America/Menominee Central - MI (Wisconsin border) +US +470659-1011757 America/North_Dakota/Center Central - ND (Oliver) +US +465042-1012439 America/North_Dakota/New_Salem Central - ND (Morton rural) +US +471551-1014640 America/North_Dakota/Beulah Central - ND (Mercer) +US +394421-1045903 America/Denver Mountain (most areas) +US +433649-1161209 America/Boise Mountain - ID (south), OR (east) +US +332654-1120424 America/Phoenix MST - AZ (except Navajo) +US +340308-1181434 America/Los_Angeles Pacific +US +611305-1495401 America/Anchorage Alaska (most areas) +US +581807-1342511 America/Juneau Alaska - Juneau area +US +571035-1351807 America/Sitka Alaska - Sitka area +US +550737-1313435 America/Metlakatla Alaska - Annette Island +US +593249-1394338 America/Yakutat Alaska - Yakutat +US +643004-1652423 America/Nome Alaska (west) +US +515248-1763929 America/Adak Alaska - western Aleutians +US +211825-1575130 Pacific/Honolulu Hawaii +UY -345433-0561245 America/Montevideo +UZ +3940+06648 Asia/Samarkand Uzbekistan (west) +UZ +4120+06918 Asia/Tashkent Uzbekistan (east) +VA +415408+0122711 Europe/Vatican +VC +1309-06114 America/St_Vincent +VE +1030-06656 America/Caracas +VG +1827-06437 America/Tortola +VI +1821-06456 America/St_Thomas +VN +1045+10640 Asia/Ho_Chi_Minh +VU -1740+16825 Pacific/Efate +WF -1318-17610 Pacific/Wallis +WS -1350-17144 Pacific/Apia +YE +1245+04512 Asia/Aden +YT -1247+04514 Indian/Mayotte +ZA -2615+02800 Africa/Johannesburg +ZM -1525+02817 Africa/Lusaka +ZW -1750+03103 Africa/Harare diff --git a/lib/python3.12/site-packages/tzdata/zoneinfo/zone1970.tab b/lib/python3.12/site-packages/tzdata/zoneinfo/zone1970.tab new file mode 100644 index 0000000000000000000000000000000000000000..cd43e3d2e0d8a4550ea6ae52d0516a4f2e987a0b --- /dev/null +++ b/lib/python3.12/site-packages/tzdata/zoneinfo/zone1970.tab @@ -0,0 +1,375 @@ +# tzdb timezone descriptions +# +# This file is in the public domain. +# +# From Paul Eggert (2025-05-15): +# This file contains a table where each row stands for a timezone where +# civil timestamps have agreed since 1970. Columns are separated by +# a single tab. Lines beginning with ‘#’ are comments. All text uses +# UTF-8 encoding. The columns of the table are as follows: +# +# 1. The countries that overlap the timezone, as a comma-separated list +# of ISO 3166 2-character country codes. +# 2. Latitude and longitude of the timezone’s principal location +# in ISO 6709 sign-degrees-minutes-seconds format, +# either ±DDMM±DDDMM or ±DDMMSS±DDDMMSS, +# first latitude (+ is north), then longitude (+ is east). +# 3. Timezone name used in value of TZ environment variable. +# Please see the theory.html file for how these names are chosen. +# If multiple timezones overlap a country, each has a row in the +# table, with each column 1 containing the country code. +# 4. Comments; present if and only if countries have multiple timezones, +# and useful only for those countries. For example, the comments +# for the row with countries CH,DE,LI and name Europe/Zurich +# are useful only for DE, since CH and LI have no other timezones. +# +# If a timezone covers multiple countries, the most-populous city is used, +# and that country is listed first in column 1; any other countries +# are listed alphabetically by country code. The table is sorted +# first by country code, then (if possible) by an order within the +# country that (1) makes some geographical sense, and (2) puts the +# most populous timezones first, where that does not contradict (1). +# +# This table is intended as an aid for users, to help them select timezones +# appropriate for their practical needs. It is not intended to take or +# endorse any position on legal or territorial claims. +# +#country- +#codes coordinates TZ comments +AD +4230+00131 Europe/Andorra +AE,OM,RE,SC,TF +2518+05518 Asia/Dubai Crozet +AF +3431+06912 Asia/Kabul +AL +4120+01950 Europe/Tirane +AM +4011+04430 Asia/Yerevan +AQ -6617+11031 Antarctica/Casey Casey +AQ -6835+07758 Antarctica/Davis Davis +AQ -6736+06253 Antarctica/Mawson Mawson +AQ -6448-06406 Antarctica/Palmer Palmer +AQ -6734-06808 Antarctica/Rothera Rothera +AQ -720041+0023206 Antarctica/Troll Troll +AQ -7824+10654 Antarctica/Vostok Vostok +AR -3436-05827 America/Argentina/Buenos_Aires Buenos Aires (BA, CF) +AR -3124-06411 America/Argentina/Cordoba most areas: CB, CC, CN, ER, FM, MN, SE, SF +AR -2447-06525 America/Argentina/Salta Salta (SA, LP, NQ, RN) +AR -2411-06518 America/Argentina/Jujuy Jujuy (JY) +AR -2649-06513 America/Argentina/Tucuman Tucumán (TM) +AR -2828-06547 America/Argentina/Catamarca Catamarca (CT), Chubut (CH) +AR -2926-06651 America/Argentina/La_Rioja La Rioja (LR) +AR -3132-06831 America/Argentina/San_Juan San Juan (SJ) +AR -3253-06849 America/Argentina/Mendoza Mendoza (MZ) +AR -3319-06621 America/Argentina/San_Luis San Luis (SL) +AR -5138-06913 America/Argentina/Rio_Gallegos Santa Cruz (SC) +AR -5448-06818 America/Argentina/Ushuaia Tierra del Fuego (TF) +AS,UM -1416-17042 Pacific/Pago_Pago Midway +AT +4813+01620 Europe/Vienna +AU -3133+15905 Australia/Lord_Howe Lord Howe Island +AU -5430+15857 Antarctica/Macquarie Macquarie Island +AU -4253+14719 Australia/Hobart Tasmania +AU -3749+14458 Australia/Melbourne Victoria +AU -3352+15113 Australia/Sydney New South Wales (most areas) +AU -3157+14127 Australia/Broken_Hill New South Wales (Yancowinna) +AU -2728+15302 Australia/Brisbane Queensland (most areas) +AU -2016+14900 Australia/Lindeman Queensland (Whitsunday Islands) +AU -3455+13835 Australia/Adelaide South Australia +AU -1228+13050 Australia/Darwin Northern Territory +AU -3157+11551 Australia/Perth Western Australia (most areas) +AU -3143+12852 Australia/Eucla Western Australia (Eucla) +AZ +4023+04951 Asia/Baku +BB +1306-05937 America/Barbados +BD +2343+09025 Asia/Dhaka +BE,LU,NL +5050+00420 Europe/Brussels +BG +4241+02319 Europe/Sofia +BM +3217-06446 Atlantic/Bermuda +BO -1630-06809 America/La_Paz +BR -0351-03225 America/Noronha Atlantic islands +BR -0127-04829 America/Belem Pará (east), Amapá +BR -0343-03830 America/Fortaleza Brazil (northeast: MA, PI, CE, RN, PB) +BR -0803-03454 America/Recife Pernambuco +BR -0712-04812 America/Araguaina Tocantins +BR -0940-03543 America/Maceio Alagoas, Sergipe +BR -1259-03831 America/Bahia Bahia +BR -2332-04637 America/Sao_Paulo Brazil (southeast: GO, DF, MG, ES, RJ, SP, PR, SC, RS) +BR -2027-05437 America/Campo_Grande Mato Grosso do Sul +BR -1535-05605 America/Cuiaba Mato Grosso +BR -0226-05452 America/Santarem Pará (west) +BR -0846-06354 America/Porto_Velho Rondônia +BR +0249-06040 America/Boa_Vista Roraima +BR -0308-06001 America/Manaus Amazonas (east) +BR -0640-06952 America/Eirunepe Amazonas (west) +BR -0958-06748 America/Rio_Branco Acre +BT +2728+08939 Asia/Thimphu +BY +5354+02734 Europe/Minsk +BZ +1730-08812 America/Belize +CA +4734-05243 America/St_Johns Newfoundland, Labrador (SE) +CA +4439-06336 America/Halifax Atlantic - NS (most areas), PE +CA +4612-05957 America/Glace_Bay Atlantic - NS (Cape Breton) +CA +4606-06447 America/Moncton Atlantic - New Brunswick +CA +5320-06025 America/Goose_Bay Atlantic - Labrador (most areas) +CA,BS +4339-07923 America/Toronto Eastern - ON & QC (most areas) +CA +6344-06828 America/Iqaluit Eastern - NU (most areas) +CA +4953-09709 America/Winnipeg Central - ON (west), Manitoba +CA +744144-0944945 America/Resolute Central - NU (Resolute) +CA +624900-0920459 America/Rankin_Inlet Central - NU (central) +CA +5024-10439 America/Regina CST - SK (most areas) +CA +5017-10750 America/Swift_Current CST - SK (midwest) +CA +5333-11328 America/Edmonton Mountain - AB, BC(E), NT(E), SK(W) +CA +690650-1050310 America/Cambridge_Bay Mountain - NU (west) +CA +682059-1334300 America/Inuvik Mountain - NT (west) +CA +5546-12014 America/Dawson_Creek MST - BC (Dawson Cr, Ft St John) +CA +5848-12242 America/Fort_Nelson MST - BC (Ft Nelson) +CA +6043-13503 America/Whitehorse MST - Yukon (east) +CA +6404-13925 America/Dawson MST - Yukon (west) +CA +4916-12307 America/Vancouver Pacific - BC (most areas) +CH,DE,LI +4723+00832 Europe/Zurich Büsingen +CI,BF,GH,GM,GN,IS,ML,MR,SH,SL,SN,TG +0519-00402 Africa/Abidjan +CK -2114-15946 Pacific/Rarotonga +CL -3327-07040 America/Santiago most of Chile +CL -4534-07204 America/Coyhaique Aysén Region +CL -5309-07055 America/Punta_Arenas Magallanes Region +CL -2709-10926 Pacific/Easter Easter Island +CN +3114+12128 Asia/Shanghai Beijing Time +CN +4348+08735 Asia/Urumqi Xinjiang Time +CO +0436-07405 America/Bogota +CR +0956-08405 America/Costa_Rica +CU +2308-08222 America/Havana +CV +1455-02331 Atlantic/Cape_Verde +CY +3510+03322 Asia/Nicosia most of Cyprus +CY +3507+03357 Asia/Famagusta Northern Cyprus +CZ,SK +5005+01426 Europe/Prague +DE,DK,NO,SE,SJ +5230+01322 Europe/Berlin most of Germany +DO +1828-06954 America/Santo_Domingo +DZ +3647+00303 Africa/Algiers +EC -0210-07950 America/Guayaquil Ecuador (mainland) +EC -0054-08936 Pacific/Galapagos Galápagos Islands +EE +5925+02445 Europe/Tallinn +EG +3003+03115 Africa/Cairo +EH +2709-01312 Africa/El_Aaiun +ES +4024-00341 Europe/Madrid Spain (mainland) +ES +3553-00519 Africa/Ceuta Ceuta, Melilla +ES +2806-01524 Atlantic/Canary Canary Islands +FI,AX +6010+02458 Europe/Helsinki +FJ -1808+17825 Pacific/Fiji +FK -5142-05751 Atlantic/Stanley +FM +0519+16259 Pacific/Kosrae Kosrae +FO +6201-00646 Atlantic/Faroe +FR,MC +4852+00220 Europe/Paris +GB,GG,IM,JE +513030-0000731 Europe/London +GE +4143+04449 Asia/Tbilisi +GF +0456-05220 America/Cayenne +GI +3608-00521 Europe/Gibraltar +GL +6411-05144 America/Nuuk most of Greenland +GL +7646-01840 America/Danmarkshavn National Park (east coast) +GL +7029-02158 America/Scoresbysund Scoresbysund/Ittoqqortoormiit +GL +7634-06847 America/Thule Thule/Pituffik +GR +3758+02343 Europe/Athens +GS -5416-03632 Atlantic/South_Georgia +GT +1438-09031 America/Guatemala +GU,MP +1328+14445 Pacific/Guam +GW +1151-01535 Africa/Bissau +GY +0648-05810 America/Guyana +HK +2217+11409 Asia/Hong_Kong +HN +1406-08713 America/Tegucigalpa +HT +1832-07220 America/Port-au-Prince +HU +4730+01905 Europe/Budapest +ID -0610+10648 Asia/Jakarta Java, Sumatra +ID -0002+10920 Asia/Pontianak Borneo (west, central) +ID -0507+11924 Asia/Makassar Borneo (east, south), Sulawesi/Celebes, Bali, Nusa Tengarra, Timor (west) +ID -0232+14042 Asia/Jayapura New Guinea (West Papua / Irian Jaya), Malukus/Moluccas +IE +5320-00615 Europe/Dublin +IL +314650+0351326 Asia/Jerusalem +IN +2232+08822 Asia/Kolkata +IO -0720+07225 Indian/Chagos +IQ +3321+04425 Asia/Baghdad +IR +3540+05126 Asia/Tehran +IT,SM,VA +4154+01229 Europe/Rome +JM +175805-0764736 America/Jamaica +JO +3157+03556 Asia/Amman +JP,AU +353916+1394441 Asia/Tokyo Eyre Bird Observatory +KE,DJ,ER,ET,KM,MG,SO,TZ,UG,YT -0117+03649 Africa/Nairobi +KG +4254+07436 Asia/Bishkek +KI,MH,TV,UM,WF +0125+17300 Pacific/Tarawa Gilberts, Marshalls, Wake +KI -0247-17143 Pacific/Kanton Phoenix Islands +KI +0152-15720 Pacific/Kiritimati Line Islands +KP +3901+12545 Asia/Pyongyang +KR +3733+12658 Asia/Seoul +KZ +4315+07657 Asia/Almaty most of Kazakhstan +KZ +4448+06528 Asia/Qyzylorda Qyzylorda/Kyzylorda/Kzyl-Orda +KZ +5312+06337 Asia/Qostanay Qostanay/Kostanay/Kustanay +KZ +5017+05710 Asia/Aqtobe Aqtöbe/Aktobe +KZ +4431+05016 Asia/Aqtau Mangghystaū/Mankistau +KZ +4707+05156 Asia/Atyrau Atyraū/Atirau/Gur’yev +KZ +5113+05121 Asia/Oral West Kazakhstan +LB +3353+03530 Asia/Beirut +LK +0656+07951 Asia/Colombo +LR +0618-01047 Africa/Monrovia +LT +5441+02519 Europe/Vilnius +LV +5657+02406 Europe/Riga +LY +3254+01311 Africa/Tripoli +MA +3339-00735 Africa/Casablanca +MD +4700+02850 Europe/Chisinau +MH +0905+16720 Pacific/Kwajalein Kwajalein +MM,CC +1647+09610 Asia/Yangon +MN +4755+10653 Asia/Ulaanbaatar most of Mongolia +MN +4801+09139 Asia/Hovd Bayan-Ölgii, Hovd, Uvs +MO +221150+1133230 Asia/Macau +MQ +1436-06105 America/Martinique +MT +3554+01431 Europe/Malta +MU -2010+05730 Indian/Mauritius +MV,TF +0410+07330 Indian/Maldives Kerguelen, St Paul I, Amsterdam I +MX +1924-09909 America/Mexico_City Central Mexico +MX +2105-08646 America/Cancun Quintana Roo +MX +2058-08937 America/Merida Campeche, Yucatán +MX +2540-10019 America/Monterrey Durango; Coahuila, Nuevo León, Tamaulipas (most areas) +MX +2550-09730 America/Matamoros Coahuila, Nuevo León, Tamaulipas (US border) +MX +2838-10605 America/Chihuahua Chihuahua (most areas) +MX +3144-10629 America/Ciudad_Juarez Chihuahua (US border - west) +MX +2934-10425 America/Ojinaga Chihuahua (US border - east) +MX +2313-10625 America/Mazatlan Baja California Sur, Nayarit (most areas), Sinaloa +MX +2048-10515 America/Bahia_Banderas Bahía de Banderas +MX +2904-11058 America/Hermosillo Sonora +MX +3232-11701 America/Tijuana Baja California +MY,BN +0133+11020 Asia/Kuching Sabah, Sarawak +MZ,BI,BW,CD,MW,RW,ZM,ZW -2558+03235 Africa/Maputo Central Africa Time +NA -2234+01706 Africa/Windhoek +NC -2216+16627 Pacific/Noumea +NF -2903+16758 Pacific/Norfolk +NG,AO,BJ,CD,CF,CG,CM,GA,GQ,NE +0627+00324 Africa/Lagos West Africa Time +NI +1209-08617 America/Managua +NP +2743+08519 Asia/Kathmandu +NR -0031+16655 Pacific/Nauru +NU -1901-16955 Pacific/Niue +NZ,AQ -3652+17446 Pacific/Auckland New Zealand time +NZ -4357-17633 Pacific/Chatham Chatham Islands +PA,CA,KY +0858-07932 America/Panama EST - ON (Atikokan), NU (Coral H) +PE -1203-07703 America/Lima +PF -1732-14934 Pacific/Tahiti Society Islands +PF -0900-13930 Pacific/Marquesas Marquesas Islands +PF -2308-13457 Pacific/Gambier Gambier Islands +PG,AQ,FM -0930+14710 Pacific/Port_Moresby Papua New Guinea (most areas), Chuuk, Yap, Dumont d’Urville +PG -0613+15534 Pacific/Bougainville Bougainville +PH +143512+1205804 Asia/Manila +PK +2452+06703 Asia/Karachi +PL +5215+02100 Europe/Warsaw +PM +4703-05620 America/Miquelon +PN -2504-13005 Pacific/Pitcairn +PR,AG,CA,AI,AW,BL,BQ,CW,DM,GD,GP,KN,LC,MF,MS,SX,TT,VC,VG,VI +182806-0660622 America/Puerto_Rico AST - QC (Lower North Shore) +PS +3130+03428 Asia/Gaza Gaza Strip +PS +313200+0350542 Asia/Hebron West Bank +PT +3843-00908 Europe/Lisbon Portugal (mainland) +PT +3238-01654 Atlantic/Madeira Madeira Islands +PT +3744-02540 Atlantic/Azores Azores +PW +0720+13429 Pacific/Palau +PY -2516-05740 America/Asuncion +QA,BH +2517+05132 Asia/Qatar +RO +4426+02606 Europe/Bucharest +RS,BA,HR,ME,MK,SI +4450+02030 Europe/Belgrade +RU +5443+02030 Europe/Kaliningrad MSK-01 - Kaliningrad +RU +554521+0373704 Europe/Moscow MSK+00 - Moscow area +# Mention RU and UA alphabetically. See “territorial claims” above. +RU,UA +4457+03406 Europe/Simferopol Crimea +RU +5836+04939 Europe/Kirov MSK+00 - Kirov +RU +4844+04425 Europe/Volgograd MSK+00 - Volgograd +RU +4621+04803 Europe/Astrakhan MSK+01 - Astrakhan +RU +5134+04602 Europe/Saratov MSK+01 - Saratov +RU +5420+04824 Europe/Ulyanovsk MSK+01 - Ulyanovsk +RU +5312+05009 Europe/Samara MSK+01 - Samara, Udmurtia +RU +5651+06036 Asia/Yekaterinburg MSK+02 - Urals +RU +5500+07324 Asia/Omsk MSK+03 - Omsk +RU +5502+08255 Asia/Novosibirsk MSK+04 - Novosibirsk +RU +5322+08345 Asia/Barnaul MSK+04 - Altai +RU +5630+08458 Asia/Tomsk MSK+04 - Tomsk +RU +5345+08707 Asia/Novokuznetsk MSK+04 - Kemerovo +RU +5601+09250 Asia/Krasnoyarsk MSK+04 - Krasnoyarsk area +RU +5216+10420 Asia/Irkutsk MSK+05 - Irkutsk, Buryatia +RU +5203+11328 Asia/Chita MSK+06 - Zabaykalsky +RU +6200+12940 Asia/Yakutsk MSK+06 - Lena River +RU +623923+1353314 Asia/Khandyga MSK+06 - Tomponsky, Ust-Maysky +RU +4310+13156 Asia/Vladivostok MSK+07 - Amur River +RU +643337+1431336 Asia/Ust-Nera MSK+07 - Oymyakonsky +RU +5934+15048 Asia/Magadan MSK+08 - Magadan +RU +4658+14242 Asia/Sakhalin MSK+08 - Sakhalin Island +RU +6728+15343 Asia/Srednekolymsk MSK+08 - Sakha (E), N Kuril Is +RU +5301+15839 Asia/Kamchatka MSK+09 - Kamchatka +RU +6445+17729 Asia/Anadyr MSK+09 - Bering Sea +SA,AQ,KW,YE +2438+04643 Asia/Riyadh Syowa +SB,FM -0932+16012 Pacific/Guadalcanal Pohnpei +SD +1536+03232 Africa/Khartoum +SG,AQ,MY +0117+10351 Asia/Singapore peninsular Malaysia, Concordia +SR +0550-05510 America/Paramaribo +SS +0451+03137 Africa/Juba +ST +0020+00644 Africa/Sao_Tome +SV +1342-08912 America/El_Salvador +SY +3330+03618 Asia/Damascus +TC +2128-07108 America/Grand_Turk +TD +1207+01503 Africa/Ndjamena +TH,CX,KH,LA,VN +1345+10031 Asia/Bangkok north Vietnam +TJ +3835+06848 Asia/Dushanbe +TK -0922-17114 Pacific/Fakaofo +TL -0833+12535 Asia/Dili +TM +3757+05823 Asia/Ashgabat +TN +3648+01011 Africa/Tunis +TO -210800-1751200 Pacific/Tongatapu +TR +4101+02858 Europe/Istanbul +TW +2503+12130 Asia/Taipei +UA +5026+03031 Europe/Kyiv most of Ukraine +US +404251-0740023 America/New_York Eastern (most areas) +US +421953-0830245 America/Detroit Eastern - MI (most areas) +US +381515-0854534 America/Kentucky/Louisville Eastern - KY (Louisville area) +US +364947-0845057 America/Kentucky/Monticello Eastern - KY (Wayne) +US +394606-0860929 America/Indiana/Indianapolis Eastern - IN (most areas) +US +384038-0873143 America/Indiana/Vincennes Eastern - IN (Da, Du, K, Mn) +US +410305-0863611 America/Indiana/Winamac Eastern - IN (Pulaski) +US +382232-0862041 America/Indiana/Marengo Eastern - IN (Crawford) +US +382931-0871643 America/Indiana/Petersburg Eastern - IN (Pike) +US +384452-0850402 America/Indiana/Vevay Eastern - IN (Switzerland) +US +415100-0873900 America/Chicago Central (most areas) +US +375711-0864541 America/Indiana/Tell_City Central - IN (Perry) +US +411745-0863730 America/Indiana/Knox Central - IN (Starke) +US +450628-0873651 America/Menominee Central - MI (Wisconsin border) +US +470659-1011757 America/North_Dakota/Center Central - ND (Oliver) +US +465042-1012439 America/North_Dakota/New_Salem Central - ND (Morton rural) +US +471551-1014640 America/North_Dakota/Beulah Central - ND (Mercer) +US +394421-1045903 America/Denver Mountain (most areas) +US +433649-1161209 America/Boise Mountain - ID (south), OR (east) +US,CA +332654-1120424 America/Phoenix MST - AZ (most areas), Creston BC +US +340308-1181434 America/Los_Angeles Pacific +US +611305-1495401 America/Anchorage Alaska (most areas) +US +581807-1342511 America/Juneau Alaska - Juneau area +US +571035-1351807 America/Sitka Alaska - Sitka area +US +550737-1313435 America/Metlakatla Alaska - Annette Island +US +593249-1394338 America/Yakutat Alaska - Yakutat +US +643004-1652423 America/Nome Alaska (west) +US +515248-1763929 America/Adak Alaska - western Aleutians +US +211825-1575130 Pacific/Honolulu Hawaii +UY -345433-0561245 America/Montevideo +UZ +3940+06648 Asia/Samarkand Uzbekistan (west) +UZ +4120+06918 Asia/Tashkent Uzbekistan (east) +VE +1030-06656 America/Caracas +VN +1045+10640 Asia/Ho_Chi_Minh south Vietnam +VU -1740+16825 Pacific/Efate +WS -1350-17144 Pacific/Apia +ZA,LS,SZ -2615+02800 Africa/Johannesburg +# +# The next section contains experimental tab-separated comments for +# use by user agents like tzselect that identify continents and oceans. +# +# For example, the comment ‘#@AQAntarctica/’ means the country code +# AQ is in the continent Antarctica regardless of the Zone name, +# so Pacific/Auckland should be listed under Antarctica as well as +# under the Pacific because its line’s country codes include AQ. +# +# If more than one country code is affected each is listed separated +# by commas, e.g., ‘#@IS,SHAtlantic/’. If a country code is in +# more than one continent or ocean, each is listed separated by +# commas, e.g., the second column of ‘#@CY,TRAsia/,Europe/’. +# +# These experimental comments are present only for country codes where +# the continent or ocean is not already obvious from the Zone name. +# For example, there is no such comment for RU since it already +# corresponds to Zone names starting with both ‘Europe/’ and ‘Asia/’. +# +#@AQ Antarctica/ +#@IS,SH Atlantic/ +#@CY,TR Asia/,Europe/ +#@SJ Arctic/ +#@CC,CX,KM,MG,YT Indian/ diff --git a/lib/python3.12/site-packages/tzdata/zoneinfo/zonenow.tab b/lib/python3.12/site-packages/tzdata/zoneinfo/zonenow.tab new file mode 100644 index 0000000000000000000000000000000000000000..1d64b3912461729615b137a6ad5fec87ddcd74dc --- /dev/null +++ b/lib/python3.12/site-packages/tzdata/zoneinfo/zonenow.tab @@ -0,0 +1,296 @@ +# tzdb timezone descriptions, for users who do not care about old timestamps +# +# This file is in the public domain. +# +# From Paul Eggert (2023-12-18): +# This file contains a table where each row stands for a timezone +# where civil timestamps are predicted to agree from now on. +# This file is like zone1970.tab (see zone1970.tab’s comments), +# but with the following changes: +# +# 1. Each timezone corresponds to a set of clocks that are planned +# to agree from now on. This is a larger set of clocks than in +# zone1970.tab, where each timezone’s clocks must agree from 1970 on. +# 2. The first column is irrelevant and ignored. +# 3. The table is sorted in a different way: +# first by standard time UTC offset; +# then, if DST is used, by daylight saving UTC offset; +# then by time zone abbreviation. +# 4. Every timezone has a nonempty comments column, with wording +# distinguishing the timezone only from other timezones with the +# same UTC offset at some point during the year. +# +# The format of this table is experimental, and may change in future versions. +# +# This table is intended as an aid for users, to help them select timezones +# appropriate for their practical needs. It is not intended to take or +# endorse any position on legal or territorial claims. +# +#XX coordinates TZ comments +# +# -11 - SST +XX -1416-17042 Pacific/Pago_Pago Midway; Samoa (SST) +# +# -11 +XX -1901-16955 Pacific/Niue Niue +# +# -10 - HST +XX +211825-1575130 Pacific/Honolulu Hawaii (HST) +# +# -10 +XX -1732-14934 Pacific/Tahiti Tahiti; Cook Islands +# +# -10/-09 - HST / HDT (North America DST) +XX +515248-1763929 America/Adak western Aleutians in Alaska (HST/HDT) +# +# -09:30 +XX -0900-13930 Pacific/Marquesas Marquesas +# +# -09 +XX -2308-13457 Pacific/Gambier Gambier +# +# -09/-08 - AKST/AKDT (North America DST) +XX +611305-1495401 America/Anchorage most of Alaska (AKST/AKDT) +# +# -08 +XX -2504-13005 Pacific/Pitcairn Pitcairn +# +# -08/-07 - PST/PDT (North America DST) +XX +340308-1181434 America/Los_Angeles Pacific (PST/PDT) - US & Canada; Mexico near US border +# +# -07 - MST +XX +332654-1120424 America/Phoenix Mountain Standard (MST) - Arizona; western Mexico; Yukon +# +# -07/-06 - MST/MDT (North America DST) +XX +394421-1045903 America/Denver Mountain (MST/MDT) - US & Canada; Mexico near US border +# +# -06 +XX -0054-08936 Pacific/Galapagos Galápagos +# +# -06 - CST +XX +1924-09909 America/Mexico_City Central Standard (CST) - Saskatchewan; central Mexico; Central America +# +# -06/-05 (Chile DST) +XX -2709-10926 Pacific/Easter Easter Island +# +# -06/-05 - CST/CDT (North America DST) +XX +415100-0873900 America/Chicago Central (CST/CDT) - US & Canada; Mexico near US border +# +# -05 +XX -1203-07703 America/Lima eastern South America +# +# -05 - EST +XX +175805-0764736 America/Jamaica Eastern Standard (EST) - Caymans; Jamaica; eastern Mexico; Panama +# +# -05/-04 - CST/CDT (Cuba DST) +XX +2308-08222 America/Havana Cuba +# +# -05/-04 - EST/EDT (North America DST) +XX +404251-0740023 America/New_York Eastern (EST/EDT) - US & Canada +# +# -04 +XX +1030-06656 America/Caracas western South America +# +# -04 - AST +XX +1828-06954 America/Santo_Domingo Atlantic Standard (AST) - eastern Caribbean +# +# -04/-03 (Chile DST) +XX -3327-07040 America/Santiago most of Chile +# +# -04/-03 - AST/ADT (North America DST) +XX +4439-06336 America/Halifax Atlantic (AST/ADT) - Canada; Bermuda +# +# -03:30/-02:30 - NST/NDT (North America DST) +XX +4734-05243 America/St_Johns Newfoundland (NST/NDT) +# +# -03 +XX -2332-04637 America/Sao_Paulo eastern and southern South America +# +# -03/-02 (North America DST) +XX +4703-05620 America/Miquelon St Pierre & Miquelon +# +# -02 +XX -0351-03225 America/Noronha Fernando de Noronha; South Georgia +# +# -02/-01 (EU DST) +XX +6411-05144 America/Nuuk most of Greenland +# +# -01 +XX +1455-02331 Atlantic/Cape_Verde Cape Verde +# +# -01/+00 (EU DST) +XX +3744-02540 Atlantic/Azores Azores +# +# +00 - GMT +XX +0519-00402 Africa/Abidjan far western Africa; Iceland (GMT) +# +# +00/+01 - GMT/BST (EU DST) +XX +513030-0000731 Europe/London United Kingdom (GMT/BST) +# +# +00/+01 - WET/WEST (EU DST) +XX +3843-00908 Europe/Lisbon western Europe (WET/WEST) +# +# +00/+02 - Troll DST +XX -720041+0023206 Antarctica/Troll Troll Station in Antarctica +# +# +01 - CET +XX +3647+00303 Africa/Algiers Algeria, Tunisia (CET) +# +# +01 - WAT +XX +0627+00324 Africa/Lagos western Africa (WAT) +# +# +01/+00 - IST/GMT (EU DST in reverse) +XX +5320-00615 Europe/Dublin Ireland (IST/GMT) +# +# +01/+00 - (Morocco DST) +XX +3339-00735 Africa/Casablanca Morocco +# +# +01/+02 - CET/CEST (EU DST) +XX +4852+00220 Europe/Paris central Europe (CET/CEST) +# +# +02 - CAT +XX -2558+03235 Africa/Maputo central Africa (CAT) +# +# +02 - EET +XX +3254+01311 Africa/Tripoli Libya; Kaliningrad (EET) +# +# +02 - SAST +XX -2615+02800 Africa/Johannesburg southern Africa (SAST) +# +# +02/+03 - EET/EEST (EU DST) +XX +3758+02343 Europe/Athens eastern Europe (EET/EEST) +# +# +02/+03 - EET/EEST (Egypt DST) +XX +3003+03115 Africa/Cairo Egypt +# +# +02/+03 - EET/EEST (Lebanon DST) +XX +3353+03530 Asia/Beirut Lebanon +# +# +02/+03 - EET/EEST (Moldova DST) +XX +4700+02850 Europe/Chisinau Moldova +# +# +02/+03 - EET/EEST (Palestine DST) +XX +3130+03428 Asia/Gaza Palestine +# +# +02/+03 - IST/IDT (Israel DST) +XX +314650+0351326 Asia/Jerusalem Israel +# +# +03 +XX +4101+02858 Europe/Istanbul Near East; Belarus +# +# +03 - EAT +XX -0117+03649 Africa/Nairobi eastern Africa (EAT) +# +# +03 - MSK +XX +554521+0373704 Europe/Moscow Moscow (MSK) +# +# +03:30 +XX +3540+05126 Asia/Tehran Iran +# +# +04 +XX +2518+05518 Asia/Dubai Russia; Caucasus; Persian Gulf; Seychelles; Réunion +# +# +04:30 +XX +3431+06912 Asia/Kabul Afghanistan +# +# +05 +XX +4120+06918 Asia/Tashkent Russia; Kazakhstan; Tajikistan; Turkmenistan; Uzbekistan; Maldives +# +# +05 - PKT +XX +2452+06703 Asia/Karachi Pakistan (PKT) +# +# +05:30 +XX +0656+07951 Asia/Colombo Sri Lanka +# +# +05:30 - IST +XX +2232+08822 Asia/Kolkata India (IST) +# +# +05:45 +XX +2743+08519 Asia/Kathmandu Nepal +# +# +06 +XX +2343+09025 Asia/Dhaka Russia; Kyrgyzstan; Bhutan; Bangladesh; Chagos +# +# +06:30 +XX +1647+09610 Asia/Yangon Myanmar; Cocos +# +# +07 +XX +1345+10031 Asia/Bangkok Russia; Indochina; Christmas Island +# +# +07 - WIB +XX -0610+10648 Asia/Jakarta Indonesia (WIB) +# +# +08 +XX +0117+10351 Asia/Singapore Russia; Brunei; Malaysia; Singapore; Concordia +# +# +08 - AWST +XX -3157+11551 Australia/Perth Western Australia (AWST) +# +# +08 - CST +XX +3114+12128 Asia/Shanghai China (CST) +# +# +08 - HKT +XX +2217+11409 Asia/Hong_Kong Hong Kong (HKT) +# +# +08 - PHT +XX +143512+1205804 Asia/Manila Philippines (PHT) +# +# +08 - WITA +XX -0507+11924 Asia/Makassar Indonesia (WITA) +# +# +08:45 +XX -3143+12852 Australia/Eucla Eucla +# +# +09 +XX +5203+11328 Asia/Chita Russia; Palau; East Timor +# +# +09 - JST +XX +353916+1394441 Asia/Tokyo Japan (JST); Eyre Bird Observatory +# +# +09 - KST +XX +3733+12658 Asia/Seoul Korea (KST) +# +# +09 - WIT +XX -0232+14042 Asia/Jayapura Indonesia (WIT) +# +# +09:30 - ACST +XX -1228+13050 Australia/Darwin Northern Territory (ACST) +# +# +09:30/+10:30 - ACST/ACDT (Australia DST) +XX -3455+13835 Australia/Adelaide South Australia (ACST/ACDT) +# +# +10 +XX +4310+13156 Asia/Vladivostok Russia; Yap; Chuuk; Papua New Guinea; Dumont d’Urville +# +# +10 - AEST +XX -2728+15302 Australia/Brisbane Queensland (AEST) +# +# +10 - ChST +XX +1328+14445 Pacific/Guam Mariana Islands (ChST) +# +# +10/+11 - AEST/AEDT (Australia DST) +XX -3352+15113 Australia/Sydney southeast Australia (AEST/AEDT) +# +# +10:30/+11 +XX -3133+15905 Australia/Lord_Howe Lord Howe Island +# +# +11 +XX -0613+15534 Pacific/Bougainville Russia; Kosrae; Bougainville; Solomons +# +# +11/+12 (Australia DST) +XX -2903+16758 Pacific/Norfolk Norfolk Island +# +# +12 +XX +5301+15839 Asia/Kamchatka Russia; Tuvalu; Fiji; etc. +# +# +12/+13 (New Zealand DST) +XX -3652+17446 Pacific/Auckland New Zealand (NZST/NZDT) +# +# +12:45/+13:45 (Chatham DST) +XX -4357-17633 Pacific/Chatham Chatham Islands +# +# +13 +XX -210800-1751200 Pacific/Tongatapu Kanton; Tokelau; Samoa (western); Tonga +# +# +14 +XX +0152-15720 Pacific/Kiritimati Kiritimati diff --git a/lib/python3.12/site-packages/tzdata/zones b/lib/python3.12/site-packages/tzdata/zones new file mode 100644 index 0000000000000000000000000000000000000000..d4c3ef55638268a2ab4d17f7db071075db94747d --- /dev/null +++ b/lib/python3.12/site-packages/tzdata/zones @@ -0,0 +1,598 @@ +Africa/Abidjan +Africa/Algiers +Africa/Bissau +Africa/Cairo +Africa/Casablanca +Africa/Ceuta +Africa/El_Aaiun +Africa/Johannesburg +Africa/Juba +Africa/Khartoum +Africa/Lagos +Africa/Maputo +Africa/Monrovia +Africa/Nairobi +Africa/Ndjamena +Africa/Sao_Tome +Africa/Tripoli +Africa/Tunis +Africa/Windhoek +America/Adak +America/Anchorage +America/Araguaina +America/Argentina/Buenos_Aires +America/Argentina/Catamarca +America/Argentina/Cordoba +America/Argentina/Jujuy +America/Argentina/La_Rioja +America/Argentina/Mendoza +America/Argentina/Rio_Gallegos +America/Argentina/Salta +America/Argentina/San_Juan +America/Argentina/San_Luis +America/Argentina/Tucuman +America/Argentina/Ushuaia +America/Asuncion +America/Bahia +America/Bahia_Banderas +America/Barbados +America/Belem +America/Belize +America/Boa_Vista +America/Bogota +America/Boise +America/Cambridge_Bay +America/Campo_Grande +America/Cancun +America/Caracas +America/Cayenne +America/Chicago +America/Chihuahua +America/Ciudad_Juarez +America/Costa_Rica +America/Coyhaique +America/Cuiaba +America/Danmarkshavn +America/Dawson +America/Dawson_Creek +America/Denver +America/Detroit +America/Edmonton +America/Eirunepe +America/El_Salvador +America/Fort_Nelson +America/Fortaleza +America/Glace_Bay +America/Goose_Bay +America/Grand_Turk +America/Guatemala +America/Guayaquil +America/Guyana +America/Halifax +America/Havana +America/Hermosillo +America/Indiana/Indianapolis +America/Indiana/Knox +America/Indiana/Marengo +America/Indiana/Petersburg +America/Indiana/Tell_City +America/Indiana/Vevay +America/Indiana/Vincennes +America/Indiana/Winamac +America/Inuvik +America/Iqaluit +America/Jamaica +America/Juneau +America/Kentucky/Louisville +America/Kentucky/Monticello +America/La_Paz +America/Lima +America/Los_Angeles +America/Maceio +America/Managua +America/Manaus +America/Martinique +America/Matamoros +America/Mazatlan +America/Menominee +America/Merida +America/Metlakatla +America/Mexico_City +America/Miquelon +America/Moncton +America/Monterrey +America/Montevideo +America/New_York +America/Nome +America/Noronha +America/North_Dakota/Beulah +America/North_Dakota/Center +America/North_Dakota/New_Salem +America/Nuuk +America/Ojinaga +America/Panama +America/Paramaribo +America/Phoenix +America/Port-au-Prince +America/Porto_Velho +America/Puerto_Rico +America/Punta_Arenas +America/Rankin_Inlet +America/Recife +America/Regina +America/Resolute +America/Rio_Branco +America/Santarem +America/Santiago +America/Santo_Domingo +America/Sao_Paulo +America/Scoresbysund +America/Sitka +America/St_Johns +America/Swift_Current +America/Tegucigalpa +America/Thule +America/Tijuana +America/Toronto +America/Vancouver +America/Whitehorse +America/Winnipeg +America/Yakutat +Antarctica/Casey +Antarctica/Davis +Antarctica/Macquarie +Antarctica/Mawson +Antarctica/Palmer +Antarctica/Rothera +Antarctica/Troll +Antarctica/Vostok +Asia/Almaty +Asia/Amman +Asia/Anadyr +Asia/Aqtau +Asia/Aqtobe +Asia/Ashgabat +Asia/Atyrau +Asia/Baghdad +Asia/Baku +Asia/Bangkok +Asia/Barnaul +Asia/Beirut +Asia/Bishkek +Asia/Chita +Asia/Colombo +Asia/Damascus +Asia/Dhaka +Asia/Dili +Asia/Dubai +Asia/Dushanbe +Asia/Famagusta +Asia/Gaza +Asia/Hebron +Asia/Ho_Chi_Minh +Asia/Hong_Kong +Asia/Hovd +Asia/Irkutsk +Asia/Jakarta +Asia/Jayapura +Asia/Jerusalem +Asia/Kabul +Asia/Kamchatka +Asia/Karachi +Asia/Kathmandu +Asia/Khandyga +Asia/Kolkata +Asia/Krasnoyarsk +Asia/Kuching +Asia/Macau +Asia/Magadan +Asia/Makassar +Asia/Manila +Asia/Nicosia +Asia/Novokuznetsk +Asia/Novosibirsk +Asia/Omsk +Asia/Oral +Asia/Pontianak +Asia/Pyongyang +Asia/Qatar +Asia/Qostanay +Asia/Qyzylorda +Asia/Riyadh +Asia/Sakhalin +Asia/Samarkand +Asia/Seoul +Asia/Shanghai +Asia/Singapore +Asia/Srednekolymsk +Asia/Taipei +Asia/Tashkent +Asia/Tbilisi +Asia/Tehran +Asia/Thimphu +Asia/Tokyo +Asia/Tomsk +Asia/Ulaanbaatar +Asia/Urumqi +Asia/Ust-Nera +Asia/Vladivostok +Asia/Yakutsk +Asia/Yangon +Asia/Yekaterinburg +Asia/Yerevan +Atlantic/Azores +Atlantic/Bermuda +Atlantic/Canary +Atlantic/Cape_Verde +Atlantic/Faroe +Atlantic/Madeira +Atlantic/South_Georgia +Atlantic/Stanley +Australia/Adelaide +Australia/Brisbane +Australia/Broken_Hill +Australia/Darwin +Australia/Eucla +Australia/Hobart +Australia/Lindeman +Australia/Lord_Howe +Australia/Melbourne +Australia/Perth +Australia/Sydney +Etc/GMT +Etc/GMT+1 +Etc/GMT+10 +Etc/GMT+11 +Etc/GMT+12 +Etc/GMT+2 +Etc/GMT+3 +Etc/GMT+4 +Etc/GMT+5 +Etc/GMT+6 +Etc/GMT+7 +Etc/GMT+8 +Etc/GMT+9 +Etc/GMT-1 +Etc/GMT-10 +Etc/GMT-11 +Etc/GMT-12 +Etc/GMT-13 +Etc/GMT-14 +Etc/GMT-2 +Etc/GMT-3 +Etc/GMT-4 +Etc/GMT-5 +Etc/GMT-6 +Etc/GMT-7 +Etc/GMT-8 +Etc/GMT-9 +Etc/UTC +Europe/Andorra +Europe/Astrakhan +Europe/Athens +Europe/Belgrade +Europe/Berlin +Europe/Brussels +Europe/Bucharest +Europe/Budapest +Europe/Chisinau +Europe/Dublin +Europe/Gibraltar +Europe/Helsinki +Europe/Istanbul +Europe/Kaliningrad +Europe/Kirov +Europe/Kyiv +Europe/Lisbon +Europe/London +Europe/Madrid +Europe/Malta +Europe/Minsk +Europe/Moscow +Europe/Paris +Europe/Prague +Europe/Riga +Europe/Rome +Europe/Samara +Europe/Saratov +Europe/Simferopol +Europe/Sofia +Europe/Tallinn +Europe/Tirane +Europe/Ulyanovsk +Europe/Vienna +Europe/Vilnius +Europe/Volgograd +Europe/Warsaw +Europe/Zurich +Factory +Indian/Chagos +Indian/Maldives +Indian/Mauritius +Pacific/Apia +Pacific/Auckland +Pacific/Bougainville +Pacific/Chatham +Pacific/Easter +Pacific/Efate +Pacific/Fakaofo +Pacific/Fiji +Pacific/Galapagos +Pacific/Gambier +Pacific/Guadalcanal +Pacific/Guam +Pacific/Honolulu +Pacific/Kanton +Pacific/Kiritimati +Pacific/Kosrae +Pacific/Kwajalein +Pacific/Marquesas +Pacific/Nauru +Pacific/Niue +Pacific/Norfolk +Pacific/Noumea +Pacific/Pago_Pago +Pacific/Palau +Pacific/Pitcairn +Pacific/Port_Moresby +Pacific/Rarotonga +Pacific/Tahiti +Pacific/Tarawa +Pacific/Tongatapu +GMT +Australia/ACT +Australia/LHI +Australia/NSW +Australia/North +Australia/Queensland +Australia/South +Australia/Tasmania +Australia/Victoria +Australia/West +Australia/Yancowinna +Brazil/Acre +Brazil/DeNoronha +Brazil/East +Brazil/West +CET +CST6CDT +Canada/Atlantic +Canada/Central +Canada/Eastern +Canada/Mountain +Canada/Newfoundland +Canada/Pacific +Canada/Saskatchewan +Canada/Yukon +Chile/Continental +Chile/EasterIsland +Cuba +EET +EST +EST5EDT +Egypt +Eire +Etc/GMT+0 +Etc/GMT-0 +Etc/GMT0 +Etc/Greenwich +Etc/UCT +Etc/Universal +Etc/Zulu +GB +GB-Eire +GMT+0 +GMT-0 +GMT0 +Greenwich +Hongkong +Iceland +Iran +Israel +Jamaica +Japan +Kwajalein +Libya +MET +MST +MST7MDT +Mexico/BajaNorte +Mexico/BajaSur +Mexico/General +NZ +NZ-CHAT +Navajo +PRC +Poland +Portugal +ROC +ROK +Singapore +Turkey +UCT +US/Alaska +US/Aleutian +US/Arizona +US/Central +US/East-Indiana +US/Eastern +US/Hawaii +US/Indiana-Starke +US/Michigan +US/Mountain +US/Pacific +US/Samoa +UTC +Universal +W-SU +Zulu +America/Buenos_Aires +America/Catamarca +America/Cordoba +America/Indianapolis +America/Jujuy +America/Knox_IN +America/Louisville +America/Mendoza +America/Virgin +Pacific/Samoa +Africa/Accra +Africa/Addis_Ababa +Africa/Asmara +Africa/Bamako +Africa/Bangui +Africa/Banjul +Africa/Blantyre +Africa/Brazzaville +Africa/Bujumbura +Africa/Conakry +Africa/Dakar +Africa/Dar_es_Salaam +Africa/Djibouti +Africa/Douala +Africa/Freetown +Africa/Gaborone +Africa/Harare +Africa/Kampala +Africa/Kigali +Africa/Kinshasa +Africa/Libreville +Africa/Lome +Africa/Luanda +Africa/Lubumbashi +Africa/Lusaka +Africa/Malabo +Africa/Maseru +Africa/Mbabane +Africa/Mogadishu +Africa/Niamey +Africa/Nouakchott +Africa/Ouagadougou +Africa/Porto-Novo +America/Anguilla +America/Antigua +America/Aruba +America/Atikokan +America/Blanc-Sablon +America/Cayman +America/Creston +America/Curacao +America/Dominica +America/Grenada +America/Guadeloupe +America/Kralendijk +America/Lower_Princes +America/Marigot +America/Montserrat +America/Nassau +America/Port_of_Spain +America/St_Barthelemy +America/St_Kitts +America/St_Lucia +America/St_Thomas +America/St_Vincent +America/Tortola +Antarctica/DumontDUrville +Antarctica/McMurdo +Antarctica/Syowa +Arctic/Longyearbyen +Asia/Aden +Asia/Bahrain +Asia/Brunei +Asia/Kuala_Lumpur +Asia/Kuwait +Asia/Muscat +Asia/Phnom_Penh +Asia/Vientiane +Atlantic/Reykjavik +Atlantic/St_Helena +Europe/Amsterdam +Europe/Bratislava +Europe/Busingen +Europe/Copenhagen +Europe/Guernsey +Europe/Isle_of_Man +Europe/Jersey +Europe/Ljubljana +Europe/Luxembourg +Europe/Mariehamn +Europe/Monaco +Europe/Oslo +Europe/Podgorica +Europe/San_Marino +Europe/Sarajevo +Europe/Skopje +Europe/Stockholm +Europe/Vaduz +Europe/Vatican +Europe/Zagreb +Indian/Antananarivo +Indian/Christmas +Indian/Cocos +Indian/Comoro +Indian/Kerguelen +Indian/Mahe +Indian/Mayotte +Indian/Reunion +Pacific/Chuuk +Pacific/Funafuti +Pacific/Majuro +Pacific/Midway +Pacific/Pohnpei +Pacific/Saipan +Pacific/Wake +Pacific/Wallis +Africa/Timbuktu +America/Argentina/ComodRivadavia +America/Atka +America/Coral_Harbour +America/Ensenada +America/Fort_Wayne +America/Montreal +America/Nipigon +America/Pangnirtung +America/Porto_Acre +America/Rainy_River +America/Rosario +America/Santa_Isabel +America/Shiprock +America/Thunder_Bay +America/Yellowknife +Antarctica/South_Pole +Asia/Choibalsan +Asia/Chongqing +Asia/Harbin +Asia/Kashgar +Asia/Tel_Aviv +Atlantic/Jan_Mayen +Australia/Canberra +Australia/Currie +Europe/Belfast +Europe/Tiraspol +Europe/Uzhgorod +Europe/Zaporozhye +Pacific/Enderbury +Pacific/Johnston +Pacific/Yap +WET +Africa/Asmera +America/Godthab +Asia/Ashkhabad +Asia/Calcutta +Asia/Chungking +Asia/Dacca +Asia/Istanbul +Asia/Katmandu +Asia/Macao +Asia/Rangoon +Asia/Saigon +Asia/Thimbu +Asia/Ujung_Pandang +Asia/Ulan_Bator +Atlantic/Faeroe +Europe/Kiev +Europe/Nicosia +HST +PST8PDT +Pacific/Ponape +Pacific/Truk diff --git a/lib/python3.12/site-packages/urllib3-2.6.3.dist-info/INSTALLER b/lib/python3.12/site-packages/urllib3-2.6.3.dist-info/INSTALLER new file mode 100644 index 0000000000000000000000000000000000000000..a1b589e38a32041e49332e5e81c2d363dc418d68 --- /dev/null +++ b/lib/python3.12/site-packages/urllib3-2.6.3.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/lib/python3.12/site-packages/urllib3-2.6.3.dist-info/METADATA b/lib/python3.12/site-packages/urllib3-2.6.3.dist-info/METADATA new file mode 100644 index 0000000000000000000000000000000000000000..b75670fb69f700549164c9ebfc8486eaed0d73ae --- /dev/null +++ b/lib/python3.12/site-packages/urllib3-2.6.3.dist-info/METADATA @@ -0,0 +1,164 @@ +Metadata-Version: 2.4 +Name: urllib3 +Version: 2.6.3 +Summary: HTTP library with thread-safe connection pooling, file post, and more. +Project-URL: Changelog, https://github.com/urllib3/urllib3/blob/main/CHANGES.rst +Project-URL: Documentation, https://urllib3.readthedocs.io +Project-URL: Code, https://github.com/urllib3/urllib3 +Project-URL: Issue tracker, https://github.com/urllib3/urllib3/issues +Author-email: Andrey Petrov +Maintainer-email: Seth Michael Larson , Quentin Pradet , Illia Volochii +License-Expression: MIT +License-File: LICENSE.txt +Keywords: filepost,http,httplib,https,pooling,ssl,threadsafe,urllib +Classifier: Environment :: Web Environment +Classifier: Intended Audience :: Developers +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +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.14 +Classifier: Programming Language :: Python :: Free Threading :: 2 - Beta +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Internet :: WWW/HTTP +Classifier: Topic :: Software Development :: Libraries +Requires-Python: >=3.9 +Provides-Extra: brotli +Requires-Dist: brotli>=1.2.0; (platform_python_implementation == 'CPython') and extra == 'brotli' +Requires-Dist: brotlicffi>=1.2.0.0; (platform_python_implementation != 'CPython') and extra == 'brotli' +Provides-Extra: h2 +Requires-Dist: h2<5,>=4; extra == 'h2' +Provides-Extra: socks +Requires-Dist: pysocks!=1.5.7,<2.0,>=1.5.6; extra == 'socks' +Provides-Extra: zstd +Requires-Dist: backports-zstd>=1.0.0; (python_version < '3.14') and extra == 'zstd' +Description-Content-Type: text/markdown + +

+ +![urllib3](https://github.com/urllib3/urllib3/raw/main/docs/_static/banner_github.svg) + +

+ +

+ PyPI Version + Python Versions + Join our Discord + Coverage Status + Build Status on GitHub + Documentation Status
+ OpenSSF Scorecard + SLSA 3 + CII Best Practices +

+ +urllib3 is a powerful, *user-friendly* HTTP client for Python. +urllib3 brings many critical features that are missing from the Python +standard libraries: + +- Thread safety. +- Connection pooling. +- Client-side SSL/TLS verification. +- File uploads with multipart encoding. +- Helpers for retrying requests and dealing with HTTP redirects. +- Support for gzip, deflate, brotli, and zstd encoding. +- Proxy support for HTTP and SOCKS. +- 100% test coverage. + +... and many more features, but most importantly: Our maintainers have a 15+ +year track record of maintaining urllib3 with the highest code standards and +attention to security and safety. + +[Much of the Python ecosystem already uses urllib3](https://urllib3.readthedocs.io/en/stable/#who-uses) +and you should too. + + +## Installing + +urllib3 can be installed with [pip](https://pip.pypa.io): + +```bash +$ python -m pip install urllib3 +``` + +Alternatively, you can grab the latest source code from [GitHub](https://github.com/urllib3/urllib3): + +```bash +$ git clone https://github.com/urllib3/urllib3.git +$ cd urllib3 +$ pip install . +``` + +## Getting Started + +urllib3 is easy to use: + +```python3 +>>> import urllib3 +>>> resp = urllib3.request("GET", "http://httpbin.org/robots.txt") +>>> resp.status +200 +>>> resp.data +b"User-agent: *\nDisallow: /deny\n" +``` + +urllib3 has usage and reference documentation at [urllib3.readthedocs.io](https://urllib3.readthedocs.io). + + +## Community + +urllib3 has a [community Discord channel](https://discord.gg/urllib3) for asking questions and +collaborating with other contributors. Drop by and say hello 👋 + + +## Contributing + +urllib3 happily accepts contributions. Please see our +[contributing documentation](https://urllib3.readthedocs.io/en/latest/contributing.html) +for some tips on getting started. + + +## Security Disclosures + +To report a security vulnerability, please use the +[Tidelift security contact](https://tidelift.com/security). +Tidelift will coordinate the fix and disclosure with maintainers. + + +## Maintainers + +Meet our maintainers since 2008: + +- Current Lead: [@illia-v](https://github.com/illia-v) (Illia Volochii) +- [@sethmlarson](https://github.com/sethmlarson) (Seth M. Larson) +- [@pquentin](https://github.com/pquentin) (Quentin Pradet) +- [@theacodes](https://github.com/theacodes) (Thea Flowers) +- [@haikuginger](https://github.com/haikuginger) (Jess Shapiro) +- [@lukasa](https://github.com/lukasa) (Cory Benfield) +- [@sigmavirus24](https://github.com/sigmavirus24) (Ian Stapleton Cordasco) +- [@shazow](https://github.com/shazow) (Andrey Petrov) + +👋 + + +## Sponsorship + +If your company benefits from this library, please consider [sponsoring its +development](https://urllib3.readthedocs.io/en/latest/sponsors.html). + + +## For Enterprise + +Professional support for urllib3 is available as part of the [Tidelift +Subscription][1]. Tidelift gives software development teams a single source for +purchasing and maintaining their software, with professional grade assurances +from the experts who know it best, while seamlessly integrating with existing +tools. + +[1]: https://tidelift.com/subscription/pkg/pypi-urllib3?utm_source=pypi-urllib3&utm_medium=referral&utm_campaign=readme diff --git a/lib/python3.12/site-packages/urllib3-2.6.3.dist-info/RECORD b/lib/python3.12/site-packages/urllib3-2.6.3.dist-info/RECORD new file mode 100644 index 0000000000000000000000000000000000000000..7b0b3f739d154e5eb540ffc2e5e0a7568bd123e1 --- /dev/null +++ b/lib/python3.12/site-packages/urllib3-2.6.3.dist-info/RECORD @@ -0,0 +1,79 @@ +urllib3-2.6.3.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +urllib3-2.6.3.dist-info/METADATA,sha256=6ROQzJr0mwGXOPHXObXNklEwwy6dPmrMCGPCHF2Ygu8,6901 +urllib3-2.6.3.dist-info/RECORD,, +urllib3-2.6.3.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87 +urllib3-2.6.3.dist-info/licenses/LICENSE.txt,sha256=Ew46ZNX91dCWp1JpRjSn2d8oRGnehuVzIQAmgEHj1oY,1093 +urllib3/__init__.py,sha256=JMo1tg1nIV1AeJ2vENC_Txfl0e5h6Gzl9DGVk1rWRbo,6979 +urllib3/__pycache__/__init__.cpython-312.pyc,, +urllib3/__pycache__/_base_connection.cpython-312.pyc,, +urllib3/__pycache__/_collections.cpython-312.pyc,, +urllib3/__pycache__/_request_methods.cpython-312.pyc,, +urllib3/__pycache__/_version.cpython-312.pyc,, +urllib3/__pycache__/connection.cpython-312.pyc,, +urllib3/__pycache__/connectionpool.cpython-312.pyc,, +urllib3/__pycache__/exceptions.cpython-312.pyc,, +urllib3/__pycache__/fields.cpython-312.pyc,, +urllib3/__pycache__/filepost.cpython-312.pyc,, +urllib3/__pycache__/poolmanager.cpython-312.pyc,, +urllib3/__pycache__/response.cpython-312.pyc,, +urllib3/_base_connection.py,sha256=T1cwH3RhzsrBh6Bz3AOGVDboRsE7veijqZPXXQTR2Rg,5568 +urllib3/_collections.py,sha256=UvV7UqtGTSKdvw8N_LxWuEikZLm5gB1zFfTZYH9KhAk,17595 +urllib3/_request_methods.py,sha256=gCeF85SO_UU4WoPwYHIoz_tw-eM_EVOkLFp8OFsC7DA,9931 +urllib3/_version.py,sha256=vKE8or0mmqgsFpVb7FYms-nNOVCPPAEifgxVrTaPByw,704 +urllib3/connection.py,sha256=1ZR2gqfFdIzTYIUwF0K5nftg26hLqU5nr1yHTdKb7WA,42800 +urllib3/connectionpool.py,sha256=ZEhudsa8BIubD2M0XoxBBsjxbsXwMgUScH7oQ9i-j1Y,43371 +urllib3/contrib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +urllib3/contrib/__pycache__/__init__.cpython-312.pyc,, +urllib3/contrib/__pycache__/pyopenssl.cpython-312.pyc,, +urllib3/contrib/__pycache__/socks.cpython-312.pyc,, +urllib3/contrib/emscripten/__init__.py,sha256=wyXve8rmqX7s2KqRQBxD5Wl48jzWPn5-1u_XoQBELVc,836 +urllib3/contrib/emscripten/__pycache__/__init__.cpython-312.pyc,, +urllib3/contrib/emscripten/__pycache__/connection.cpython-312.pyc,, +urllib3/contrib/emscripten/__pycache__/fetch.cpython-312.pyc,, +urllib3/contrib/emscripten/__pycache__/request.cpython-312.pyc,, +urllib3/contrib/emscripten/__pycache__/response.cpython-312.pyc,, +urllib3/contrib/emscripten/connection.py,sha256=giElsBoUsKVURbZzb8GCrJmqW23Xnvj2aNyQVF42slg,8960 +urllib3/contrib/emscripten/emscripten_fetch_worker.js,sha256=z1k3zZ4_hDKd3-tN7wzz8LHjHC2pxN_uu8B3k9D9A3c,3677 +urllib3/contrib/emscripten/fetch.py,sha256=5xcd--viFxZd2nBy0aK73dtJ9Tsh1yYZU_SUXwnwibk,23520 +urllib3/contrib/emscripten/request.py,sha256=mL28szy1KvE3NJhWor5jNmarp8gwplDU-7gwGZY5g0Q,566 +urllib3/contrib/emscripten/response.py,sha256=7oVPENYZHuzEGRtG40HonpH5tAIYHsGcHPbJt2Z0U-Y,9507 +urllib3/contrib/pyopenssl.py,sha256=4awTja4o3beTGTGmmWo_3rBoEgzje95Q4bgWz4iiSx8,19724 +urllib3/contrib/socks.py,sha256=eB2eWfu8Wz1fn-qvr_qE_dZAceck2Ncv7XQ15DlvVbU,7547 +urllib3/exceptions.py,sha256=eeQ77nJjF97bP6SvCK4gmx6BpQZKU8yjvM-AIDwZdX8,9952 +urllib3/fields.py,sha256=FCf7UULSkf10cuTRUWTQESzxgl1WT8e2aCy3kfyZins,10829 +urllib3/filepost.py,sha256=U8eNZ-mpKKHhrlbHEEiTxxgK16IejhEa7uz42yqA_dI,2388 +urllib3/http2/__init__.py,sha256=xzrASH7R5ANRkPJOot5lGnATOq3KKuyXzI42rcnwmqs,1741 +urllib3/http2/__pycache__/__init__.cpython-312.pyc,, +urllib3/http2/__pycache__/connection.cpython-312.pyc,, +urllib3/http2/__pycache__/probe.cpython-312.pyc,, +urllib3/http2/connection.py,sha256=bHMH6fNvatwXPrKqrcn74yA3pUWcqPDppnK1LcKCbP8,12578 +urllib3/http2/probe.py,sha256=nnAkqbhAakOiF75rz7W0udZ38Eeh_uD8fjV74N73FEI,3014 +urllib3/poolmanager.py,sha256=NYP5vkKfadGddaBacUk6z6u8rTP9wgCFGGjVtf1mkcc,23811 +urllib3/py.typed,sha256=UaCuPFa3H8UAakbt-5G8SPacldTOGvJv18pPjUJ5gDY,93 +urllib3/response.py,sha256=2VDtH9KrYNQLUbDYHZ3GgwzH3JZphkn_JoqtB7ozkt0,52931 +urllib3/util/__init__.py,sha256=-qeS0QceivazvBEKDNFCAI-6ACcdDOE4TMvo7SLNlAQ,1001 +urllib3/util/__pycache__/__init__.cpython-312.pyc,, +urllib3/util/__pycache__/connection.cpython-312.pyc,, +urllib3/util/__pycache__/proxy.cpython-312.pyc,, +urllib3/util/__pycache__/request.cpython-312.pyc,, +urllib3/util/__pycache__/response.cpython-312.pyc,, +urllib3/util/__pycache__/retry.cpython-312.pyc,, +urllib3/util/__pycache__/ssl_.cpython-312.pyc,, +urllib3/util/__pycache__/ssl_match_hostname.cpython-312.pyc,, +urllib3/util/__pycache__/ssltransport.cpython-312.pyc,, +urllib3/util/__pycache__/timeout.cpython-312.pyc,, +urllib3/util/__pycache__/url.cpython-312.pyc,, +urllib3/util/__pycache__/util.cpython-312.pyc,, +urllib3/util/__pycache__/wait.cpython-312.pyc,, +urllib3/util/connection.py,sha256=JjO722lzHlzLXPTkr9ZWBdhseXnMVjMSb1DJLVrXSnQ,4444 +urllib3/util/proxy.py,sha256=seP8-Q5B6bB0dMtwPj-YcZZQ30vHuLqRu-tI0JZ2fzs,1148 +urllib3/util/request.py,sha256=itpnC8ug7D4nVfDmGUCRMlgkARUQ13r_XMxSnzTwmpE,8363 +urllib3/util/response.py,sha256=vQE639uoEhj1vpjEdxu5lNIhJCSUZkd7pqllUI0BZOA,3374 +urllib3/util/retry.py,sha256=WOcIHVaxKf-dVb89lUbpvcpeM7rNYF_vsKsCOKw10Z8,19235 +urllib3/util/ssl_.py,sha256=Y9RNkWCIehDxIRvyFnHUjiMlPolm368GYMya2YdDOag,19929 +urllib3/util/ssl_match_hostname.py,sha256=Di7DU7zokoltapT_F0Sj21ffYxwaS_cE5apOtwueeyA,5845 +urllib3/util/ssltransport.py,sha256=Ez4O8pR_vT8dan_FvqBYS6dgDfBXEMfVfrzcdUoWfi4,8847 +urllib3/util/timeout.py,sha256=4eT1FVeZZU7h7mYD1Jq2OXNe4fxekdNvhoWUkZusRpA,10346 +urllib3/util/url.py,sha256=WRh-TMYXosmgp8m8lT4H5spoHw5yUjlcMCfU53AkoAs,15205 +urllib3/util/util.py,sha256=j3lbZK1jPyiwD34T8IgJzdWEZVT-4E-0vYIJi9UjeNA,1146 +urllib3/util/wait.py,sha256=_ph8IrUR3sqPqi0OopQgJUlH4wzkGeM5CiyA7XGGtmI,4423 diff --git a/lib/python3.12/site-packages/urllib3-2.6.3.dist-info/WHEEL b/lib/python3.12/site-packages/urllib3-2.6.3.dist-info/WHEEL new file mode 100644 index 0000000000000000000000000000000000000000..ae8ec1bdaa94d726ceb907542d76cbd5d38cafcd --- /dev/null +++ b/lib/python3.12/site-packages/urllib3-2.6.3.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: hatchling 1.28.0 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/lib/python3.12/site-packages/urllib3-2.6.3.dist-info/licenses/LICENSE.txt b/lib/python3.12/site-packages/urllib3-2.6.3.dist-info/licenses/LICENSE.txt new file mode 100644 index 0000000000000000000000000000000000000000..e6183d0276b26c5b87aecccf8d0d5bcd7b1148d4 --- /dev/null +++ b/lib/python3.12/site-packages/urllib3-2.6.3.dist-info/licenses/LICENSE.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2008-2020 Andrey Petrov and contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE.