diff --git a/bin/build_dependencies_unix.sh b/bin/build_dependencies_unix.sh index 6dfdeb12..f4805e85 100755 --- a/bin/build_dependencies_unix.sh +++ b/bin/build_dependencies_unix.sh @@ -22,6 +22,7 @@ SKIP_MPFR=no USE_GMP=gmp PATCH_GMP_ARM64=no BUILD_ARB=no +USE_GMP_GITHUB_MIRROR=no while [[ $# -gt 0 ]] do @@ -124,7 +125,7 @@ cd src # # # ------------------------------------------------------------------------- # -if [ $USE_GMP = "gmp" ]; then +if [ "$USE_GMP" = "gmp" ]; then # ----------------------------------------------------------------------- # # # @@ -132,7 +133,7 @@ if [ $USE_GMP = "gmp" ]; then # # # ----------------------------------------------------------------------- # - if [ $SKIP_GMP = "yes" ]; then + if [ "$SKIP_GMP" = "yes" ]; then echo echo -------------------------------------------- echo " skipping GMP" @@ -145,7 +146,7 @@ if [ $USE_GMP = "gmp" ]; then echo -------------------------------------------- echo - if [ $USE_GMP_GITHUB_MIRROR = "yes" ]; then + if [ "$USE_GMP_GITHUB_MIRROR" = "yes" ]; then # Needed in GitHub Actions because it is blocked from gmplib.org git clone https://github.com/oscarbenjamin/gmp_mirror.git cp gmp_mirror/gmp-$GMPVER.tar.xz . @@ -163,7 +164,7 @@ if [ $USE_GMP = "gmp" ]; then # from the GMP repo but was applied after the release of GMP 6.2.1. # This patch is no longer needed for GMP 6.3.0. # - if [ $PATCH_GMP_ARM64 = "yes" ]; then + if [ "$PATCH_GMP_ARM64" = "yes" ]; then echo echo -------------------------------------------- echo " patching GMP" @@ -260,7 +261,7 @@ fi # # # ------------------------------------------------------------------------- # -if [ $SKIP_MPFR = "yes" ]; then +if [ "$SKIP_MPFR" = "yes" ]; then echo echo -------------------------------------------- echo " skipping MPFR" @@ -326,7 +327,7 @@ cd .. # # # ------------------------------------------------------------------------- # -if [ $BUILD_ARB = "yes" ]; then +if [ "$BUILD_ARB" = "yes" ]; then echo echo -------------------------------------------- @@ -369,7 +370,7 @@ echo $PREFIX echo echo Versions: -if [ $SKIP_GMP = "yes" ]; then +if [ "$SKIP_GMP" = "yes" ]; then echo GMP: skipped else if [[ $USE_GMP = "gmp" ]]; then @@ -379,7 +380,7 @@ else fi fi -if [ $SKIP_MPFR = "yes" ]; then +if [ "$SKIP_MPFR" = "yes" ]; then echo MPFR: skipped else echo MPFR: $MPFRVER @@ -387,7 +388,7 @@ fi echo Flint: $FLINTVER -if [ $BUILD_ARB = "yes" ]; then +if [ "$BUILD_ARB" = "yes" ]; then echo Arb: $ARBVER fi echo diff --git a/coverage_plugin.py b/coverage_plugin.py index 8382dc26..549872df 100644 --- a/coverage_plugin.py +++ b/coverage_plugin.py @@ -62,7 +62,7 @@ def get_cython_build_rules(): @cache -def parse_all_cfile_lines(): +def parse_all_cfile_lines(excluded_line_patterns=()): """Parse all generated C files from the build directory.""" # # Each .c file can include code generated from multiple Cython files (e.g. @@ -77,6 +77,14 @@ def parse_all_cfile_lines(): # expensive. # all_code_lines = {} + all_excluded_lines = defaultdict(set) + if excluded_line_patterns: + pattern = re.compile("|".join([f"(?:{regex})" for regex in excluded_line_patterns])) + line_is_excluded = pattern.search + else: + line_is_excluded = lambda line: False + + source_cache = {} for c_file, _ in get_cython_build_rules(): @@ -85,13 +93,33 @@ def parse_all_cfile_lines(): for cython_file, line_map in cfile_lines.items(): if cython_file == '(tree fragment)': continue - elif cython_file in all_code_lines: + src_lines = source_cache.get(cython_file) + if src_lines is None: + src_path = src_dir / cython_file + if src_path.exists(): + with open(src_path, encoding="utf8") as src: + src_lines = src.read().splitlines() + else: + src_lines = [] + source_cache[cython_file] = src_lines + + excluded = set() + filtered_line_map = {} + for lineno, line in line_map.items(): + source_line = src_lines[lineno - 1] if 0 < lineno <= len(src_lines) else "" + if line_is_excluded(source_line) or line_is_excluded(line): + excluded.add(lineno) + else: + filtered_line_map[lineno] = line + if cython_file in all_code_lines: # Possibly need to merge the lines? - assert all_code_lines[cython_file] == line_map + assert all_code_lines[cython_file] == filtered_line_map + all_excluded_lines[cython_file].update(excluded) else: - all_code_lines[cython_file] = line_map + all_code_lines[cython_file] = filtered_line_map + all_excluded_lines[cython_file] = excluded - return all_code_lines + return all_code_lines, all_excluded_lines def parse_cfile_lines(c_file): @@ -102,6 +130,11 @@ def parse_cfile_lines(c_file): class Plugin(CoveragePlugin): """A coverage plugin for a spin/meson project with Cython code.""" + _excluded_line_patterns = () + + def configure(self, config): + # Match Cython's plugin behavior and respect coverage's exclusion regexes. + self._excluded_line_patterns = tuple(config.get_option("report:exclude_lines")) def file_tracer(self, filename): """Find a tracer for filename to handle trace events.""" @@ -121,7 +154,7 @@ def file_tracer(self, filename): def file_reporter(self, filename): """Return a file reporter for filename.""" srcfile = Path(filename).relative_to(src_dir) - return CyFileReporter(srcfile) + return CyFileReporter(srcfile, self._excluded_line_patterns) class CyFileTracer(FileTracer): @@ -157,7 +190,7 @@ def get_source_filename(filename): class CyFileReporter(FileReporter): """File reporter for Cython or Python files (.pyx,.pxd,.py).""" - def __init__(self, srcpath): + def __init__(self, srcpath, excluded_line_patterns): abspath = (src_dir / srcpath) assert abspath.exists() @@ -165,6 +198,7 @@ def __init__(self, srcpath): super().__init__(str(abspath)) self.srcpath = srcpath + self.excluded_line_patterns = excluded_line_patterns def relative_filename(self): """Path displayed in the coverage reports.""" @@ -173,10 +207,15 @@ def relative_filename(self): def lines(self): """Set of line numbers for possibly traceable lines.""" srcpath = str(self.srcpath) - all_line_maps = parse_all_cfile_lines() + all_line_maps, _ = parse_all_cfile_lines(self.excluded_line_patterns) line_map = all_line_maps[srcpath] return set(line_map) + def excluded_lines(self): + srcpath = str(self.srcpath) + _, all_excluded_lines = parse_all_cfile_lines(self.excluded_line_patterns) + return set(all_excluded_lines.get(srcpath, ())) + def coverage_init(reg, options): plugin = Plugin() diff --git a/pyproject.toml b/pyproject.toml index 05aa589c..efa88bef 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -87,6 +87,9 @@ package = "flint" "spin.cmds.meson.run", ] +[tool.mypy] +files = ["src"] + [tool.pytest.ini_options] addopts = "--import-mode=importlib" diff --git a/src/flint/__init__.py b/src/flint/__init__.py index 1401e17c..e7811272 100644 --- a/src/flint/__init__.py +++ b/src/flint/__init__.py @@ -50,6 +50,23 @@ __version__ = "0.8.0" + +def _flint_version_at_least(major: int, minor: int) -> bool: + version_parts = __FLINT_VERSION__.split(".") + if len(version_parts) < 2: + return False + try: + current_major = int(version_parts[0]) + current_minor = int(version_parts[1]) + except ValueError: + return False + return (current_major, current_minor) >= (major, minor) + + +def _has_acb_theta() -> bool: + return _flint_version_at_least(3, 1) + + __all__ = [ "ctx", "fmpz", diff --git a/src/flint/flint_base/flint_base.pyi b/src/flint/flint_base/flint_base.pyi index 760baa41..2c217b3d 100644 --- a/src/flint/flint_base/flint_base.pyi +++ b/src/flint/flint_base/flint_base.pyi @@ -87,6 +87,16 @@ class flint_poly(flint_elem, Generic[Telem]): def complex_roots(self) -> list[Any]: ... def derivative(self) -> Self: ... +class flint_mat(flint_elem, Generic[Telem]): + def nrows(self) -> int: ... + def ncols(self) -> int: ... + def __getitem__(self, index: tuple[int, int], /) -> Telem: ... + def __setitem__(self, index: tuple[int, int], value: Telem | int, /) -> None: ... + def entries(self) -> list[Telem]: ... + def table(self) -> list[list[Telem]]: ... + def tolist(self) -> list[list[Telem]]: ... + def __iter__(self) -> Iterator[Telem]: ... + class Ordering(enum.Enum): lex = "lex" deglex = "deglex" diff --git a/src/flint/flint_base/flint_context.pyi b/src/flint/flint_base/flint_context.pyi new file mode 100644 index 00000000..692b2caa --- /dev/null +++ b/src/flint/flint_base/flint_context.pyi @@ -0,0 +1,53 @@ +from __future__ import annotations + +from typing import Callable, ParamSpec, TypeVar + + +_P = ParamSpec("_P") +_R = TypeVar("_R") + + +class FlintContext: + pretty: bool + unicode: bool + + def __init__(self) -> None: ... + def default(self) -> None: ... + + @property + def prec(self) -> int: ... + @prec.setter + def prec(self, prec: int) -> None: ... + + @property + def dps(self) -> int: ... + @dps.setter + def dps(self, prec: int) -> None: ... + + @property + def cap(self) -> int: ... + @cap.setter + def cap(self, cap: int) -> None: ... + + @property + def threads(self) -> int: ... + @threads.setter + def threads(self, num: int) -> None: ... + + def extraprec(self, n: int) -> PrecisionManager: ... + def extradps(self, n: int) -> PrecisionManager: ... + def workprec(self, n: int) -> PrecisionManager: ... + def workdps(self, n: int) -> PrecisionManager: ... + + def __repr__(self) -> str: ... + def cleanup(self) -> None: ... + + +class PrecisionManager: + def __init__(self, ctx: FlintContext, eprec: int = -1, edps: int = -1) -> None: ... + def __call__(self, func: Callable[_P, _R]) -> Callable[_P, _R]: ... + def __enter__(self) -> None: ... + def __exit__(self, exc_type: object, value: object, traceback: object) -> None: ... + + +thectx: FlintContext diff --git a/src/flint/flint_base/meson.build b/src/flint/flint_base/meson.build index 6057efd0..328494f2 100644 --- a/src/flint/flint_base/meson.build +++ b/src/flint/flint_base/meson.build @@ -3,6 +3,7 @@ pkgdir = 'flint/flint_base' pyfiles = [ '__init__.py', 'flint_base.pyi', + 'flint_context.pyi', ] exts = [ diff --git a/src/flint/flintlib/functions/arf.pxd b/src/flint/flintlib/functions/arf.pxd index 3abbc68f..2767c302 100644 --- a/src/flint/flintlib/functions/arf.pxd +++ b/src/flint/flintlib/functions/arf.pxd @@ -16,6 +16,7 @@ from flint.flintlib.types.fmpq cimport fmpq_t # .. macro:: ARF_PREC_EXACT cdef extern from "flint/arf.h": + cdef const slong ARF_PREC_EXACT void arf_init(arf_t x) void arf_clear(arf_t x) slong arf_allocated_bytes(const arf_t x) diff --git a/src/flint/meson.build b/src/flint/meson.build index f76c7781..4feb64ea 100644 --- a/src/flint/meson.build +++ b/src/flint/meson.build @@ -4,6 +4,7 @@ pyfiles = [ '__init__.py', 'typing.py', 'py.typed', + 'pyflint.pyi', ] exts = [ diff --git a/src/flint/pyflint.pyi b/src/flint/pyflint.pyi new file mode 100644 index 00000000..4e7fc43a --- /dev/null +++ b/src/flint/pyflint.pyi @@ -0,0 +1,4 @@ +from .flint_base.flint_context import FlintContext + + +ctx: FlintContext diff --git a/src/flint/test/__main__.py b/src/flint/test/__main__.py index 01813465..ff913759 100644 --- a/src/flint/test/__main__.py +++ b/src/flint/test/__main__.py @@ -8,12 +8,39 @@ import doctest import traceback import argparse +import importlib +import inspect +import pkgutil +from typing import Callable import flint -from flint.test.test_all import all_tests from flint.test.test_docstrings import find_doctests +def collect_all_tests() -> list[Callable[[], object]]: + tests: list[Callable[[], object]] = [] + import flint.test as test_pkg + + for mod in pkgutil.iter_modules(test_pkg.__path__, test_pkg.__name__ + "."): + mod_name = mod.name.rsplit(".", 1)[-1] + if not mod_name.startswith("test_"): + continue + module = importlib.import_module(mod.name) + for name, obj in vars(module).items(): + if ( + name.startswith("test_") + and inspect.isfunction(obj) + and obj.__module__ == module.__name__ + and len(inspect.signature(obj).parameters) == 0 + ): + tests.append(obj) + + return tests + + +all_tests = collect_all_tests() + + def run_tests(verbose=None): """Run all tests diff --git a/src/flint/test/helpers.py b/src/flint/test/helpers.py new file mode 100644 index 00000000..638dc263 --- /dev/null +++ b/src/flint/test/helpers.py @@ -0,0 +1,171 @@ +from __future__ import annotations + +from typing import Callable, Sequence + +from flint import acb, acb_mat, acb_poly, acb_series, arb, arb_mat, arb_poly, arb_series + + +def raises(f: Callable[[], object], exception: type[Exception]) -> bool: + try: + f() + except exception: + return True + return False + + +def is_close_arb( + x: arb, + y: int | float | str | arb, + *, + tol: int | float | str | arb = 1e-10, + rel_tol: int | float | str | arb = 1e-10, + max_width: int | float | str | arb = 1e-10, +) -> bool: + y = arb(y) + tol_arb = arb(tol) + rel_tol_arb = arb(rel_tol) + max_width_arb = arb(max_width) + return ( + isinstance(x, arb) + and x.rad() < max_width_arb + and y.rad() < max_width_arb + and abs(x - y) <= tol_arb + rel_tol_arb * abs(y) + ) + + +def is_close_acb( + x: acb, + y: int | float | complex | str | acb, + *, + tol: int | float | str | arb = 1e-10, + rel_tol: int | float | str | arb = 1e-10, + max_width: int | float | str | arb = 1e-10, +) -> bool: + y = acb(y) + tol_arb = arb(tol) + rel_tol_arb = arb(rel_tol) + max_width_arb = arb(max_width) + return ( + isinstance(x, acb) + and x.rad() < max_width_arb + and y.rad() < max_width_arb + and abs(x - y) <= tol_arb + rel_tol_arb * abs(y) + ) + + +def is_close_arb_poly( + x: arb_poly, + y: arb_poly | int | float | arb, + *, + tol: int | float | str | arb = 1e-10, + rel_tol: int | float | str | arb = 1e-10, + max_width: int | float | str | arb = 1e-10, +) -> bool: + if not isinstance(x, arb_poly): + return False + if not isinstance(y, arb_poly): + y = arb_poly(y) + d = x - y + for i in range(d.length()): + if not is_close_arb(d[i], 0, tol=tol, rel_tol=rel_tol, max_width=max_width): + return False + return True + + +def is_close_acb_poly( + x: acb_poly, + y: acb_poly | int | float | complex | str | acb, + *, + tol: int | float | str | arb = 1e-10, + rel_tol: int | float | str | arb = 1e-10, + max_width: int | float | str | arb = 1e-10, +) -> bool: + if not isinstance(x, acb_poly): + return False + if not isinstance(y, acb_poly): + y = acb_poly(y) + d = x - y + for i in range(d.length()): + if not is_close_acb(d[i], 0, tol=tol, rel_tol=rel_tol, max_width=max_width): + return False + return True + + +def is_close_arb_series( + x: arb_series, + y: arb_series | Sequence[int | float | arb], + *, + tol: int | float | str | arb = 1e-10, + rel_tol: int | float | str | arb = 1e-10, + max_width: int | float | str | arb = 1e-10, +) -> bool: + if not isinstance(x, arb_series): + return False + if not isinstance(y, arb_series): + y = arb_series(y, prec=len(x)) + if x.prec != y.prec: + return False + for xc, yc in zip(x.coeffs(), y.coeffs()): + if not is_close_arb(xc, yc, tol=tol, rel_tol=rel_tol, max_width=max_width): + return False + return True + + +def is_close_arb_mat( + x: arb_mat, + y: arb_mat | Sequence[Sequence[int | float | str | arb]], + *, + tol: int | float | str | arb = 1e-10, + rel_tol: int | float | str | arb = 1e-10, + max_width: int | float | str | arb = 1e-10, +) -> bool: + if not isinstance(x, arb_mat): + return False + y = arb_mat(y) + if x.nrows() != y.nrows() or x.ncols() != y.ncols(): + return False + for i in range(x.nrows()): + for j in range(x.ncols()): + if not is_close_arb(x[i, j], y[i, j], tol=tol, rel_tol=rel_tol, max_width=max_width): + return False + return True + + +def is_close_acb_mat( + x: acb_mat, + y: acb_mat | Sequence[Sequence[int | float | complex | str | acb]], + *, + tol: int | float | str | arb = 1e-10, + rel_tol: int | float | str | arb = 1e-10, + max_width: int | float | str | arb = 1e-10, +) -> bool: + if not isinstance(x, acb_mat): + return False + y = acb_mat(y) + if x.nrows() != y.nrows() or x.ncols() != y.ncols(): + return False + for i in range(x.nrows()): + for j in range(x.ncols()): + if not is_close_acb(x[i, j], y[i, j], tol=tol, rel_tol=rel_tol, max_width=max_width): + return False + return True + + +def is_close_acb_series( + x: acb_series, + y: acb_series | Sequence[int | float | complex | acb], + *, + tol: int | float | str | arb = 1e-10, + rel_tol: int | float | str | arb = 1e-10, + max_width: int | float | str | arb = 1e-10, +) -> bool: + if not isinstance(x, acb_series): + return False + if not isinstance(y, acb_series): + y = acb_series(y, prec=len(x)) + if x.prec != y.prec: + return False + for xc, yc in zip(x.coeffs(), y.coeffs()): + if not is_close_acb(xc, yc, tol=tol, rel_tol=rel_tol, max_width=max_width): + return False + return True diff --git a/src/flint/test/meson.build b/src/flint/test/meson.build index 1d2af323..77c102c9 100644 --- a/src/flint/test/meson.build +++ b/src/flint/test/meson.build @@ -3,9 +3,21 @@ thisdir = 'flint/test' pyfiles = [ '__init__.py', '__main__.py', + 'helpers.py', 'test_all.py', + 'test_acb.py', + 'test_acb_mat.py', + 'test_acb_poly.py', + 'test_acb_series.py', + 'test_acb_theta.py', 'test_arb.py', + 'test_arb_mat.py', + 'test_arb_series.py', + 'test_arb_poly.py', + 'test_fmpq_vec.py', + 'test_fmpz_vec.py', 'test_docstrings.py', + 'test_dirichlet.py', ] py.install_sources( diff --git a/src/flint/test/test_acb.py b/src/flint/test/test_acb.py new file mode 100644 index 00000000..97898aaf --- /dev/null +++ b/src/flint/test/test_acb.py @@ -0,0 +1,597 @@ +"""Tests for python-flint's `acb` constructor.""" + +import math + +from flint import acb, arb, dirichlet_char, fmpz_poly +from flint.test.helpers import is_close_acb, is_close_arb, raises + + +def test_acb_constructor() -> None: + """Cover all branches of acb.__init__.""" + + z0 = acb() + assert z0.real == 0 + assert z0.imag == 0 + + z1 = acb(acb(1, 2)) + assert z1.real == 1 + assert z1.imag == 2 + + z2 = acb(3) + assert z2.real == 3 + assert z2.imag == 0 + + z3 = acb(4 + 5j) + assert z3.real == 4 + assert z3.imag == 5 + + class DummyMpc: + def __init__(self) -> None: + self._mpc_ = acb(6, 7)._mpc_ + + z4 = acb(DummyMpc()) + assert z4.real == 6 + assert z4.imag == 7 + + z5 = acb(8, 9) + assert z5.real == 8 + assert z5.imag == 9 + + assert raises(lambda: acb(1 + 2j, 3), ValueError) + assert raises(lambda: acb(object()), TypeError) # type: ignore[call-overload] + assert raises(lambda: acb(1, object()), TypeError) # type: ignore[call-overload] + + +def test_fmpz_poly_call_with_acb() -> None: + p = fmpz_poly([1, 2]) + assert is_close_acb(p(acb(1, 2)), acb(3, 4)) + assert is_close_acb(p(acb(1)), acb(3)) + + +def test_acb_is_zero() -> None: + assert acb().is_zero() is True + assert acb(0).is_zero() is True + assert acb(0, 0).is_zero() is True + assert acb(1).is_zero() is False + assert acb(0, 1).is_zero() is False + + +def test_acb_is_finite_and_exact() -> None: + assert acb(1, 2).is_finite() is True + assert acb("1 +/- 0.1").is_finite() is True + assert acb(float("inf")).is_finite() is False + assert acb(float("nan")).is_finite() is False + + assert acb(1, 2).is_exact() is True + assert acb("1 +/- 0.1").is_exact() is False + assert acb(1, "0 +/- 0.1").is_exact() is False + + +def test_acb_pos() -> None: + x = acb(2, 3) + y = +x + assert y == x + assert y is not x + + +def test_acb_neg_dunder() -> None: + x = acb(2, 3) + assert -x == acb(-2, -3) + + +def test_acb_neg_method() -> None: + x = acb(2, 3) + assert x.neg(exact=True) == acb(2, 3) + assert x.neg(exact=False) == acb(2, 3) + + +def test_acb_conjugate() -> None: + x = acb(2, 3) + assert x.conjugate(exact=True) == acb(2, -3) + assert x.conjugate(exact=False) == acb(2, -3) + + +def test_acb_abs() -> None: + x = acb(3, 4) + y = abs(x) + assert isinstance(y, arb) + assert y == arb(5) + + +def test_acb_add() -> None: + x = acb(2, 3) + assert x + acb(5, 7) == acb(7, 10) + assert x + 1 == acb(3, 3) + assert raises(lambda: x + object(), TypeError) # type: ignore[operator] + + +def test_acb_radd() -> None: + x = acb(2, 3) + assert 1 + x == acb(3, 3) + assert acb(5, 7) + x == acb(7, 10) + assert raises(lambda: object() + x, TypeError) # type: ignore[operator] + + +def test_acb_sub() -> None: + x = acb(2, 3) + assert x - acb(5, 7) == acb(-3, -4) + assert x - 1 == acb(1, 3) + assert raises(lambda: x - object(), TypeError) # type: ignore[operator] + + +def test_acb_rsub() -> None: + x = acb(2, 3) + assert 1 - x == acb(-1, -3) + assert acb(5, 7) - x == acb(3, 4) + assert raises(lambda: object() - x, TypeError) # type: ignore[operator] + + +def test_acb_mul() -> None: + x = acb(2, 3) + assert x * acb(5, 7) == acb(-11, 29) + assert x * 2 == acb(4, 6) + assert raises(lambda: x * object(), TypeError) # type: ignore[operator] + + +def test_acb_rmul() -> None: + x = acb(2, 3) + assert 2 * x == acb(4, 6) + assert acb(5, 7) * x == acb(-11, 29) + assert raises(lambda: object() * x, TypeError) # type: ignore[operator] + + +def test_acb_truediv() -> None: + x = acb(2, 3) + assert x / 2 == acb(1, 1.5) + assert is_close_acb(x / acb(2, -1), acb(0.2, 1.6)) + assert raises(lambda: x / object(), TypeError) # type: ignore[operator] + + +def test_acb_rtruediv() -> None: + x = acb(2, 3) + assert is_close_acb(2 / x, acb(4 / 13, -6 / 13)) + assert is_close_acb(acb(2, -1) / x, acb(0.07692307692307693, -0.6153846153846154)) + assert raises(lambda: object() / x, TypeError) # type: ignore[operator] + + +def test_acb_pow() -> None: + x = acb(2, 3) + assert x ** 2 == acb(-5, 12) + assert x ** acb(2) == acb(-5, 12) + assert raises(lambda: x ** object(), TypeError) # type: ignore[operator] + assert raises(lambda: pow(x, 2, 3), ValueError) # type: ignore[misc] + + +def test_acb_rpow() -> None: + x = acb(2, 3) + y = 2 ** x + assert is_close_acb(y, acb(2) ** x) + assert raises(lambda: object() ** x, TypeError) # type: ignore[operator] + assert raises(lambda: pow(2, x, 3), ValueError) # type: ignore[misc] + + +def test_acb_eq_ne() -> None: + x = acb(2, 3) + assert (x == acb(2, 3)) is True + assert (x != acb(2, 3)) is False + assert (x == acb(2, -3)) is False + assert (x != acb(2, -3)) is True + assert (x == (2 + 3j)) is True + assert (x != (2 + 3j)) is False + assert (x == 2) is False + assert (x != 2) is True + + +def test_acb_eq_ne_intervals() -> None: + a = acb("1 +/- 0.1", "2 +/- 0.2") + b = acb("1 +/- 0.1", "2 +/- 0.2") + c = acb(1, 2) + d = acb("1 +/- 0.2", "2 +/- 0.2") + e = acb("10 +/- 0.1", "2 +/- 0.2") + + assert (a == b) is False + assert (a != b) is False + assert (a == c) is False + assert (a != c) is False + assert (a == d) is False + assert (a != d) is False + assert (a == e) is False + assert (a != e) is True + + +def test_acb_eq_ne_notimplemented_path() -> None: + class Unsupported: + pass + + x = acb(2, 3) + assert (x == Unsupported()) is False + assert (x != Unsupported()) is True + + +def test_acb_ordering_raises() -> None: + x = acb(2, 3) + y = acb(3, 4) + assert raises(lambda: x < y, TypeError) # type: ignore[operator] + assert raises(lambda: x <= y, TypeError) # type: ignore[operator] + assert raises(lambda: x > y, TypeError) # type: ignore[operator] + assert raises(lambda: x >= y, TypeError) # type: ignore[operator] + + +def test_acb_contains_methods() -> None: + outer = acb("1 +/- 0.5", "2 +/- 0.5") + inner = acb("1 +/- 0.25", "2 +/- 0.25") + touching = acb("1.5 +/- 0.5", "2 +/- 0.5") + disjoint = acb(4, 5) + + assert (inner in outer) is True + assert (outer in inner) is False + assert outer.contains(inner) is True + assert outer.contains(1 + 2j) is True + assert outer.contains_interior(inner) is True + assert outer.contains_interior(outer) is False + assert outer.overlaps(touching) is True + assert outer.overlaps(disjoint) is False + assert acb(2).contains_integer() is True + assert acb(1.1).contains_integer() is False + assert raises(lambda: outer.contains(object()), TypeError) # type: ignore[arg-type] + + +def test_acb_union() -> None: + a = acb(1, 2) + b = acb("2 +/- 0.5", "3 +/- 0.5") + u = a.union(b) + assert u.contains(a) + assert u.contains(b) + + +def test_acb_mid_rad_complex_rad() -> None: + x = acb(arb(1, (1, -2)), arb(2, (1, -1))) + assert x.mid() == acb(1, 2) + assert is_close_arb(x.rad(), math.hypot(0.25, 0.5), tol=1e-8, rel_tol=1e-8) + assert is_close_acb(x.complex_rad(), acb(0.25, 0.5), tol=1e-8, rel_tol=1e-8) + + +def test_acb_repr_str() -> None: + x = acb(2) + y = acb(0, 3) + z = acb(2, -3) + w = acb(2, 3) + + assert x.repr().startswith("acb(") + assert ", " in z.repr() + + assert x.str() == x.real.str() + assert y.str().endswith("j") + assert " + " in w.str() + assert " - " in z.str() + + +def test_acb_complex_dunder() -> None: + x = acb(2, 3) + assert complex(x) == complex(2, 3) + + +def test_acb_abs_methods() -> None: + x = acb(3, -4) + y1 = abs(x) + y2 = x.__abs__() + assert is_close_arb(y1, 5.0) + assert is_close_arb(y2, 5.0) + assert is_close_arb(x.abs_lower(), 5.0) + assert is_close_arb(x.abs_upper(), 5.0) + + +def test_acb_csgn_sgn() -> None: + assert is_close_arb(acb(2, 3).csgn(), 1.0) + assert is_close_arb(acb(-1).csgn(), -1.0) + + assert is_close_acb(acb(-1).sgn(), acb(-1)) + assert is_close_acb(acb(0).sgn(), acb(0)) + assert is_close_acb(acb(5, 5).sgn(), acb(math.sqrt(0.5), math.sqrt(0.5))) + + +def test_acb_arg() -> None: + assert is_close_arb(acb(3.3).arg(), 0.0) + assert is_close_arb(acb(-1).arg(), math.pi) + + +def test_acb_elementary_transcendentals() -> None: + x = acb(1, 2) + y = acb(2, 3) + q = acb(0.25) + + assert is_close_acb(x.pow(3), x ** 3) + assert is_close_acb(x.log().exp(), x, tol=1e-8, rel_tol=1e-8) + assert is_close_acb(x.log1p(), acb(1.039720770839918, 0.7853981633974483)) + assert is_close_acb(acb(2).asin(), acb(1.5707963267948966, -1.3169578969248166)) + assert is_close_acb(acb(2).acos(), acb(0, 1.3169578969248166)) + assert is_close_acb(x.atan(), acb(1.3389725222944935, 0.4023594781085251)) + assert is_close_acb(y.asinh(), acb(1.9686379257930963, 0.9646585044076028)) + assert is_close_acb(y.acosh(), acb(1.9833870299165354, 1.0001435424737972)) + assert is_close_acb(y.atanh(), acb(0.14694666622552975, 1.3389725222944935)) + assert is_close_acb(acb.pi(), acb(math.pi)) + assert is_close_acb(x.sqrt(), acb(1.272019649514069, 0.7861513777574233)) + assert is_close_acb(x.rsqrt(), acb(0.5688644810057831, -0.35157758425414293)) + assert is_close_acb(x.exp(), acb(-1.1312043837568137, 2.4717266720048188)) + assert is_close_acb(x.exp_pi_i(), acb("-0.001867442731707988814430213")) + assert is_close_acb(acb("1e-8").expm1(), acb("1e-8"), tol=1e-12, rel_tol=1e-12) + + s1, c1 = x.sin_cos() + assert is_close_acb(s1, x.sin()) + assert is_close_acb(c1, x.cos()) + sp, cp = x.sin_cos_pi() + assert is_close_acb(sp, x.sin_pi()) + assert is_close_acb(cp, x.cos_pi()) + sh, ch = x.sinh_cosh() + assert is_close_acb(sh, x.sinh()) + assert is_close_acb(ch, x.cosh()) + + assert is_close_acb(x.cot(), 1 / x.tan(), tol=1e-8, rel_tol=1e-8) + assert is_close_acb(x.tan_pi(), x.sin_pi() / x.cos_pi(), tol=1e-8, rel_tol=1e-8) + assert is_close_acb(x.cot_pi(), x.cos_pi() / x.sin_pi(), tol=1e-8, rel_tol=1e-8) + assert is_close_acb(y.sec(), 1 / y.cos(), tol=1e-8, rel_tol=1e-8) + assert is_close_acb(y.csc(), 1 / y.sin(), tol=1e-8, rel_tol=1e-8) + assert is_close_acb(y.tanh(), y.sinh() / y.cosh(), tol=1e-8, rel_tol=1e-8) + assert is_close_acb(y.coth(), y.cosh() / y.sinh(), tol=1e-8, rel_tol=1e-8) + assert is_close_acb(y.sech(), 1 / y.cosh(), tol=1e-8, rel_tol=1e-8) + assert is_close_acb(y.csch(), 1 / y.sinh(), tol=1e-8, rel_tol=1e-8) + + assert is_close_acb(y.sinc(), y.sin() / y, tol=1e-8, rel_tol=1e-8) + assert is_close_acb(q.sinc_pi(), q.sin_pi() / (acb.pi() * q), tol=1e-8, rel_tol=1e-8) + + +def test_acb_special_functions() -> None: + x = acb(1, 2) + y = acb(2, 3) + + assert is_close_acb(x.agm(), x.agm(acb(1))) + assert is_close_acb(x.agm(acb(1.5)), acb(1.5).agm(x)) + assert is_close_acb(x.gamma(), acb(0.15190400267003614, 0.01980488016185498)) + assert is_close_acb(x.rgamma(), acb(6.4730736260191345, -0.8439438407732021)) + assert is_close_acb(x.lgamma(), acb(-1.8760787864309293, 0.12964631630978832)) + assert is_close_acb(x.digamma(), acb(0.7145915153739775, 1.3208072826422302)) + assert is_close_acb(acb(0.5, 1000).zeta(), acb(0.35633436719439606, 0.9319978312329937)) + assert is_close_acb(x.zeta(acb(2, 3)), acb(-2.953059572088557, 3.4109625245120507)) + assert is_close_acb(x.lerch_phi(3, 4), acb(0.006872751459699249, 0.011125353146863518)) + assert is_close_acb(y.erf(), acb(-20.829461427614568, 8.687318271470163)) + assert is_close_acb(acb("77.7").erfc(), acb("7.929310690520378873143053e-2625"), tol=arb("1e-2630"), rel_tol=1e-8) + assert is_close_acb(acb(10).erfi(), acb("1.524307422708669699360547e+42"), tol=1e20, rel_tol=1e-8, max_width=1e30) + assert is_close_acb(y.gamma_upper(1 + 2j), acb(0.02614303924198793, -0.0007536537278463329)) + assert is_close_acb(y.gamma_lower(2.5), acb(1.6460770101348767, 1.140585862703101)) + assert is_close_acb(y.beta_lower(1, 2.5), acb(0.2650137734913867, -7.111836702381955)) + assert is_close_acb(y.expint(1 + 2j), acb(-0.014426614955270803, 0.019423483729866873)) + assert is_close_acb(acb(10).ei(), acb("2492.228976241877759138440"), tol=1e-8, rel_tol=1e-8) + assert is_close_acb(acb(10).si(), acb("1.658347594218874049330972")) + assert is_close_acb(acb(10).ci(), acb("-0.04545643300445537263453283")) + assert is_close_acb(acb(10).shi(), acb("1246.114490199423344411882"), tol=1e-8, rel_tol=1e-8) + assert is_close_acb(acb(10).chi(), acb("1246.114486042454414726558"), tol=1e-8, rel_tol=1e-8) + assert is_close_acb(acb(10).li(), acb("6.165599504787297937522982")) + assert is_close_acb(acb(10).li(offset=True), acb("5.120435724669805152678393")) + assert is_close_acb(x.rising(5), acb(-540, -100)) + r1, r2 = x.rising2(5) + assert is_close_acb(r1, acb(-540, -100)) + assert is_close_acb(r2, acb(-666, 420)) + assert is_close_acb(acb(3).polylog(2), acb(2.3201804233130984, -3.4513922952232027)) + z = acb(-1, 1) + assert is_close_acb(z.airy_ai(), acb(0.8221174265552726, -0.11996634266442434)) + assert is_close_acb(z.airy_bi(), acb(0.21429040153487357, 0.6739169237227052)) + ai, aip, bi, bip = y.airy() + assert is_close_acb(ai, y.airy_ai()) + assert is_close_acb(aip, y.airy_ai(derivative=1)) + assert is_close_acb(bi, y.airy_bi()) + assert is_close_acb(bip, y.airy_bi(derivative=1)) + assert is_close_acb(acb(1).lambertw(), acb("0.5671432904097838729999687")) + assert is_close_acb(acb(3).fresnel_s(), acb("0.4963129989673750360976123")) + assert is_close_acb(acb(3).fresnel_c(), acb("0.6057207892976856295561611")) + assert is_close_acb(acb(2).bessel_j(1), acb("0.5767248077568733872024482")) + assert is_close_acb(acb(2).bessel_y(1), acb("-0.1070324315409375468883708")) + assert is_close_acb(acb(2).bessel_k(1), acb("0.1398658818165224272845988")) + assert is_close_acb(acb(2).bessel_i(1), acb("1.590636854637329063382254")) + + +def test_acb_accuracy_bits() -> None: + x = acb(1, 2) + assert isinstance(x.rel_accuracy_bits(), int) + assert isinstance(x.rel_one_accuracy_bits(), int) + assert x.bits() >= 1 + assert acb("2047/2048").bits() == 11 + assert acb(float("nan")).bits() == 0 + + +def test_acb_real_methods() -> None: + assert is_close_acb(acb(-5, 2).real_abs(), acb(5, -2)) + assert is_close_acb(acb(-5, 2).real_sgn(), acb(-1)) + assert is_close_acb(acb(0).real_sgn(), acb(0)) + assert acb(0).real_sgn(analytic=True).is_finite() is False + + assert is_close_acb(acb(-5, 2).real_heaviside(), acb(0)) + assert is_close_acb(acb(5, 2).real_heaviside(), acb(1)) + assert is_close_acb(acb(0).real_heaviside(), acb(0.5)) + assert acb(0).real_heaviside(analytic=True).is_finite() is False + + assert is_close_acb(acb(2.7).real_floor(), acb(2)) + assert is_close_acb(acb(2.2).real_ceil(), acb(3)) + assert is_close_acb(acb(2, 10).real_max(acb(3, -1)), acb(3, -1)) + assert is_close_acb(acb(2, 10).real_min(acb(3, -1)), acb(2, 10)) + assert is_close_acb(acb(9).real_sqrt(), acb(3)) + + +def test_acb_elliptic_methods() -> None: + assert is_close_acb(2 * acb(0).elliptic_k(), acb.pi()) + assert is_close_acb(2 * acb(0).elliptic_e(), acb.pi()) + + assert is_close_acb(acb.elliptic_rf(1, 2 + 3j, 3 + 4j), acb(0.5577655465453922, -0.2202042457195556)) + assert is_close_acb(acb.elliptic_rc(1, 2 + 3j), acb(0.5952169239306156, -0.23879819090905094)) + assert is_close_acb(acb.elliptic_rj(1, 2, 1 + 2j, 2 + 3j), acb(0.1604659632144333, -0.2502751672723324)) + assert is_close_acb(acb.elliptic_rd(1, 2, 1 + 2j), acb(0.20437225103026298, -0.3559745898273716)) + assert is_close_acb(acb.elliptic_rg(1, 2, 1 + 2j), acb(1.2065571680567230, 0.27521766887077397)) + + assert is_close_acb(acb.elliptic_f(2, 0.75), acb(2.9525696736557795)) + assert is_close_acb(acb.elliptic_e_inc(2, 0.75), acb(1.4434330690994616)) + assert is_close_acb(acb.elliptic_pi(0.25, 0.125), acb(1.8793494518796038)) + assert is_close_acb(acb.elliptic_pi_inc(0.25, 0.5, 0.125), acb(0.5128718023282086)) + assert is_close_acb(acb.elliptic_pi_inc(0.25, 0.5, 0.125, pi=True), acb.elliptic_pi(0.25, 0.125)) + + z = acb("1/3", "1/5") + wp = z.elliptic_p(1j) + assert is_close_acb(wp, acb(3.6863806460788798, -4.5914983714972594)) + assert is_close_acb(z.elliptic_zeta(1j), acb(2.2196803395084187, -1.5049479257552417)) + assert is_close_acb(z.elliptic_sigma(1j), acb(0.33965494971368865, 0.19706907623509313)) + assert is_close_acb(wp.elliptic_inv_p(1j), z, tol=1e-9, rel_tol=1e-9, max_width=1e-9) + + e1, e2, e3 = acb(0.5 + 1j).elliptic_roots() + assert is_close_acb(e1 + e2 + e3, acb(0), tol=1e-9, rel_tol=1e-9, max_width=1e-9) + assert is_close_acb(e1, acb(6.2853881186694)) + + g2, g3 = acb(0.5 + 1j).elliptic_invariants() + assert is_close_acb(g2, acb(72.64157667926128)) + assert is_close_acb(g3, acb(536.6642788346023)) + + +def test_acb_hypgeom_functions() -> None: + z = acb(5) + assert is_close_acb(z.hypgeom_2f1(1, 2, 3), acb(-0.5109035488895912, -0.25132741228718347)) + assert is_close_acb(z.hypgeom_2f1(1, 2, 3, regularized=True), acb(-0.2554517744447956, -0.12566370614359174)) + + w = acb(0.2) + assert is_close_acb(w.hypgeom_0f1(2.5), acb(1.0823198864627344)) + assert is_close_acb(w.hypgeom_0f1(2.5, regularized=True), acb(0.8141781413451533)) + assert is_close_acb(w.hypgeom([1, 2], [3]), acb(1.1571775657104878)) + assert is_close_acb(w.hypgeom([1, 2], [3], regularized=True), acb(0.5785887828552439)) + assert is_close_acb(w.hypgeom_u(1, 2.5), acb(11.378859082050016)) + assert is_close_acb(w.hypgeom_1f1(1, 2.5), acb(1.0847822248780032)) + assert is_close_acb(w.hypgeom_1f1(1, 2.5, regularized=True), acb(0.8160304422585721)) + + +def test_acb_modular_functions() -> None: + z = acb(1 + 1j) + tau = acb(1.25 + 3j) + + t1, t2, t3, t4 = acb.modular_theta(z, tau) + assert is_close_acb(t1, acb(1.8202359101249896, -1.216251950154478)) + assert is_close_acb(t2, acb(-1.2207902675769677, -1.8270555167911547)) + assert is_close_acb(t3, acb(0.9694430387796704, -0.030556961208168033)) + assert is_close_acb(t4, acb(1.0305569611960065, 0.030556961208168033)) + + assert is_close_acb(acb(1 + 1j).modular_eta(), acb(0.7420487758365647, 0.19883137022991072)) + assert is_close_acb((1 + acb(-163).sqrt() / 2).modular_j(), acb(262537412640769488.0), max_width=1e5) + assert is_close_acb(acb(0.25 + 5j).modular_lambda(), acb(1.7049954156680393e-6, 1.7049925086620794e-6)) + assert is_close_acb(acb(0.25 + 5j).modular_delta(), acb(1.2378960150102817e-26, 2.271101068324094e-14)) + + u1, u2, u3, u4 = acb.modular_theta(z, tau, 1) + assert is_close_acb(u1, acb(-3.8353056542516, -5.7398107897127), tol=1e-9, rel_tol=1e-9) + assert is_close_acb(u2, acb(-5.7184931625874, 3.8208882734627), tol=1e-9, rel_tol=1e-9) + assert is_close_acb(u3, acb(-0.19199371059495, 0.19199371074778), tol=1e-9, rel_tol=1e-9) + assert is_close_acb(u4, acb(0.19199371059495, -0.19199371044212), tol=1e-9, rel_tol=1e-9) + + +def test_acb_orthogonal_and_related_functions() -> None: + x = acb(1) / 3 + assert is_close_acb(x.chebyshev_t(3), acb(-0.8518518518518519)) + assert is_close_acb(x.chebyshev_u(3), acb(-1.037037037037037)) + assert is_close_acb(x.jacobi_p(5, 0.25, 0.5), acb(0.4131944444444444)) + assert is_close_acb(x.gegenbauer_c(5, 0.25), acb(0.13218557098765432)) + assert is_close_acb(x.laguerre_l(5, 0.25), acb(0.03871323490012003)) + assert is_close_acb(x.hermite_h(5), acb(34.20576131687243)) + assert is_close_acb(x.legendre_p(5), acb(1 / 3)) + assert is_close_acb(x.legendre_q(5), acb(0.1655245300933242)) + assert is_close_acb( + acb(3).legendre_q(5, 1.5, type=3), acb(0, -0.0003010942389043591), tol=1e-8, rel_tol=1e-8, max_width=1e-8 + ) + assert is_close_acb(acb.spherical_y(5, 3, 0.25, 0.75), acb(0.02451377199072374, -0.03036343496553117)) + assert raises(lambda: x.legendre_p(5, type=4), ValueError) + assert raises(lambda: x.legendre_q(5, type=4), ValueError) + + +def test_acb_misc_special_functions() -> None: + assert is_close_acb(acb(5).dirichlet_eta(), acb(0.9721197704469093)) + assert is_close_acb(acb(1).dirichlet_eta(), acb(math.log(2.0))) + assert is_close_acb(acb(2).polygamma(1), acb(0.6449340668482264)) + assert is_close_acb(acb(3).log_barnes_g(), acb(0)) + assert is_close_acb(acb(3).barnes_g(), acb(1)) + assert is_close_acb(acb.stieltjes(1), acb(-0.07281584548367672)) + assert raises(lambda: acb.stieltjes(-1), ValueError) + assert is_close_acb(acb(0.25 + 0.25j).bernoulli_poly(5), acb(-0.05859375, 0.006510416666666667)) + assert is_close_acb(acb(5 + 2j).log_sin_pi(), acb(5.590034639271204, -14.13716694115407)) + + chi = dirichlet_char(3, 1) + assert is_close_acb(acb(2).dirichlet_l(chi), acb(1.46216361497620)) + assert is_close_acb(acb(2).dirichlet_l((3, 1)), acb(1.46216361497620)) + + vals = acb.dft(range(1, 12)) + back = acb.dft(vals, inverse=True) + for i, v in enumerate(back, start=1): + assert is_close_acb(v, acb(i), tol=1e-9, rel_tol=1e-9, max_width=1e-9) + assert acb.dft([]) == [] + + assert acb("5 +/- 0.1").unique_fmpz() is not None + assert acb(5.5, 0.1).unique_fmpz() is None + + +def test_acb_more_special_function_branches() -> None: + z = acb(-1, 1) + assert is_close_acb(z.airy_ai(derivative=1), acb(-0.3790604792268335, -0.6045001308622461)) + assert is_close_acb(z.airy_bi(derivative=1), acb(0.8344734885227826, -0.3465260632668285)) + assert raises(lambda: z.airy_ai(derivative=2), ValueError) + assert raises(lambda: z.airy_bi(derivative=2), ValueError) + + assert is_close_acb(acb(-5, "+/- 1e-20").lambertw(left=True), acb(0.844844605432170, 1.97500875488903)) + assert is_close_acb(acb(-0.25, "+/- 1e-20").lambertw(middle=True), acb(-2.15329236411035)) + + assert is_close_acb(acb(5).bessel_k(1, scaled=True), acb(0.6002738587883126)) + assert is_close_acb(acb(5).bessel_i(1, scaled=True), acb(0.16397226694454236)) + assert is_close_acb(acb(-1).root(3), acb(0.5, 0.8660254037844386)) + + +def test_acb_zeta_zero_methods() -> None: + z1 = acb.zeta_zero(1) + assert is_close_acb(z1, acb(0.5, 14.134725141734694)) + zs = acb.zeta_zeros(1000, 3) + assert len(zs) == 3 + assert is_close_acb(zs[0], acb(0.5, 1419.42248094600)) + assert is_close_acb(zs[1], acb(0.5, 1420.41652632375)) + assert is_close_acb(zs[2], acb(0.5, 1421.85056718705)) + assert raises(lambda: acb.zeta_zero(0), ValueError) + assert raises(lambda: acb.zeta_zeros(0, 1), ValueError) + assert raises(lambda: acb.zeta_zeros(1, -1), ValueError) + + +def test_acb_integral() -> None: + res = acb.integral(lambda z, d: z, 0, 1) + assert is_close_acb(res, acb(0.5), tol=1e-9, rel_tol=1e-9, max_width=1e-9) + assert is_close_acb(acb.integral(lambda z, d: z, 0, 1, rel_tol=arb("1e-20"), abs_tol=arb("1e-20")), acb(0.5)) + assert is_close_acb( + acb.integral(lambda z, d: z, 0, 1, deg_limit=8, eval_limit=1000, depth_limit=30, use_heap=True, verbose=False), + acb(0.5), + ) + assert raises(lambda: acb.integral(lambda z, d: 1, 0, 1), TypeError) # type: ignore[arg-type] + + +def test_acb_coulomb_functions() -> None: + x = acb(1) + l = acb(0.5) + eta = acb(0.25) + f, g, hp, hn = x.coulomb(l, eta) + assert is_close_acb(f, x.coulomb_f(l, eta), tol=1e-9, rel_tol=1e-9, max_width=1e-9) + assert is_close_acb(g, x.coulomb_g(l, eta), tol=1e-9, rel_tol=1e-9, max_width=1e-9) + assert is_close_acb(hp, g + 1j * f, tol=1e-9, rel_tol=1e-9, max_width=1e-9) + assert is_close_acb(hn, g - 1j * f, tol=1e-9, rel_tol=1e-9, max_width=1e-9) + assert is_close_acb(f, acb(0.4283180781043542)) + assert is_close_acb(g, acb(1.218454487206368)) + + +def test_acb_hypgeom_remaining_branches() -> None: + x = acb("11/10") + a = acb(2).sqrt() + b = acb("1/2") + c = a + acb("3/2") + + assert x.hypgeom_2f1(a, b, c, ab=True).is_finite() is False + assert x.hypgeom_2f1(a, b, c, ac=True).is_finite() is False + assert x.hypgeom_2f1(a, b, c, bc=True).is_finite() is False + assert is_close_acb(x.hypgeom_2f1(a, b, c, abc=True), acb(1.8017826594800542, -0.3114019850045849)) + + w = acb(0.2) + assert is_close_acb(w.hypgeom([1, 2], [3], n=5), acb(1.1571775657104878), tol=1e-3, rel_tol=1e-3, max_width=1e-3) + assert raises(lambda: w.hypgeom([1, 2], [3], regularized=True, n=5), NotImplementedError) + assert is_close_acb(acb(-30).hypgeom_u(1 + 1j, 2 + 3j, n=30, asymp=True), acb(0.7808944974, -0.2674783065), tol=1e-8, rel_tol=1e-8) diff --git a/src/flint/test/test_acb_mat.py b/src/flint/test/test_acb_mat.py new file mode 100644 index 00000000..d13ab095 --- /dev/null +++ b/src/flint/test/test_acb_mat.py @@ -0,0 +1,322 @@ +from __future__ import annotations + +import sys +from unittest.mock import patch + +from flint import _has_acb_theta, acb, acb_mat, arb_mat, ctx, fmpq_mat, fmpz_mat +from flint.test.helpers import is_close_acb, is_close_acb_mat as is_close, is_close_arb_mat, raises + + +class _DummyMatrix: + rows = 2 + cols = 2 + + def __getitem__(self, index: tuple[int, int]) -> complex: + i, j = index + return complex(i + 2 * j + 1, i - j) + + +def test_acb_mat_constructor() -> None: + z = fmpz_mat([[1, 2], [3, 4]]) + q = fmpq_mat([[1, 2], [3, 4]]) + a = arb_mat([[1, 2], [3, 4]]) + b = acb_mat([[1, 2], [3, 4]]) + + assert is_close(acb_mat(b), [[1, 2], [3, 4]]) + assert is_close(acb_mat(a), [[1, 2], [3, 4]]) + assert is_close(acb_mat(z), [[1, 2], [3, 4]]) + assert is_close(acb_mat(q), [[1, 2], [3, 4]]) + assert is_close(acb_mat(_DummyMatrix()), [[1, 3 - 1j], [2 + 1j, 4]]) + + assert isinstance(acb_mat.convert(z), acb_mat) + assert isinstance(acb_mat.convert(q), acb_mat) + assert isinstance(acb_mat.convert(a), acb_mat) + assert raises(lambda: acb_mat.convert(object()), TypeError) # type: ignore[arg-type] + + assert is_close(acb_mat(2, 2), [[0, 0], [0, 0]]) + assert is_close(acb_mat(2, 2, [1, 2, 3, 4]), [[1, 2], [3, 4]]) + assert is_close(acb_mat(3, 3, 5), [[5, 0, 0], [0, 5, 0], [0, 0, 5]]) + + assert raises(lambda: acb_mat([1, 2]), TypeError) # type: ignore[arg-type,list-item] + assert raises(lambda: acb_mat([[1], [2, 3]]), ValueError) + assert raises(lambda: acb_mat(object()), TypeError) # type: ignore[call-overload] + assert raises(lambda: acb_mat(2, 2, [1, 2, 3]), ValueError) + assert raises(lambda: acb_mat(1, 2, 3, 4), ValueError) # type: ignore[call-overload] + + +def test_acb_mat_basics_and_indexing() -> None: + a = acb_mat([[1, 2], [3, 4]]) + assert a.nrows() == 2 + assert a.ncols() == 2 + assert is_close_acb(a[0, 1], 2) + a[0, 1] = 1 + 2j + assert is_close_acb(a[0, 1], 1 + 2j) + + assert raises(lambda: a[2, 0], ValueError) # type: ignore[index] + assert raises(lambda: a[0, 2], ValueError) # type: ignore[index] + + def set_oob_1() -> None: + a[2, 0] = 1 + + def set_oob_2() -> None: + a[0, 2] = 1 + + def set_bad() -> None: + a[0, 0] = object() # type: ignore[assignment] + + assert raises(set_oob_1, ValueError) + assert raises(set_oob_2, ValueError) + assert raises(set_bad, TypeError) + + assert is_close(a.transpose(), [[1, 3], [1 + 2j, 4]]) + assert is_close(a.conjugate(), [[1, 1 - 2j], [3, 4]]) + assert is_close(+a, a) + assert is_close(-a, [[-1, -1 - 2j], [-3, -4]]) + assert raises(lambda: bool(a), NotImplementedError) + + oldpretty = ctx.pretty + try: + ctx.pretty = False + assert "acb_mat(" in repr(a) + finally: + ctx.pretty = oldpretty + assert "[" in str(a) + + +def test_acb_mat_add_sub() -> None: + a = acb_mat([[1, 2], [3, 4]]) + b = acb_mat([[4, 5], [6, 7]]) + z = fmpz_mat([[4, 5], [6, 7]]) + q = fmpq_mat([[4, 5], [6, 7]]) + r = arb_mat([[4, 5], [6, 7]]) + + assert is_close(a + b, [[5, 7], [9, 11]]) + assert is_close(a + z, [[5, 7], [9, 11]]) + assert is_close(a + q, [[5, 7], [9, 11]]) + assert is_close(a + r, [[5, 7], [9, 11]]) + assert is_close(z + a, [[5, 7], [9, 11]]) + assert is_close(q + a, [[5, 7], [9, 11]]) + assert is_close(r + a, [[5, 7], [9, 11]]) + + assert is_close(a - b, [[-3, -3], [-3, -3]]) + assert is_close(a - z, [[-3, -3], [-3, -3]]) + assert is_close(a - q, [[-3, -3], [-3, -3]]) + assert is_close(a - r, [[-3, -3], [-3, -3]]) + assert is_close(z - a, [[3, 3], [3, 3]]) + assert is_close(q - a, [[3, 3], [3, 3]]) + assert is_close(r - a, [[3, 3], [3, 3]]) + + assert is_close(a + 2, [[3, 2], [3, 6]]) + assert is_close(2 + a, [[3, 2], [3, 6]]) + assert is_close(a - 2, [[-1, 2], [3, 2]]) + assert is_close(2 - a, [[1, -2], [-3, -2]]) + + assert is_close(a + (1 + 2j), [[2 + 2j, 2], [3, 5 + 2j]]) + assert is_close((1 + 2j) + a, [[2 + 2j, 2], [3, 5 + 2j]]) + assert is_close(a - (1 + 2j), [[-2j, 2], [3, 3 - 2j]]) + assert is_close((1 + 2j) - a, [[2j, -2], [-3, -3 + 2j]]) + + assert raises(lambda: a + acb_mat([[1, 2, 3]]), ValueError) + assert raises(lambda: a - acb_mat([[1, 2, 3]]), ValueError) + assert raises(lambda: a + object(), TypeError) # type: ignore[operator] + assert raises(lambda: object() + a, TypeError) # type: ignore[operator] + assert raises(lambda: a - object(), TypeError) # type: ignore[operator] + assert raises(lambda: object() - a, TypeError) # type: ignore[operator] + + +def test_acb_mat_mul_div() -> None: + a = acb_mat([[1, 2], [3, 4]]) + b = acb_mat([[4, 5], [6, 7]]) + z = fmpz_mat([[4, 5], [6, 7]]) + q = fmpq_mat([[4, 5], [6, 7]]) + r = arb_mat([[4, 5], [6, 7]]) + + assert is_close(a * b, [[16, 19], [36, 43]]) + assert is_close(a * z, [[16, 19], [36, 43]]) + assert is_close(a * q, [[16, 19], [36, 43]]) + assert is_close(a * r, [[16, 19], [36, 43]]) + assert is_close(z * a, [[19, 28], [27, 40]]) + assert is_close(q * a, [[19, 28], [27, 40]]) + assert is_close(r * a, [[19, 28], [27, 40]]) + + assert is_close(a * 2, [[2, 4], [6, 8]]) + assert is_close(2 * a, [[2, 4], [6, 8]]) + assert is_close(a * 0.5, [[0.5, 1], [1.5, 2]]) + assert is_close(a / 2, [[0.5, 1], [1.5, 2]]) + assert is_close(a * (1 + 2j), [[1 + 2j, 2 + 4j], [3 + 6j, 4 + 8j]]) + assert is_close((1 + 2j) * a, [[1 + 2j, 2 + 4j], [3 + 6j, 4 + 8j]]) + + assert raises(lambda: a * acb_mat([[1, 2, 3]]), ValueError) + assert raises(lambda: a * object(), TypeError) # type: ignore[operator] + assert raises(lambda: object() * a, TypeError) # type: ignore[operator] + assert raises(lambda: a / object(), TypeError) # type: ignore[operator] + + +def test_acb_mat_pow_inv_solve() -> None: + a = acb_mat([[1, 2], [3, 4]]) + assert is_close(a**2, [[7, 10], [15, 22]]) + assert raises(lambda: pow(a, 2, 3), TypeError) # type: ignore[misc] + assert raises(lambda: acb_mat([[1, 2, 3]]) ** 2, ValueError) + + ai = a.inv() + assert is_close(a * ai, [[1, 0], [0, 1]], tol=1e-8, rel_tol=1e-8, max_width=1e-8) + assert raises(lambda: acb_mat([[1, 2], [2, 4]]).inv(), ZeroDivisionError) + assert raises(lambda: acb_mat([[1, 2, 3]]).inv(), ValueError) + + inv_ns = acb_mat([[1, 2], [2, 4]]).inv(nonstop=True) + assert inv_ns[0, 0].is_finite() is False + assert inv_ns[1, 1].is_finite() is False + + x = acb_mat([[1], [2]]) + b = a * x + assert is_close(a.solve(b), x, tol=1e-8, rel_tol=1e-8, max_width=1e-8) + assert is_close(a.solve(b, algorithm="lu"), x, tol=1e-8, rel_tol=1e-8, max_width=1e-8) + assert is_close(a.solve(b, algorithm="precond"), x, tol=1e-8, rel_tol=1e-8, max_width=1e-8) + assert is_close(a.solve(b, algorithm="approx"), x, tol=1e-8, rel_tol=1e-8, max_width=1e-8) + + assert raises(lambda: a.solve(b, algorithm="bad"), ValueError) # type: ignore[arg-type] + assert raises(lambda: acb_mat([[1, 2], [2, 4]]).solve(b), ZeroDivisionError) + + solve_ns = acb_mat([[1, 2], [2, 4]]).solve(b, nonstop=True) + assert solve_ns[0, 0].is_finite() is False + assert solve_ns[1, 0].is_finite() is False + + assert raises(lambda: acb_mat([[1, 2, 3]]).solve(acb_mat([[1], [2], [3]])), ValueError) + assert raises(lambda: a.solve([[1], [2]]), TypeError) # type: ignore[arg-type] + + +def test_acb_mat_special_methods() -> None: + a = acb_mat([[1, 2], [3, 4]]) + assert is_close_acb(a.det(), -2) + assert is_close_acb(a.trace(), 5) + assert is_close(a.mid(), a) + + e = acb_mat(2, 2, [1, 4, -2, 1]).exp() + assert is_close( + e, + [ + [-2.58607310345045, 1.18429895089106], + [-0.592149475445530, -2.58607310345045], + ], + tol=1e-12, + rel_tol=1e-12, + max_width=1e-12, + ) + + assert raises(lambda: acb_mat([[1, 2, 3]]).det(), ValueError) + assert raises(lambda: acb_mat([[1, 2, 3]]).trace(), ValueError) + assert raises(lambda: acb_mat([[1, 2, 3]]).exp(), ValueError) + + p = acb_mat([[1, 1], [1, 0]]).charpoly() + assert is_close_acb(p[0], -1) + assert is_close_acb(p[1], -1) + assert is_close_acb(p[2], 1) + assert raises(lambda: acb_mat([[1, 2, 3]]).charpoly(), ValueError) + + d = acb_mat.dft(3) + assert d.nrows() == 3 + assert d.ncols() == 3 + assert is_close_acb(d[0, 0], 0.5773502691896257) + assert is_close_acb(d[1, 1], acb(-0.28867513459481287, -0.5), tol=1e-12, rel_tol=1e-12) + + d2 = acb_mat.dft(2, 3) + assert d2.nrows() == 2 + assert d2.ncols() == 3 + + +def test_acb_mat_contains_overlap_chop_cmp_real_imag() -> None: + a = acb_mat([[1, 2], [3, 4]]) + b = (a / 3) * 3 + + assert b.contains(a) is True + assert a.contains(b) is False + assert b.contains(fmpz_mat([[1, 2], [3, 4]])) is True + assert (a / 3).contains(fmpq_mat([[1, 2], [3, 4]]) / 3) is True + assert ((a / 3) * 3).contains(arb_mat([[1, 2], [3, 4]])) is True + assert raises(lambda: a.contains(object()), TypeError) # type: ignore[arg-type] + + assert b.overlaps(a) is True + assert (a + 100).overlaps(a) is False + + c = acb_mat([[1e-20 + 1e-20j, 2], [3j, -1e-20 + 1e-20j]]) + chopped = c.chop(1e-10) + assert is_close(chopped, [[0, 2], [3j, 0]]) + + assert (a == acb_mat([[1, 2], [3, 4]])) is True + assert (a != acb_mat([[1, 2], [3, 4]])) is False + assert (a == fmpz_mat([[1, 2], [3, 4]])) is True + assert (a != fmpz_mat([[1, 2], [3, 5]])) is True + assert raises(lambda: a < acb_mat([[1, 2], [3, 4]]), ValueError) # type: ignore[operator] + assert raises(lambda: a <= acb_mat([[1, 2], [3, 4]]), ValueError) # type: ignore[operator] + assert raises(lambda: a > acb_mat([[1, 2], [3, 4]]), ValueError) # type: ignore[operator] + assert raises(lambda: a >= acb_mat([[1, 2], [3, 4]]), ValueError) # type: ignore[operator] + assert (a == object()) is False + assert (a != object()) is True + + d = acb_mat.dft(3) + assert is_close_arb_mat(d.real, [[0.5773502691896257, 0.5773502691896257, 0.5773502691896257], [0.5773502691896257, -0.28867513459481287, -0.28867513459481287], [0.5773502691896257, -0.28867513459481287, -0.28867513459481287]]) + assert is_close_arb_mat(d.imag, [[0, 0, 0], [0, -0.5, 0.5], [0, 0.5, -0.5]]) + + +def test_acb_mat_eig_theta_and_helper() -> None: + a = acb_mat([[1, 0], [0, 2]]) + + vals = a.eig() + assert len(vals) == 2 + assert any(v.real.contains(1) for v in vals) + assert any(v.real.contains(2) for v in vals) + + vals_l, left = a.eig(left=True) + assert len(vals_l) == 2 + assert isinstance(left, acb_mat) + + vals_r, right = a.eig(right=True) + assert len(vals_r) == 2 + assert isinstance(right, acb_mat) + + vals_lr, left2, right2 = a.eig(left=True, right=True) + assert len(vals_lr) == 2 + assert isinstance(left2, acb_mat) + assert isinstance(right2, acb_mat) + + vals_approx = acb_mat.dft(4).eig(algorithm="approx") + assert len(vals_approx) == 4 + + vals_rump = a.eig(algorithm="rump") + assert len(vals_rump) == 2 + + vals_vm = a.eig(algorithm="vdhoeven_mourrain") + assert len(vals_vm) == 2 + + vals_tol = a.eig(tol=1e-12) + assert len(vals_tol) == 2 + + vals_multi = acb_mat.dft(4).eig(multiple=True) + assert len(vals_multi) == 4 + + vals_multi_rump = acb_mat.dft(4).eig(multiple=True, algorithm="rump") + assert len(vals_multi_rump) == 4 + + assert raises(lambda: acb_mat.dft(4).eig(multiple=True, right=True), NotImplementedError) + assert raises(lambda: acb_mat.dft(4).eig(), ValueError) + assert raises(lambda: acb_mat([[1, 2, 3]]).eig(), ValueError) + + assert acb_mat(0, 0).eig() == [] + + tau = acb_mat([[1j]]) + if _has_acb_theta(): + theta_vals = acb_mat.theta(tau, acb_mat([[0]])) + assert isinstance(theta_vals, acb_mat) + assert theta_vals.nrows() == 1 + assert theta_vals.ncols() == 4 + else: + assert raises(lambda: acb_mat.theta(tau, acb_mat([[0]])), NotImplementedError) + + with patch.dict(sys.modules, {"flint.types.acb_theta": None}): + assert raises(lambda: acb_mat.theta(tau, acb_mat([[0]])), NotImplementedError) + + assert is_close(a, [[1, 0], [0, 2]]) is True + assert is_close(a, acb_mat([[1, 0], [0, 2]])) is True + assert is_close(a, [[1, 0, 0], [0, 2, 0]]) is False + assert is_close(a, [[1, 0], [0, 3]]) is False + assert is_close(object(), [[1]]) is False # type: ignore[arg-type] diff --git a/src/flint/test/test_acb_poly.py b/src/flint/test/test_acb_poly.py new file mode 100644 index 00000000..90d6e21e --- /dev/null +++ b/src/flint/test/test_acb_poly.py @@ -0,0 +1,269 @@ +"""Tests for python-flint's `acb_poly` type.""" + +from __future__ import annotations + +from flint import acb, acb_poly, arb, arb_poly, fmpq, fmpq_poly, fmpz, fmpz_poly +from flint.test.helpers import is_close_acb, is_close_acb_poly as is_close, is_close_arb, raises + + +def test_acb_poly_constructor_and_basic() -> None: + x = acb_poly([0, 1]) + p0 = acb_poly() + assert len(p0) == 0 + assert p0.length() == 0 + assert p0.degree() == -1 + + p = acb_poly([1, 2, 3]) + assert len(p) == 3 + assert p.length() == 3 + assert p.degree() == 2 + assert is_close(p, 3*x**2 + 2*x + 1) + assert is_close_acb(p[-1], 0) + assert p.repr() == "acb_poly([1.00000000000000, 2.00000000000000, 3.00000000000000])" + + q = acb_poly(p) + assert q is not p + assert is_close(q, p) + + assert is_close(acb_poly(5), acb_poly([5])) + assert is_close(acb_poly(2 + 3j), acb_poly([acb(2, 3)])) + assert is_close(acb_poly(arb_poly([1, 2])), 2*x + 1) + assert is_close(acb_poly(fmpz_poly([1, 2])), 2*x + 1) + assert is_close(acb_poly(fmpq_poly([1, fmpq(1, 2)])), 0.5*x + 1) + + mixed = acb_poly([1, 2.5, 3 + 4j, arb(4), fmpz(5), fmpq(7, 2), acb(1, -2)]) + assert is_close(mixed, (1 - 2j)*x**6 + 3.5*x**5 + 5*x**4 + 4*x**3 + (3 + 4j)*x**2 + 2.5*x + 1) + + assert raises(lambda: acb_poly([1, object()]), TypeError) # type: ignore[list-item] + + +def test_acb_poly_setitem() -> None: + p = acb_poly([1]) + p[3] = 5 + assert p.degree() == 3 + assert is_close_acb(p[3], 5) + p[2] = acb(1.25, -0.5) + assert is_close_acb(p[2], acb(1.25, -0.5)) + assert raises(lambda: p.__setitem__(-1, 1), ValueError) + + +def test_acb_poly_from_roots() -> None: + x = acb_poly([0, 1]) + p = acb_poly.from_roots([0, 1, 2]) + assert is_close(p, x*(x - 1)*(x - 2)) + assert is_close_acb(p(0), 0) + assert is_close_acb(p(1), 0) + assert is_close_acb(p(2), 0) + + q = acb_poly.from_roots([1j, -1j]) + assert is_close(q, x**2 + 1) + assert is_close_acb(q(1j), 0) + assert is_close_acb(q(-1j), 0) + + one = acb_poly.from_roots([]) + assert one.degree() == 0 + assert is_close_acb(one[0], 1) + + +def test_acb_poly_evaluate() -> None: + p = acb_poly([1, 2, 3]) + ys_fast = p.evaluate([0, 1, 2], algorithm="fast") + ys_iter = p.evaluate([0, 1, 2], algorithm="iter") + + assert len(ys_fast) == 3 + assert is_close_acb(ys_fast[0], 1) + assert is_close_acb(ys_fast[1], 6) + assert is_close_acb(ys_fast[2], 17) + + assert len(ys_iter) == 3 + assert is_close_acb(ys_iter[0], 1) + assert is_close_acb(ys_iter[1], 6) + assert is_close_acb(ys_iter[2], 17) + + assert p.evaluate([], algorithm="fast") == [] + assert raises(lambda: p.evaluate([1], algorithm="bad"), AssertionError) + + +def test_acb_poly_interpolate() -> None: + x = acb_poly([0, 1]) + xs = [0, 1, 2] + ys = [1, 6, 17] + + p_fast = acb_poly.interpolate(xs, ys, algorithm="fast") + p_newton = acb_poly.interpolate(xs, ys, algorithm="newton") + p_bary = acb_poly.interpolate(xs, ys, algorithm="barycentric") + + assert is_close(p_fast, 3*x**2 + 2*x + 1) + assert is_close(p_newton, 3*x**2 + 2*x + 1) + assert is_close(p_bary, 3*x**2 + 2*x + 1) + + assert raises(lambda: acb_poly.interpolate([0], [1, 2]), ValueError) + assert raises(lambda: acb_poly.interpolate([0], [1], algorithm="bad"), AssertionError) + + +def test_acb_poly_calculus_and_unary() -> None: + x = acb_poly([0, 1]) + p = 3*x**2 + 2*x + 1 + assert is_close(p.derivative(), 6*x + 2) + assert is_close(p.integral(), x**3 + x**2 + x) + assert (+p) is p + assert is_close(-p, -(3*x**2 + 2*x + 1)) + + +def test_acb_poly_arithmetic_real_and_complex_coercions() -> None: + x = acb_poly([0, 1]) + p = 3*x**2 + 2*x + 1 + + assert is_close(p + 2, 3*x**2 + 2*x + 3) + assert is_close(2 + p, 3*x**2 + 2*x + 3) + assert is_close(p - 2, 3*x**2 + 2*x - 1) + assert is_close(2 - p, -3*x**2 - 2*x + 1) + assert is_close(p*2, 6*x**2 + 4*x + 2) + assert is_close(2*p, 6*x**2 + 4*x + 2) + + assert is_close(p + 1j, 3*x**2 + 2*x + (1 + 1j)) + assert is_close((1 + 1j) + p, 3*x**2 + 2*x + (2 + 1j)) + assert is_close(p - 1j, 3*x**2 + 2*x + (1 - 1j)) + assert is_close((1 + 1j) - p, -3*x**2 - 2*x + 1j) + assert is_close(p*(1 + 1j), (3 + 3j)*x**2 + (2 + 2j)*x + (1 + 1j)) + assert is_close((1 + 1j)*p, (3 + 3j)*x**2 + (2 + 2j)*x + (1 + 1j)) + + assert is_close(p + fmpz(2), 3*x**2 + 2*x + 3) + assert is_close(p + fmpq(1, 2), 3*x**2 + 2*x + 1.5) + assert is_close(p + arb(1.25), 3*x**2 + 2*x + 2.25) + assert is_close(p + acb(1, -2), 3*x**2 + 2*x + acb(2, -2)) + assert is_close(p + fmpz_poly([1, 1]), 3*x**2 + 3*x + 2) + assert is_close(p + fmpq_poly([1, fmpq(1, 2)]), 3*x**2 + 2.5*x + 2) + assert is_close(p + arb_poly([1, 1]), 3*x**2 + 3*x + 2) + + +def test_acb_poly_arithmetic_divmod_and_errors() -> None: + x = acb_poly([0, 1]) + p = 3*x**2 + 2*x + 1 + d = x + 1 + + q = p // d + r = p % d + q2, r2 = divmod(p, d) + assert is_close(q, 3*x - 1) + assert is_close(r, 2) + assert is_close(q2, 3*x - 1) + assert is_close(r2, 2) + + assert is_close(3 // d, 0) + assert is_close(3 % d, 3) + rq, rr = divmod(3, d) + assert is_close(rq, 0) + assert is_close(rr, 3) + + assert is_close(p // acb(2), 1.5*x**2 + x + 0.5) + assert is_close(p % acb(2), 0) + q3, r3 = divmod(p, acb(2)) + assert is_close(q3, 1.5*x**2 + x + 0.5) + assert is_close(r3, 0) + + z = acb_poly([0]) + assert raises(lambda: p // z, ZeroDivisionError) + assert raises(lambda: p % z, ZeroDivisionError) + assert raises(lambda: divmod(p, z), ZeroDivisionError) + + +def test_acb_poly_arithmetic_unsupported_operands() -> None: + p = acb_poly([1, 2, 3]) + assert raises(lambda: p + object(), TypeError) # type: ignore[operator] + assert raises(lambda: p - object(), TypeError) # type: ignore[operator] + assert raises(lambda: p * object(), TypeError) # type: ignore[operator] + assert raises(lambda: p // object(), TypeError) # type: ignore[operator] + assert raises(lambda: p % object(), TypeError) # type: ignore[operator] + assert raises(lambda: divmod(p, object()), TypeError) # type: ignore[operator] + assert raises(lambda: object() + p, TypeError) # type: ignore[operator] + assert raises(lambda: object() - p, TypeError) # type: ignore[operator] + assert raises(lambda: object() * p, TypeError) # type: ignore[operator] + assert raises(lambda: object() // p, TypeError) # type: ignore[operator] + assert raises(lambda: object() % p, TypeError) # type: ignore[operator] + assert raises(lambda: divmod(object(), p), TypeError) # type: ignore[operator] + + +def test_acb_poly_truncate_shifts_pow() -> None: + x = acb_poly([0, 1]) + p = 3*x**2 + 2*x + 1 + + assert p.truncate(0).length() == 0 + assert is_close(p.truncate(-1), 0) + assert is_close(p.truncate(2), 2*x + 1) + assert is_close(p.truncate(5), p) + assert is_close(p.left_shift(0), p) + assert is_close(p.left_shift(2), x**2*p) + assert is_close(p.right_shift(0), p) + assert is_close(p.right_shift(2), 3) + assert p.right_shift(6).length() == 0 + assert raises(lambda: p.left_shift(-1), ValueError) + assert raises(lambda: p.right_shift(-1), ValueError) + + p3 = p**3 + assert is_close(p3, p*p*p) + assert raises(lambda: pow(p, 2, 3), NotImplementedError) # type: ignore[misc] + + +def test_acb_poly_call_paths() -> None: + p = acb_poly([1, 2, 3]) + x = acb_poly([0, 1]) + + assert is_close(p(acb_poly([1, 1])), 3*x**2 + 8*x + 6) + assert is_close(p(fmpz_poly([1, 1])), 3*x**2 + 8*x + 6) + assert is_close(p(fmpq_poly([1, 1])), 3*x**2 + 8*x + 6) + assert is_close(p(arb_poly([1, 1])), 3*x**2 + 8*x + 6) + + assert is_close_acb(p(acb(2, 3)), acb(-10, 42)) + assert is_close_acb(p(2 + 3j), acb(-10, 42)) + assert is_close_acb(p(arb(2)), 17) + assert is_close_acb(p(2), 17) + assert is_close_acb(p(2.0), 17) + assert is_close_acb(p(fmpz(2)), 17) + assert is_close_acb(p(fmpq(1, 2)), 2.75) + + assert raises(lambda: p(object()), TypeError) # type: ignore[call-overload] + + +def test_acb_poly_unique_fmpz_poly() -> None: + p = acb_poly([1, 2, 3]) + q = acb_poly([1.1, 2, 3]) + assert p.unique_fmpz_poly() == fmpz_poly([1, 2, 3]) + assert q.unique_fmpz_poly() is None + + +def test_acb_poly_roots_and_complex_roots() -> None: + x = acb_poly([0, 1]) + p = x**2 + 1 + rs0 = p.roots() + assert len(rs0) == 2 + assert any(is_close_acb(r, 1j, tol=1e-5, max_width=1e-5) for r in rs0) + assert any(is_close_acb(r, -1j, tol=1e-5, max_width=1e-5) for r in rs0) + + rs = p.roots(tol=1e-30, maxprec=128) + assert len(rs) == 2 + assert any(is_close_acb(r, 1j, tol=arb("1e-20"), rel_tol=arb("1e-20")) for r in rs) + assert any(is_close_acb(r, -1j, tol=arb("1e-20"), rel_tol=arb("1e-20")) for r in rs) + assert is_close_acb(p(rs[0]), 0, tol=arb("1e-20"), rel_tol=arb("1e-20")) + assert is_close_acb(p(rs[1]), 0, tol=arb("1e-20"), rel_tol=arb("1e-20")) + + rs2 = p.complex_roots(tol=1e-30, maxprec=128) + assert len(rs2) == 2 + assert any(is_close_acb(r, 1j, tol=arb("1e-20"), rel_tol=arb("1e-20")) for r in rs2) + assert any(is_close_acb(r, -1j, tol=arb("1e-20"), rel_tol=arb("1e-20")) for r in rs2) + + z = acb_poly() + assert z.roots() == [] + assert z.complex_roots() == [] + + squareful = x**2 + assert raises(lambda: squareful.roots(tol=1e-20, maxprec=128), ValueError) + assert raises(lambda: (x**3 - 1).roots(tol=1e-30, maxprec=40), ValueError) + + +def test_acb_poly_root_bound() -> None: + x = acb_poly([0, 1]) + p = x**2 + 1 + b = p.root_bound() + assert is_close_arb(b, 1.4142135623730951, tol=1e-7, rel_tol=1e-7) + assert (b >= 1) is True diff --git a/src/flint/test/test_acb_series.py b/src/flint/test/test_acb_series.py new file mode 100644 index 00000000..a6e78d6d --- /dev/null +++ b/src/flint/test/test_acb_series.py @@ -0,0 +1,288 @@ +"""Tests for python-flint's `acb_series` type.""" + +from __future__ import annotations + +from flint import ( + acb, + acb_poly, + acb_series, + arb, + arb_series, + ctx, + dirichlet_char, + fmpq, + fmpq_poly, + fmpq_series, + fmpz, + fmpz_poly, + fmpz_series, +) +from flint.test.helpers import is_close_acb, is_close_acb_series as is_close, raises + + +def test_acb_series_constructor_and_basic() -> None: + old_cap = ctx.cap + try: + ctx.cap = 8 + p0 = acb_series() + assert len(p0) == 0 + assert p0.length() == 0 + assert p0.prec == 8 + + p = acb_series([1, 2], prec=3) + assert len(p) == 2 + assert p.length() == 2 + assert p.prec == 3 + assert p.repr() == "acb_series([1.00000000000000, 2.00000000000000], prec=3)" + assert str(p) == "1.00000000000000 + 2.00000000000000*x + O(x^3)" + + q = acb_series(p) + assert q is not p + assert is_close(q, p) + assert q.prec == p.prec + + assert is_close(acb_series(arb_series([1, 2], prec=4)), acb_series([1, 2], prec=4)) + assert is_close(acb_series(fmpz_series([1, 2], prec=4)), acb_series([1, 2], prec=4)) + assert is_close(acb_series(fmpq_series([1, fmpq(1, 2)], prec=4)), acb_series([1, 1/2], prec=4)) + assert is_close(acb_series(fmpz_poly([1, 2])), acb_series([1, 2], prec=8)) + assert is_close(acb_series(fmpq_poly([1, fmpq(1, 2)])), acb_series([1, 1/2], prec=8)) + assert is_close(acb_series(acb_poly([1, 2])), acb_series([1, 2], prec=8)) + assert is_close(acb_series(5, prec=1), [5]) + + assert str(acb_series([1], prec=0)) == "O(x^0)" + assert str(acb_series([1], prec=-1)) == "(invalid power series)" + + x = acb_series([1], prec=4) + x[3] = 5 + assert is_close_acb(x[3], 5) + assert is_close_acb(x[-1], 0) + assert raises(lambda: x.__setitem__(-1, 1), ValueError) + finally: + ctx.cap = old_cap + + +def test_acb_series_arithmetic_and_valuation() -> None: + old_cap = ctx.cap + try: + ctx.cap = 8 + x = acb_series([0, 1], prec=8) + one = acb_series([1], prec=8) + x2 = acb_series([0, 1], prec=2) + one2 = acb_series([1], prec=2) + + assert (+x) is x + assert is_close(-x2, [0, -1]) + + assert is_close(x + 1, one + x) + assert is_close(1 + x, one + x) + assert is_close(x - 1, x + (-1)) + assert is_close(1 - x, one - x) + assert is_close((one + x) * (one - x), one - x * x) + assert is_close(2 * x, x + x) + assert is_close(x * 2, x + x) + assert is_close(x2 + 2j, [2j, 1]) + assert is_close(2j + x2, [2j, 1]) + assert is_close(x + fmpz(2), x + 2) + assert is_close(x + fmpq(1, 2), x + 0.5) + assert is_close(x + arb(1.25), x + 1.25) + assert is_close(x + acb(1.25, -0.5), x + (1.25 - 0.5j)) + assert is_close(x2 + fmpz_poly([1, 1]), [1, 2]) + assert is_close(x2 + fmpq_poly([1, fmpq(1, 2)]), [1, 3/2]) + assert is_close(x2 + fmpz_series([1, 1], prec=2), [1, 2]) + assert is_close(x2 + fmpq_series([1, fmpq(1, 2)], prec=2), [1, 3/2]) + + x8 = acb_series([0, 1], prec=8) + assert x8.valuation() == 1 + assert acb_series([0], prec=8).valuation() == -1 + assert acb_series([0, 0, 2], prec=8).valuation() == 2 + + assert raises(lambda: x8 + object(), TypeError) # type: ignore[operator] + assert raises(lambda: x8 - object(), TypeError) # type: ignore[operator] + assert raises(lambda: x8 * object(), TypeError) # type: ignore[operator] + assert raises(lambda: object() + x8, TypeError) # type: ignore[operator] + assert raises(lambda: object() - x8, TypeError) # type: ignore[operator] + assert raises(lambda: object() * x8, TypeError) # type: ignore[operator] + finally: + ctx.cap = old_cap + + +def test_acb_series_division_and_pow() -> None: + old_cap = ctx.cap + try: + ctx.cap = 8 + x = acb_series([0, 1], prec=8) + one = acb_series([1], prec=8) + + assert is_close(x / (one + x), x - x**2 + x**3 - x**4 + x**5 - x**6 + x**7) + assert ((x * x) / (x * (one + x))).prec == 7 + assert is_close((x * x) / (x * (one + x)), [0, 1, -1, 1, -1, 1, -1]) + assert (acb_series([0], prec=8) / (one + x)).prec == 8 + x2 = acb_series([0, 1], prec=2) + assert is_close(x2 / 2, [0, 1/2]) + assert is_close(2 / (one + x), [2, -2, 2, -2, 2, -2, 2, -2]) + assert raises(lambda: x / acb_series([0], prec=8), ZeroDivisionError) + assert raises(lambda: one / x, ValueError) + assert raises(lambda: 2 / x, ValueError) + bad = acb_series([acb(arb(0, 1), 0), 1], prec=8) + assert raises(lambda: x / bad, ValueError) + assert raises(lambda: x / object(), TypeError) # type: ignore[operator] + assert raises(lambda: object() / x, TypeError) # type: ignore[operator] + + assert is_close((one + x) ** 2, one + 2 * x + x * x) + y = 2**x + assert is_close_acb(y[0], 1) + assert is_close_acb(y[1], 0.6931471805599453, tol=1e-12, rel_tol=1e-12) + assert raises(lambda: pow(x, 2, 3), NotImplementedError) # type: ignore[misc] + assert raises(lambda: x**object(), TypeError) # type: ignore[operator] + assert raises(lambda: object() ** x, TypeError) # type: ignore[operator] + finally: + ctx.cap = old_cap + + +def test_acb_series_call_reversion_inv() -> None: + old_cap = ctx.cap + try: + ctx.cap = 8 + x = acb_series([0, 1], prec=8) + one = acb_series([1], prec=8) + + assert is_close((one + x)(x), one + x) + assert raises(lambda: (one + x)(one + x), ValueError) + assert raises(lambda: (one + x)(1), TypeError) # type: ignore[arg-type] + + r = (x + x * x).reversion() + assert is_close((x + x * x)(r), x) + assert raises(lambda: (one + x).reversion(), ValueError) + + inv = (one + x).inv() + assert is_close((one + x) * inv, one) + assert raises(lambda: acb_series([0], prec=8).inv(), ZeroDivisionError) + finally: + ctx.cap = old_cap + + +def test_acb_series_elementary_functions() -> None: + old_cap = ctx.cap + try: + ctx.cap = 8 + x = acb_series([0, 1], prec=8) + one = acb_series([1], prec=8) + x2 = acb_series([0, 1], prec=2) + one2 = acb_series([1], prec=2) + x3 = acb_series([0, 1], prec=3) + one3 = acb_series([1], prec=3) + + x7 = acb_series([0, 1], prec=7) + assert is_close((one3 + x3 + x3 * x3).derivative(), [1, 2]) + assert is_close((one2 + x2).integral(), [0, 1, 1/2]) + assert is_close((one + x).sqrt(), [1, 1/2, -1/8, 1/16, -5/128, 7/256, -21/1024, 33/2048]) + assert is_close((one + x).rsqrt(), [1, -1/2, 3/8, -5/16, 35/128, -63/256, 231/1024, -429/2048]) + assert is_close(x.exp(), [1, 1, 1/2, 1/6, 1/24, 1/120, 1/720, 1/5040]) + assert is_close((one + x).log(), [0, 1, -1/2, 1/3, -1/4, 1/5, -1/6, 1/7]) + assert is_close(x.atan(), [0, 1, 0, -1/3, 0, 1/5, 0, -1/7]) + assert is_close(x.sin(), [0, 1, 0, -1/6, 0, 1/120, 0, -1/5040]) + assert is_close(x7.cos(), [1, 0, -1/2, 0, 1/24, 0, -1/720]) + assert is_close(x.tan(), [0, 1, 0, 1/3, 0, 2/15, 0, 17/315]) + + s, c = x.sin_cos() + assert is_close(s, [0, 1, 0, -1/6, 0, 1/120, 0, -1/5040]) + assert is_close(c, x.cos()) + assert is_close(s * s + c * c, one) + + pi = acb.pi() + assert is_close(x.sin_pi(), [0, pi, 0, -(pi**3)/6, 0, (pi**5)/120, 0, -(pi**7)/5040]) + assert is_close(x7.cos_pi(), [1, 0, -(pi**2)/2, 0, (pi**4)/24, 0, -(pi**6)/720]) + sp, cp = x.sin_cos_pi() + assert is_close(sp, [0, pi, 0, -(pi**3)/6, 0, (pi**5)/120, 0, -(pi**7)/5040]) + assert is_close(cp, x.cos_pi()) + assert is_close(x.sin() / x.cos(), x.tan()) + assert (1 + x).cot_pi().prec == 8 + finally: + ctx.cap = old_cap + + +def test_acb_series_special_and_classmethods() -> None: + old_cap = ctx.cap + try: + ctx.cap = 8 + x = acb_series([0, 1], prec=8) + one = acb_series([1], prec=8) + + assert is_close((one + x).gamma() * (one + x).rgamma(), one) + assert is_close((one + x).lgamma().exp(), (one + x).gamma()) + assert is_close(x.rising(3), 2 * x + 3 * x * x + x**3) + assert is_close_acb((2 + x).zeta()[0], acb(2).zeta(), tol=1e-8, rel_tol=1e-8) + chi = dirichlet_char(3, 1) + assert is_close((2 + x).dirichlet_l(chi), (2 + x).dirichlet_l((3, 1))) + assert (2 + x).dirichlet_l((3, 1), deflate=True).prec == 8 + + p = acb_series.polylog(1 + x, 0.5) + assert p.prec == 8 + + assert (2 + x).agm().prec == 8 + assert is_close((2 + x).agm(3 + x), ((2 + x) / (3 + x)).agm() * (3 + x)) + assert (x * x).elliptic_k().prec == 8 + assert x.elliptic_p(1j).prec == 8 + + assert is_close(x.erf() + x.erfc(), one) + assert x.erfi().prec == 8 + + up = acb_series.gamma_upper(2, x) + lo = acb_series.gamma_lower(2, x) + assert is_close(acb_series([up[0] + lo[0]], prec=1), [1]) + assert acb_series.gamma_upper(2, x, regularized=1).prec == 8 + assert acb_series.gamma_lower(2, x, regularized=1).prec == 8 + assert acb_series.beta_lower(2, 3, x).prec == 8 + assert acb_series.beta_lower(2, 3, x, regularized=1).prec == 8 + + assert acb_series.hypgeom([1 + x], [2 + x], x).prec == 8 + assert acb_series.hypgeom([1 + x], [2 + x], x, n=4, regularized=True).prec == 8 + + w0 = x.lambertw() + wm1 = x.lambertw(-1) + assert is_close(w0.exp() * w0, x) + assert wm1.prec == 8 + finally: + ctx.cap = old_cap + + +def test_acb_series_airy_modular_coulomb_fresnel_and_misc() -> None: + old_cap = ctx.cap + try: + ctx.cap = 8 + x = acb_series([0, 1], prec=8) + + ai, aip, bi, bip = x.airy() + assert is_close(ai, x.airy_ai()) + assert is_close(aip, x.airy_ai_prime()) + assert is_close(bi, x.airy_bi()) + assert is_close(bip, x.airy_bi_prime()) + + t1, t2, t3, t4 = x.modular_theta(1j) + assert t1.prec == 8 + assert t2.prec == 8 + assert t3.prec == 8 + assert t4.prec == 8 + + f, g, hpos, hneg = x.coulomb(0, 0) + assert is_close(f, x.coulomb_f(0, 0)) + assert str(g) == str(x.coulomb_g(0, 0)) + assert hpos.prec == 8 + assert hneg.prec == 8 + + fs, fc = x.fresnel() + assert is_close(fs, x.fresnel_s()) + assert is_close(fc, x.fresnel_c()) + assert x.fresnel(normalized=False)[0].prec == 8 + assert x.fresnel_s(normalized=False).prec == 8 + assert x.fresnel_c(normalized=False).prec == 8 + + assert x.ei().prec == 8 + assert is_close_acb(x.si()[0], 0) + assert x.ci().prec == 8 + assert is_close_acb(x.shi()[0], 0) + assert x.chi().prec == 8 + assert (2 + x).li().prec == 8 + assert (2 + x).li(offset=True).prec == 8 + finally: + ctx.cap = old_cap diff --git a/src/flint/test/test_acb_theta.py b/src/flint/test/test_acb_theta.py new file mode 100644 index 00000000..4ea6237a --- /dev/null +++ b/src/flint/test/test_acb_theta.py @@ -0,0 +1,47 @@ +from __future__ import annotations + +from flint import _has_acb_theta, acb, acb_mat +from flint.test.helpers import is_close_acb_mat as is_close, raises + + +def test_acb_theta_basic() -> None: + if not _has_acb_theta(): + return + + from flint.types.acb_theta import acb_theta + + z = acb(1 + 1j) + tau = acb(1.25 + 3j) + zmat = acb_mat([[z]]) + taumat = acb_mat([[tau]]) + + direct = acb_theta(zmat, taumat) + via_method = taumat.theta(zmat) + assert is_close(direct, via_method, tol=1e-12, rel_tol=1e-12, max_width=1e-12) + assert direct.nrows() == 1 + assert direct.ncols() == 4 + + squared = acb_theta(zmat, taumat, square=True) + assert squared.nrows() == 1 + assert squared.ncols() == 4 + + +def test_acb_theta_shape_assertions() -> None: + if not _has_acb_theta(): + return + + from flint.types.acb_theta import acb_theta + + z = acb_mat([[0]]) + tau_bad = acb_mat([[1j, 0]]) + assert raises(lambda: acb_theta(z, tau_bad), AssertionError) + + tau = acb_mat([[1j]]) + z_bad_rows = acb_mat([[0], [0]]) + assert raises(lambda: acb_theta(z_bad_rows, tau), AssertionError) + + z_bad_cols = acb_mat([[0, 0]]) + assert raises(lambda: acb_theta(z_bad_cols, tau), AssertionError) + + assert raises(lambda: acb_theta(object(), tau), TypeError) # type: ignore[arg-type] + assert raises(lambda: acb_theta(z, object()), TypeError) # type: ignore[arg-type] diff --git a/src/flint/test/test_all.py b/src/flint/test/test_all.py index 4f32b8d4..32f4c3b1 100644 --- a/src/flint/test/test_all.py +++ b/src/flint/test/test_all.py @@ -1,37 +1,33 @@ from __future__ import annotations -from typing import Any, Callable, Sequence, TypeVar, Iterable, Protocol, TYPE_CHECKING +from typing import Any, Callable, Sequence, TypeVar, Iterable, Protocol, TYPE_CHECKING, TypeGuard import math import operator import pickle import platform import random +import importlib +import inspect +import pkgutil import flint import flint.typing as typ import flint.flint_base.flint_base as flint_base from flint.utils.flint_exceptions import DomainError, IncompatibleContextError -from flint.test.test_arb import all_tests as arb_tests +from flint.test.helpers import raises PYPY = platform.python_implementation() == "PyPy" ctx = flint.ctx -def raises(f, exception) -> bool: - try: - f() - except exception: - return True - return False - if TYPE_CHECKING: - from typing import TypeIs + from typing_extensions import TypeIs -Tscalar = TypeVar('Tscalar', bound=flint_base.flint_scalar) +Tscalar = TypeVar('Tscalar', bound=typ.scalar_p) Tscalar_co = TypeVar('Tscalar_co', bound=flint_base.flint_scalar, covariant=True) Tmpoly = TypeVar('Tmpoly', bound=flint_base.flint_mpoly) Tmpoly_p = TypeVar('Tmpoly_p', bound=typ.mpoly_p) @@ -967,7 +963,8 @@ def test_fmpq() -> None: (lambda n: flint.fmpq.dedekind_sum(n, 3), [-Q(1,18), Q(0), Q(1,18), -Q(1,18), Q(0), Q(1,18), -Q(1,18)]), ] - is_exception = lambda v: isinstance(v, type) and issubclass(v, Exception) + def is_exception(v: object) -> TypeGuard[type[Exception]]: + return isinstance(v, type) and issubclass(v, Exception) for func2, values2 in cases2: for n, val in enumerate(values2, -1): @@ -1102,6 +1099,9 @@ def test_fmpq_mat(): assert (Q(1,1,[1]) != 1) is True assert (1 == Q(1,1,[1])) is False assert (1 != Q(1,1,[1])) is True + marker = object() + assert (Q(1,1,[1]) == marker) is False + assert (Q(1,1,[1]) != marker) is True assert Q(1,2,[3,4]) * 2 == Q(1,2,[6,8]) assert Q(1,2,[3,4]) * flint.fmpq(1,3) == Q(1,2,[1,flint.fmpq(4,3)]) assert Q(1,2,[3,4]) * flint.fmpq(5,3) == Q(1,2,[5,flint.fmpq(20,3)]) @@ -1113,6 +1113,7 @@ def test_fmpq_mat(): assert M ** 1 == M assert M ** 2 == Q([[7,10],[15,22]]) assert M ** 12 == Q([[138067399, 201223170],[301834755, 439902154]]) + assert raises(lambda: pow(M, 2, 3), TypeError) M = Q([[1,2],[2,4]]) assert raises(lambda: M ** -1, ZeroDivisionError) assert Q(1,2,[3,4]) / 2 == Q(1,2,[flint.fmpq(3,2),2]) @@ -1172,6 +1173,9 @@ def set_bad(i): X = Q([[1,2],[3,4]]) B = A*X assert A.solve(B) == X + I25 = Q([[1 if i == j else 0 for j in range(25)] for i in range(25)]) + X25 = Q.hilbert(25, 2) + assert I25.solve(X25) == X25 for algorithm in None, "fflu", "dixon": assert A.solve(B, algorithm=algorithm) == X assert raises(lambda: A.solve(B, algorithm="invalid"), ValueError) @@ -1590,6 +1594,7 @@ def test_nmod_mat(): assert str(M(2,2,[1,2,3,4],17)) == '[1, 2]\n[3, 4]' assert repr(M(2,2,[1,2,3,4],17)) == '[1, 2]\n[3, 4]' assert M(1,2,[3,4],17) / 3 == M(1,2,[3,4],17) * (~G(3,17)) + assert raises(lambda: pow(M([[1]],17), 2, 3), NotImplementedError) assert M(2,2,[1,2,3,4], 17).inv().det() == ~(M(2,2,[1,2,3,4], 17).det()) assert M(2,2,[1,2,3,4], 17).inv().inv() == M(2,2,[1,2,3,4], 17) assert M(2,2,[0,1,2,3],17) * M(2, 2, [2,3,4,5], 17) == M(2,2,[4,5,16,4],17) @@ -1607,6 +1612,11 @@ def test_nmod_mat(): assert (M([[1]],17) != M([[1]],13)) is True assert (M([[1]],17) == None) is False assert (M([[1]],17) != None) is True + class _nmod_mat_subclass(M): + pass + + assert raises(lambda: M([[1]],3) + _nmod_mat_subclass([[1]],5), ValueError) + assert raises(lambda: M([[1]],3) - _nmod_mat_subclass([[1]],5), ValueError) M2 = M.randtest(3,4,5) assert all(0 <= int(x) < 5 for x in M2.entries()) assert (M2.nrows(), M2.ncols()) == (3, 4) @@ -1681,7 +1691,184 @@ def test_nmod_series(): pass -def test_arb(): +def test_arf() -> None: + oldpretty = ctx.pretty + oldprec = ctx.prec + try: + ctx.prec = 53 + + z = flint.arf() + assert z.is_zero() is True + assert z.is_finite() is True + assert z.man_exp() == (flint.fmpz(0), flint.fmpz(0)) + assert str(z) == "0.0" + + a = flint.arf(-100) + b = flint.arf(15.125) + c = flint.arf(flint.arf(3)) + e = flint.arf(flint.fmpz(7)) + d = flint.arf((10, -2)) + assert a.is_finite() is True + assert b.is_finite() is True + assert c.is_finite() is True + assert d.is_finite() is True + assert e.is_finite() is True + assert str(e) == "7.00000000000000" + assert d.man_exp() == (flint.fmpz(5), flint.fmpz(-1)) + assert str(d) == "2.50000000000000" + assert repr(d) == str(d) == "2.50000000000000" + + assert flint.arf(1.0 / 3.0)._dec_str(num_digits=6).startswith("0.333333") + + pinf = flint.arf("inf") + pinf2 = flint.arf("+inf") + ninf = flint.arf("-inf") + nan = flint.arf("nan") + assert pinf.is_pos_inf() is True + assert ninf.is_pos_inf() is False + assert pinf2.is_pos_inf() is True + assert ninf.is_neg_inf() is True + assert nan.is_nan() is True + assert str(pinf) == "inf" + assert str(ninf) == "-inf" + assert str(nan) == "nan" + + assert raises(lambda: flint.arf("bogus"), TypeError) + assert raises(lambda: flint.arf((10, "bad")), TypeError) # type: ignore + assert raises(lambda: flint.arf([]), TypeError) # type: ignore + assert raises(lambda: flint.arf(object()), TypeError) # type: ignore + assert flint.arf(flint.fmpq(3, 2)) == flint.arf(1.5) + assert raises(lambda: flint.arf(1j), TypeError) # type: ignore + assert raises(lambda: flint.arf({}), TypeError) # type: ignore + + ctx.pretty = False + assert repr(z) == "arf(0.0)" + assert repr(d) == "arf((0x5, -0x1))" + assert repr(flint.arf(1.25)) == "arf((0x5, -0x2))" + assert repr(pinf) == "arf('+inf')" + assert repr(ninf) == "arf('-inf')" + assert repr(nan) == "arf('nan')" + ctx.pretty = oldpretty + + x = flint.arf(2) + y = flint.arf(3) + assert (x == flint.arf(2)) is True + assert (x == 2) is True + assert (x == 2.0) is True + assert (x == 2.5) is False + assert (x != 2) is False + assert (x != y) is True + assert (x < y) is True + assert (x <= y) is True + assert (y > x) is True + assert (y >= x) is True + assert (x == 2) is True + assert (x != 3) is True + assert (x == flint.fmpz(2)) is True + assert (x != flint.fmpz(3)) is True + assert (x < flint.fmpz(3)) is True + assert (x == 2.0) is True + assert (x != 2.0) is False + assert (x < 3) is True + assert (2 < y) is True + assert (x < 2.5) is True + assert (x < flint.fmpq(5, 2)) is True + assert (x == flint.fmpq(4, 2)) is True + assert (x != flint.fmpq(5, 2)) is True + one = flint.arf(1) + q_close = flint.fmpq(2**80 + 1, 2**80) + assert (flint.fmpq(1, 1) < q_close) is True + assert (one == q_close) is False + assert (one < q_close) is True + assert (flint.arf("nan") == flint.fmpq(1, 2)) is False + assert (flint.arf("nan") != flint.fmpq(1, 2)) is True + assert (flint.arf("inf") > flint.fmpq(1, 2)) is True + assert (flint.arf("-inf") < flint.fmpq(1, 2)) is True + huge_cmp = 1 << 200 + assert (flint.arf(huge_cmp) == huge_cmp) is True + assert (flint.arf(huge_cmp) != huge_cmp + 1) is True + assert (flint.arf(huge_cmp) < huge_cmp + 1) is True + assert raises(lambda: x < "bad", TypeError) # type: ignore + + assert bool(flint.arf(0)) is False + assert bool(flint.arf("nan")) is True + assert bool(flint.arf("inf")) is True + assert float(flint.arf(1.5)) == 1.5 + assert int(flint.arf(2.9)) == 2 + assert int(flint.arf(-2.9)) == -2 + assert raises(lambda: int(flint.arf("nan")), ValueError) + assert raises(lambda: int(flint.arf("inf")), OverflowError) + assert flint.arf(0).as_integer_ratio() == (0, 1) + assert flint.arf(-1.25).as_integer_ratio() == (-5, 4) + assert raises(lambda: flint.arf("nan").as_integer_ratio(), ValueError) + assert raises(lambda: flint.arf("-inf").as_integer_ratio(), OverflowError) + + # unary ops + assert +x == x + assert -x == flint.arf(-2) + assert abs(flint.arf(-2)) == flint.arf(2) + + # binary ops and NotImplemented paths + assert x + y == flint.arf(5) + assert y - x == flint.arf(1) + assert x * y == flint.arf(6) + assert x / y == flint.arf(2 / 3) + assert x + 1 == flint.arf(3) + assert x + flint.fmpz(1) == flint.arf(3) + assert x + 0.5 == flint.arf(2.5) + assert 0.5 + x == flint.arf(2.5) + assert flint.fmpz(1) + x == flint.arf(3) + assert x - 1 == flint.arf(1) + assert x - flint.fmpz(1) == flint.arf(1) + assert x - 0.5 == flint.arf(1.5) + assert 5.0 - x == flint.arf(3) + assert flint.fmpz(5) - x == flint.arf(3) + assert x * 3 == flint.arf(6) + assert x * flint.fmpz(3) == flint.arf(6) + assert x * 0.5 == flint.arf(1) + assert 0.5 * x == flint.arf(1) + assert flint.fmpz(3) * x == flint.arf(6) + assert x / 2 == flint.arf(1) + assert x / flint.fmpz(2) == flint.arf(1) + assert x / 0.5 == flint.arf(4) + assert 6.0 / x == flint.arf(3) + assert flint.fmpz(6) / x == flint.arf(3) + huge = 1 << 200 + assert x + huge == x + flint.fmpz(huge) + assert huge + x == flint.fmpz(huge) + x + assert x - huge == x - flint.fmpz(huge) + assert huge - x == flint.fmpz(huge) - x + assert x * huge == x * flint.fmpz(huge) + assert huge * x == flint.fmpz(huge) * x + assert x / huge == x / flint.fmpz(huge) + assert huge / x == flint.fmpz(huge) / x + half = flint.fmpq(1, 2) + assert raises(lambda: x + half, TypeError) # type: ignore + assert raises(lambda: half + x, TypeError) # type: ignore + assert raises(lambda: x - half, TypeError) # type: ignore + assert raises(lambda: half - x, TypeError) # type: ignore + assert raises(lambda: x * half, TypeError) # type: ignore + assert raises(lambda: half * x, TypeError) # type: ignore + assert raises(lambda: x / half, TypeError) # type: ignore + assert raises(lambda: half / x, TypeError) # type: ignore + assert 1 + x == flint.arf(3) + assert 5 - x == flint.arf(3) + assert 2 * x == flint.arf(4) + assert 6 / x == flint.arf(3) + assert raises(lambda: x + "bad", TypeError) # type: ignore + assert raises(lambda: x - "bad", TypeError) # type: ignore + assert raises(lambda: x * "bad", TypeError) # type: ignore + assert raises(lambda: x / "bad", TypeError) # type: ignore + assert raises(lambda: "bad" + x, TypeError) # type: ignore + assert raises(lambda: "bad" - x, TypeError) # type: ignore + assert raises(lambda: "bad" * x, TypeError) # type: ignore + assert raises(lambda: "bad" / x, TypeError) # type: ignore + finally: + ctx.pretty = oldpretty + ctx.prec = oldprec + + +def test_arb() -> None: arb = flint.arb assert arb(3) > arb(2.5) assert arb(3) >= arb("2.5") @@ -2598,46 +2785,46 @@ def _all_polys() -> list[tuple[Any, Any, bool, flint.fmpz]]: (flint.fmpq_poly, flint.fmpq, True, flint.fmpz(0)), # Z/pZ (p prime) - (lambda *a: flint.nmod_poly(*a, 17), lambda x: flint.nmod(x, 17), True, flint.fmpz(17)), - (lambda *a: flint.fmpz_mod_poly(*a, flint.fmpz_mod_poly_ctx(163)), + (lambda a: flint.nmod_poly(a, 17), lambda x: flint.nmod(x, 17), True, flint.fmpz(17)), + (lambda a: flint.fmpz_mod_poly(a, flint.fmpz_mod_poly_ctx(163)), lambda x: flint.fmpz_mod(x, flint.fmpz_mod_ctx(163)), True, flint.fmpz(163)), - (lambda *a: flint.fmpz_mod_poly(*a, flint.fmpz_mod_poly_ctx(2**127 - 1)), + (lambda a: flint.fmpz_mod_poly(a, flint.fmpz_mod_poly_ctx(2**127 - 1)), lambda x: flint.fmpz_mod(x, flint.fmpz_mod_ctx(2**127 - 1)), True, flint.fmpz(2**127 - 1)), - (lambda *a: flint.fmpz_mod_poly(*a, flint.fmpz_mod_poly_ctx(2**255 - 19)), + (lambda a: flint.fmpz_mod_poly(a, flint.fmpz_mod_poly_ctx(2**255 - 19)), lambda x: flint.fmpz_mod(x, flint.fmpz_mod_ctx(2**255 - 19)), True, flint.fmpz(2**255 - 19)), # GF(p^k) (p prime) - (lambda *a: flint.fq_default_poly(*a, flint.fq_default_poly_ctx(2**127 - 1)), + (lambda a: flint.fq_default_poly(a, flint.fq_default_poly_ctx(2**127 - 1)), lambda x: flint.fq_default(x, flint.fq_default_ctx(2**127 - 1)), True, flint.fmpz(2**127 - 1)), - (lambda *a: flint.fq_default_poly(*a, flint.fq_default_poly_ctx(2**127 - 1, 2)), + (lambda a: flint.fq_default_poly(a, flint.fq_default_poly_ctx(2**127 - 1, 2)), lambda x: flint.fq_default(x, flint.fq_default_ctx(2**127 - 1, 2)), True, flint.fmpz(2**127 - 1)), - (lambda *a: flint.fq_default_poly(*a, flint.fq_default_poly_ctx(65537)), + (lambda a: flint.fq_default_poly(a, flint.fq_default_poly_ctx(65537)), lambda x: flint.fq_default(x, flint.fq_default_ctx(65537)), True, flint.fmpz(65537)), - (lambda *a: flint.fq_default_poly(*a, flint.fq_default_poly_ctx(65537, 5)), + (lambda a: flint.fq_default_poly(a, flint.fq_default_poly_ctx(65537, 5)), lambda x: flint.fq_default(x, flint.fq_default_ctx(65537, 5)), True, flint.fmpz(65537)), - (lambda *a: flint.fq_default_poly(*a, flint.fq_default_poly_ctx(11)), + (lambda a: flint.fq_default_poly(a, flint.fq_default_poly_ctx(11)), lambda x: flint.fq_default(x, flint.fq_default_ctx(11)), True, flint.fmpz(11)), - (lambda *a: flint.fq_default_poly(*a, flint.fq_default_poly_ctx(11, 5)), + (lambda a: flint.fq_default_poly(a, flint.fq_default_poly_ctx(11, 5)), lambda x: flint.fq_default(x, flint.fq_default_ctx(11, 5)), True, flint.fmpz(11)), # Z/nZ (n composite) - (lambda *a: flint.nmod_poly(*a, 16), lambda x: flint.nmod(x, 16), False, flint.fmpz(16)), - (lambda *a: flint.fmpz_mod_poly(*a, flint.fmpz_mod_poly_ctx(164)), + (lambda a: flint.nmod_poly(a, 16), lambda x: flint.nmod(x, 16), False, flint.fmpz(16)), + (lambda a: flint.fmpz_mod_poly(a, flint.fmpz_mod_poly_ctx(164)), lambda x: flint.fmpz_mod(x, flint.fmpz_mod_ctx(164)), False, flint.fmpz(164)), - (lambda *a: flint.fmpz_mod_poly(*a, flint.fmpz_mod_poly_ctx(2**127)), + (lambda a: flint.fmpz_mod_poly(a, flint.fmpz_mod_poly_ctx(2**127)), lambda x: flint.fmpz_mod(x, flint.fmpz_mod_ctx(2**127)), False, flint.fmpz(2**127)), - (lambda *a: flint.fmpz_mod_poly(*a, flint.fmpz_mod_poly_ctx(2**255)), + (lambda a: flint.fmpz_mod_poly(a, flint.fmpz_mod_poly_ctx(2**255)), lambda x: flint.fmpz_mod(x, flint.fmpz_mod_ctx(2**255)), False, flint.fmpz(2**255)), ] @@ -3026,8 +3213,11 @@ def setbad(obj, i, val): p1 = P([1, 0, 1]) p2 = P([2, 1]) g, s, t = P([1]), P([1])/5, P([2, -1])/5 - assert p1.xgcd(p2) == (g, s, t) - assert raises(lambda: p1.xgcd(None), TypeError) # type: ignore + if isinstance(p1, (flint.fmpq_poly, flint.nmod_poly, flint.fmpz_mod_poly, flint.fq_default_poly)): + assert p1.xgcd(p2) == (g, s, t) + assert raises(lambda: p1.xgcd(None), TypeError) # type: ignore + else: + assert False if not composite_characteristic: assert P([1, 2, 1]).factor() == (S(1), [(P([1, 1]), 2)]) @@ -3066,16 +3256,16 @@ def setbad(obj, i, val): # resultant checks. x = P([0, 1]) - if composite_characteristic and type(x) in [flint.fmpz_mod_poly, flint.nmod_poly]: + if composite_characteristic and isinstance(x, (flint.fmpz_mod_poly, flint.nmod_poly)): # Flint sometimes crashes in this case, even though the resultant # could be computed. divisor = characteristic.factor()[0][0] assert raises(lambda: x.resultant(x + divisor), ValueError) - elif type(x) == flint.fq_default_poly: + elif isinstance(x, flint.fq_default_poly): # Flint does not implement resultants over GF(q) for nonprime q, so # there's nothing for us to check. pass - else: + elif isinstance(x, (flint.fmpz_poly, flint.fmpq_poly, flint.nmod_poly, flint.fmpz_mod_poly)): assert x.resultant(x) == 0 assert x.resultant(x**2 + x - x) == 0 assert x.resultant(x**10 - x**5 + 1) == S(1) @@ -3086,6 +3276,8 @@ def setbad(obj, i, val): for k in range(-10, 10): assert x.resultant(x + S(k)) == S(k) + else: + assert False def test_poly_resultants(): @@ -3274,7 +3466,7 @@ def quick_poly(): assert new_ctx != ctx assert new_poly != quick_poly() - new_ctx = new_ctx.from_context(new_ctx, ordering=ctx.ordering()) + new_ctx = get_context(new_ctx.names(), ordering=ctx.ordering()) assert new_ctx == ctx assert new_poly.project_to_context(new_ctx) == quick_poly() @@ -3480,7 +3672,14 @@ def quick_poly(): + mpoly({(0, 0): 5, (0, 1): 6, (1, 0): 7, (2, 2): 8}) \ == mpoly({(0, 0): 6, (0, 1): 8, (1, 0): 10, (2, 2): 12}) - for T in [int, S, flint.fmpz, lambda x: P(x, ctx=ctx)]: + converters: tuple[Callable[[int], int | flint.fmpz | Tscalar | Tmpoly_p], ...] = ( + lambda x: int(x), + lambda x: S(x), + lambda x: flint.fmpz(x), + lambda x: P(x, ctx=ctx), + ) + + for T in converters: p = quick_poly() p += T(1) q = quick_poly() @@ -3499,7 +3698,7 @@ def quick_poly(): assert quick_poly() - mpoly({(0, 0): 5, (0, 1): 6, (1, 0): 7, (2, 2): 8}) \ == mpoly({(0, 0): -4, (0, 1): -4, (1, 0): -4, (2, 2): -4}) - for T in [int, S, flint.fmpz, lambda x: P(x, ctx=ctx)]: + for T in converters: p = quick_poly() p -= T(1) q = quick_poly() @@ -3524,7 +3723,7 @@ def quick_poly(): (0, 1): 6 }) - for T in [int, S, flint.fmpz, lambda x: P(x, ctx=ctx)]: + for T in converters: p = quick_poly() p *= T(2) q = quick_poly() @@ -3712,9 +3911,9 @@ def quick_poly(): if isinstance(p, (flint.fmpz_mpoly, flint.fmpq_mpoly)): if isinstance(p, flint.fmpq_mpoly) and _is_Q(S): assert p.integral(0) == p.integral("x0") == \ - mpoly({(3, 2): S(4, 3), (2, 0): S(3, 2), (1, 1): S(2), (1, 0): S(1)}) + mpoly({(3, 2): S(4) / 3, (2, 0): S(3) / 2, (1, 1): S(2), (1, 0): S(1)}) assert p.integral(1) == p.integral("x1") == \ - mpoly({(2, 3): S(4, 3), (1, 1): S(3), (0, 2): S(1), (0, 1): S(1)}) + mpoly({(2, 3): S(4) / 3, (1, 1): S(3), (0, 2): S(1), (0, 1): S(1)}) else: assert p.integral(0) == p.integral("x0") == \ (6, mpoly({(3, 2): 8, (2, 0): 9, (1, 1): 12, (1, 0): 6})) @@ -5163,87 +5362,25 @@ def get_dets(): def test_all_tests(): - test_funcs = {f for name, f in globals().items() if name.startswith("test_")} - untested = test_funcs - set(all_tests) - assert not untested, f"Untested functions: {untested}" - - -all_tests = [ - test_pyflint, - test_showgood, - - test_fmpz, - test_fmpz_factor, - test_fmpz_functions, - test_fmpz_poly, - test_fmpz_poly_factor, - test_fmpz_poly_functions, - test_fmpz_mat, - test_fmpz_series, - - test_fmpq, - test_fmpq_poly, - test_fmpq_mat, - test_fmpq_series, - - test_nmod, - test_nmod_poly, - test_nmod_mat, - test_nmod_series, - - test_fmpz_mod, - test_fmpz_mod_dlog, - test_fmpz_mod_poly, - test_fmpz_mod_mat, - - test_division_scalar, - test_division_poly, - test_division_matrix, - - test_properties_poly_mpoly, - test_factor_poly_mpoly, - test_division_poly_mpoly, - - test_polys, - test_mpolys_constructor, - test_mpolys_properties, - - test_poly_resultants, - - test_fmpz_mpoly_vec, - - test_matrices_eq, - test_matrices_constructor, - test_matrices_strrepr, - test_matrices_getitem, - test_matrices_setitem, - test_matrices_bool, - test_matrices_transpose, - test_matrices_pos_neg, - test_matrices_add, - test_matrices_sub, - test_matrices_mul, - test_matrices_pow, - test_matrices_div, - test_matrices_properties, - test_matrices_inv, - test_matrices_det, - test_matrices_charpoly, - test_matrices_minpoly, - test_matrices_rank, - test_matrices_rref, - test_matrices_solve, - test_matrices_fflu, - - test_fq_default, - test_fq_default_poly, - - test_arb, - test_acb, - - test_pickling, - - test_python_threads, - - test_all_tests, -] + arb_tests + from flint.test.__main__ import collect_all_tests + + collected = set(collect_all_tests()) + expected = set() + + import flint.test as test_pkg + for mod in pkgutil.iter_modules(test_pkg.__path__, test_pkg.__name__ + "."): + mod_name = mod.name.rsplit(".", 1)[-1] + if not mod_name.startswith("test_"): + continue + module = importlib.import_module(mod.name) + expected.update( + obj for name, obj in vars(module).items() + if ( + name.startswith("test_") + and inspect.isfunction(obj) + and obj.__module__ == module.__name__ + and len(inspect.signature(obj).parameters) == 0 + ) + ) + + assert collected == expected diff --git a/src/flint/test/test_arb.py b/src/flint/test/test_arb.py index 66fce5f2..dfabe06e 100644 --- a/src/flint/test/test_arb.py +++ b/src/flint/test/test_arb.py @@ -1,14 +1,17 @@ """Test for python-flint's `arb` type.""" +from __future__ import annotations + import math from flint import arb, ctx +from flint.test.helpers import is_close_arb, raises -def assert_almost_equal(x, y, places=7): +def assert_almost_equal(x: float | int, y: float | int, places: int = 7) -> None: """Helper method for approximate comparisons.""" assert round(x-y, ndigits=places) == 0 -def test_from_int(): +def test_from_int() -> None: """Tests instantiating `arb`s from ints.""" for val in [ -42 * 10**9, @@ -24,31 +27,31 @@ def test_from_int(): man, exp = x.man_exp() assert (man * 2**exp) == val -def test_from_float(): +def test_from_float() -> None: """Tests instantiating `arb`s from floats.""" for val in [0.0, 1.1, -1.1, 9.9 * 0.123, 99.12]: x = arb(val) man, exp = x.man_exp() assert (int(man) * 2 ** int(exp)) == val -def test_from_float_inf(): +def test_from_float_inf() -> None: """Tests `arb` works with +/- inf.""" posinf = arb(float("inf")) neginf = arb(float("-inf")) - assert not posinf.is_finite() - assert not neginf.is_finite() + assert posinf.is_finite() is False + assert neginf.is_finite() is False assert float(posinf) == float("inf") assert float(neginf) == float("-inf") -def test_from_man_exp(): +def test_from_man_exp() -> None: """Tests instantiating `arb`s with mantissa and exponent.""" for man, exp in [(2, 30), (4, 300), (5 * 10**2, 7**8)]: x = arb(mid=(man, exp)) m, e = x.man_exp() assert (m * 2**e) == (man * 2**exp) -def test_from_midpoint_radius(): +def test_from_midpoint_radius() -> None: """Tests instantiating `arb`s with midpoint and radius.""" for mid, rad in [(10, 1), (10000, 5), (10, 1), (10, 1)]: mid_arb = arb(mid) @@ -58,7 +61,7 @@ def test_from_midpoint_radius(): actual_radius = float(x.rad()) assert_almost_equal(actual_radius, rad) -def test_is_exact(): +def test_is_exact() -> None: """Tests `arb.is_exact`.""" for arb_val, exact in [ (arb(10), True), @@ -69,30 +72,30 @@ def test_is_exact(): ]: assert arb_val.is_exact() == exact -def test_is_finite(): +def test_is_finite() -> None: """Tests `arb.is_finite`.""" assert not (arb(-float("inf")).is_finite()) assert not (arb(float("inf")).is_finite()) assert (arb(10).is_finite()) -def test_is_nan(): +def test_is_nan() -> None: """Tests `arb.is_nan`.""" assert (arb(float("nan")).is_nan()) assert not (arb(0.0).is_nan()) -def test_lower(): +def test_lower() -> None: """Tests `arb.lower`.""" with ctx.workprec(100): arb_val = arb(1, 0.5) assert_almost_equal(float(arb_val.lower()), 0.5) -def test_upper(): +def test_upper() -> None: """Tests `arb.upper`.""" with ctx.workprec(100): arb_val = arb(1, 0.5) assert_almost_equal(float(arb_val.upper()), 1.5) -def test_contains(): +def test_contains() -> None: """`y.__contains__(x)` returns True iff every number in `x` is also in `y`.""" for x, y, expected in [ ( @@ -110,7 +113,7 @@ def test_contains(): ]: assert (x in y) == expected -def test_hash(): +def test_hash() -> None: """`x` and `y` hash to the same value if they have the same midpoint and radius. Args: @@ -118,7 +121,7 @@ def test_hash(): y: An arb. expected: Whether `x` and `y` should hash to the same value. """ - def arb_pi(prec): + def arb_pi(prec: int) -> arb: """Helper to calculate arb to a given precision.""" with ctx.workprec(prec): return arb.pi() @@ -140,7 +143,430 @@ def arb_pi(prec): else: assert False, f"Expected {x} to raise an error if hashed, but succeeded." +def test_arb_constructor() -> None: + """Cover constructor conversion failures and string parsing branches.""" + assert raises(lambda: arb(object()), TypeError) # type: ignore[arg-type] + assert raises(lambda: arb((1, "bad")), TypeError) # type: ignore[arg-type] + assert raises(lambda: arb("not-a-number"), ValueError) + + x = arb("1.5 ± 0.25") + assert x.contains(arb(1.5)) + assert x.contains(arb(1.25)) + assert x.contains(arb(1.75)) + +def test_arb_contains() -> None: + """Cover contains/overlaps helper methods and real/imag properties.""" + outer = arb(10, 2) # [8, 12] + inner = arb(10, 1) # [9, 11] + touching = arb(12, 1) # [11, 13] + disjoint = arb(20, 0.5) # [19.5, 20.5] + + assert outer.contains(inner) + assert outer.contains_interior(arb(10, 0.5)) + assert not outer.contains_interior(outer) + assert outer.overlaps(touching) + assert not outer.overlaps(disjoint) + + assert arb(1.1, 0.2).contains_integer() + assert not arb(1.1, 0.05).contains_integer() + + x = arb(3, 0.1) + assert x.real is x + assert x.imag == arb(0) + +def test_arb_comparison() -> None: + x = arb(2) + y = arb(3) + assert (x < y) is True + assert (x > y) is False + assert (x <= y) is True + assert (x >= y) is False + assert (x < x) is False + assert (x <= x) is True + assert (y < x) is False + assert (y <= x) is False + assert (y > x) is True + assert (y >= x) is True + assert (x != y) is True + assert (x == arb(2)) is True + + xi = arb('2 +/- 1') + assert (xi < x) is False + assert (xi > x) is False + +def test_arb_comparison_scalars() -> None: + x = arb(2) + assert (x == 2) is True + assert (x == 2.0) is True + assert (x < 3) is True + assert (x <= 2) is True + assert (x > 1) is True + assert (x >= 2.0) is True + assert (3 > x) is True + assert (2 == x) is True + +def test_arb_comparison_invalid_type() -> None: + x = arb(2) + assert raises(lambda: x < "bad", TypeError) # type: ignore[operator] + assert raises(lambda: x <= "bad", TypeError) # type: ignore[operator] + assert raises(lambda: x > "bad", TypeError) # type: ignore[operator] + assert raises(lambda: x >= "bad", TypeError) # type: ignore[operator] + assert raises(lambda: "bad" < x, TypeError) # type: ignore[operator] + assert raises(lambda: "bad" <= x, TypeError) # type: ignore[operator] + assert raises(lambda: "bad" > x, TypeError) # type: ignore[operator] + assert raises(lambda: "bad" >= x, TypeError) # type: ignore[operator] + +def test_arb_pos_floor_ceil() -> None: + x = arb(2.25, 0.25) # [2.0, 2.5] + assert x in (+x) + assert (+x) in x + assert (arb(2.9).floor() == arb(2)) is True + assert (arb(2.1).ceil() == arb(3)) is True + +def test_arb_abs_bounds() -> None: + x = arb(-5, 2) # [-7, -3] + lower = x.abs_lower() + upper = x.abs_upper() + assert float(lower) <= 3.0 + assert float(upper) >= 7.0 + assert (lower <= upper) is True + +def test_arb_exact_conversions_and_string_options() -> None: + x = arb(3) + assert x.is_integer() is True + assert int(x.fmpz()) == 3 + assert x.fmpq() == 3 + + y = arb(1, 1) + assert raises(lambda: y.man_exp(), ValueError) + assert raises(lambda: y.fmpq(), ValueError) + assert raises(lambda: y.fmpz(), ValueError) + + assert raises(lambda: arb(float("inf")).man_exp(), ValueError) + assert raises(lambda: arb(float("nan")).fmpq(), ValueError) + + z = arb("1.5 +/- 0.25") + assert z.repr().startswith("arb(") + assert "+/-" in z.str(10) + assert "+/-" not in z.str(10, radius=False) + assert isinstance(z.str(10, more=True), str) + assert isinstance(z.str(10, condense=2), str) + + oldunicode = ctx.unicode + try: + ctx.unicode = True + assert "±" in z.str(10) + finally: + ctx.unicode = oldunicode + +def test_arb_mpf_mid_rad_and_interval_ops() -> None: + t_pos = arb(3)._mpf_ + t_neg = arb(-3)._mpf_ + t_inf = arb(float("inf"))._mpf_ + assert t_pos[0] == 0 + assert t_neg[0] == 1 + assert t_inf[3] == -1 + + mid, rad, exp = (arb(1) / 3).mid_rad_10exp(12) + assert int(rad) >= 0 + assert isinstance(int(mid), int) + assert isinstance(int(exp), int) + + a = arb(1, 1) # [0,2] + b = arb(2, 1) # [1,3] + c = a.intersection(b) + assert c.contains(arb(1.5)) + assert raises(lambda: arb(2).intersection(3), ValueError) + + nn = arb(-1, 2).nonnegative_part() + assert nn.contains(0) + assert nn.contains(1) + +def test_arb_arithmetic() -> None: + x = arb(2) + assert (x + 1) == arb(3) + assert (1 + x) == arb(3) + assert (x - 1) == arb(1) + assert (5 - x) == arb(3) + assert (x * 3) == arb(6) + assert (3 * x) == arb(6) + assert (x / 2) == arb(1) + assert (6 / x) == arb(3) + assert (x ** 2) == arb(4) + assert (2 ** x) == arb(4) + + assert raises(lambda: x + "bad", TypeError) # type: ignore[operator] + assert raises(lambda: "bad" + x, TypeError) # type: ignore[operator] + assert raises(lambda: x ** "bad", TypeError) # type: ignore[operator] + assert raises(lambda: "bad" ** x, TypeError) # type: ignore[operator] + +def test_arb_functions() -> None: + x = arb(0.5) + xpi = arb(0.25) + y = arb(2) + + with ctx.workprec(53): + assert is_close_arb(y.sqrt(), 1.4142135623730951) + assert is_close_arb(y.rsqrt(), 1.0 / math.sqrt(2.0)) + assert is_close_arb(x.expm1(), math.expm1(0.5)) + assert is_close_arb(y.log(), math.log(2.0)) + assert is_close_arb(x.log1p(), math.log1p(0.5)) + + assert is_close_arb(x.sin(), math.sin(0.5)) + assert is_close_arb(x.cos(), math.cos(0.5)) + s, c = x.sin_cos() + assert is_close_arb(s, math.sin(0.5)) + assert is_close_arb(c, math.cos(0.5)) + assert is_close_arb(xpi.sin_pi(), math.sin(math.pi * 0.25)) + assert is_close_arb(xpi.cos_pi(), math.cos(math.pi * 0.25)) + s, c = xpi.sin_cos_pi() + assert is_close_arb(s, math.sin(math.pi * 0.25)) + assert is_close_arb(c, math.cos(math.pi * 0.25)) + assert is_close_arb(x.tan(), math.tan(0.5)) + assert is_close_arb(x.cot(), 1.0 / math.tan(0.5)) + assert is_close_arb(xpi.tan_pi(), math.tan(math.pi * 0.25)) + assert is_close_arb(xpi.cot_pi(), 1.0 / math.tan(math.pi * 0.25)) + assert is_close_arb(x.sec(), 1.0 / math.cos(0.5)) + assert is_close_arb(x.csc(), 1.0 / math.sin(0.5)) + assert is_close_arb(x.sinc(), math.sin(0.5) / 0.5) + assert is_close_arb(x.sinc_pi(), math.sin(math.pi * 0.5) / (math.pi * 0.5)) + + assert is_close_arb(x.atan(), math.atan(0.5)) + assert is_close_arb(x.acos(), math.acos(0.5)) + assert is_close_arb(x.asin(), math.asin(0.5)) + assert is_close_arb(x.atanh(), math.atanh(0.5)) + assert is_close_arb(x.asinh(), math.asinh(0.5)) + assert is_close_arb(y.acosh(), math.acosh(2.0)) + assert is_close_arb(x.sinh(), math.sinh(0.5)) + assert is_close_arb(x.cosh(), math.cosh(0.5)) + s, c = x.sinh_cosh() + assert is_close_arb(s, math.sinh(0.5)) + assert is_close_arb(c, math.cosh(0.5)) + assert is_close_arb(x.tanh(), math.tanh(0.5)) + assert is_close_arb(x.coth(), 1.0 / math.tanh(0.5)) + assert is_close_arb(x.sech(), 1.0 / math.cosh(0.5)) + assert is_close_arb(x.csch(), 1.0 / math.sinh(0.5)) + +def test_arb_constants_and_special_values() -> None: + with ctx.workprec(53): + assert is_close_arb(arb.const_e(), math.e) + assert is_close_arb(arb.const_log2(), math.log(2.0)) + assert is_close_arb(arb.const_log10(), math.log(10.0)) + + posinf = arb.pos_inf() + neginf = arb.neg_inf() + nan = arb.nan() + + assert (posinf.is_finite()) is False + assert (neginf.is_finite()) is False + assert (nan.is_nan()) is True + assert float(posinf) == float("inf") + assert float(neginf) == float("-inf") +def test_arb_unique_fmpz_and_bits() -> None: + u1 = arb("5 +/- 0.1").unique_fmpz() + u2 = arb("5 +/- 0.9").unique_fmpz() + assert u1 is not None + assert u2 is not None + assert int(u1) == 5 + assert int(u2) == 5 + assert arb("5.1 +/- 0.9").unique_fmpz() is None + assert arb("2047/2048").bits() == 11 + assert arb.nan().bits() == 0 + +def test_arb_atan2_log_base_and_lambertw() -> None: + with ctx.workprec(53): + assert is_close_arb(arb.atan2(arb(3), arb(2)), math.atan2(3.0, 2.0)) + assert is_close_arb(arb(8).log_base(2), 3.0) + + w = arb(1).lambertw() + assert is_close_arb(w * w.exp(), 1.0, tol=1e-9, rel_tol=1e-9, max_width=1e-9) + + wneg = arb("-0.1").lambertw(-1) + assert (wneg < -1) is True + assert raises(lambda: arb(1).lambertw(2), ValueError) + +def test_arb_special_functions() -> None: + with ctx.workprec(53): + assert is_close_arb(arb(5).gamma(), 24.0) + assert is_close_arb(arb.gamma_fmpq(arb(1).fmpq() / 2), math.sqrt(math.pi)) + assert is_close_arb(arb(5).rgamma(), 1.0 / 24.0) + assert is_close_arb(arb(5).lgamma(), math.lgamma(5.0)) + assert is_close_arb(arb(1).digamma(), -0.5772156649015329, tol=1e-9, rel_tol=1e-9, max_width=1e-9) + + assert is_close_arb(arb(3).rising(3), 60.0) + assert is_close_arb(arb.rising_fmpq_ui(arb(1).fmpq() / 2, 2), 0.75) + u, v = arb(3).rising2(5) + assert is_close_arb(u, 2520.0) + assert is_close_arb(v, 2754.0) + + assert is_close_arb(arb(2).zeta(), math.pi**2 / 6) + assert is_close_arb(arb(2).zeta(3), "0.3949340668482264364724152") + assert is_close_arb(arb(2).agm(3), "2.474680436236304462606658") + + assert is_close_arb(arb.bernoulli(2), 1.0 / 6.0) + assert is_close_arb(arb.bell_number(6), 203.0) + assert is_close_arb(arb.partitions_p(5), 7.0) + assert is_close_arb(arb(arb(1) / 3).bernoulli_poly(3), "0.03703703703703703703703716") + + assert is_close_arb(arb(5).fac(), 120.0) + assert is_close_arb(arb.fac_ui(6), 720.0) + assert is_close_arb(arb(10).bin(3), 120.0) + assert is_close_arb(arb.bin_uiui(10, 3), 120.0) + assert is_close_arb(arb.fib(10), 55.0) + assert is_close_arb(arb(0.5).polylog(2), "0.5822405264650125059026562") + + assert is_close_arb(arb(1).airy_ai(), "0.1352924163128814155241473") + assert is_close_arb(arb(1).airy_ai(derivative=1), "-0.1591474412967932127875002") + assert raises(lambda: arb(1).airy_ai(derivative=2), ValueError) + + assert is_close_arb(arb(1).airy_bi(), "1.207423594952871259436378") + assert is_close_arb(arb(1).airy_bi(derivative=1), "0.9324359333927756329594508") + assert raises(lambda: arb(1).airy_bi(derivative=2), ValueError) + + ai, aip, bi, bip = arb(1).airy() + assert is_close_arb(ai, "0.1352924163128814155241473") + assert is_close_arb(aip, "-0.1591474412967932127875002") + assert is_close_arb(bi, "1.207423594952871259436378") + assert is_close_arb(bip, "0.9324359333927756329594508") + + assert is_close_arb(arb.airy_ai_zero(1), "-2.338107410459767038489195") + assert is_close_arb(arb.airy_ai_zero(1, derivative=1), "-1.018792971647471089017324") + assert raises(lambda: arb.airy_ai_zero(0), ValueError) + assert raises(lambda: arb.airy_ai_zero(1, derivative=2), ValueError) + + assert is_close_arb(arb.airy_bi_zero(1), "-1.173713222709127924919979") + assert is_close_arb(arb.airy_bi_zero(1, derivative=1), "-2.294439682614123246622457") + assert raises(lambda: arb.airy_bi_zero(0), ValueError) + assert raises(lambda: arb.airy_bi_zero(1, derivative=2), ValueError) + + x = arb(1) / 3 + assert is_close_arb(x.chebyshev_t(3), "-0.8518518518518518518518510") + assert is_close_arb(x.chebyshev_u(3), "-1.037037037037037037037036") + assert is_close_arb(x.jacobi_p(3, 0.25, 0.5), "-0.4722222222222222222222269") + assert is_close_arb(x.gegenbauer_c(3, 0.25), "-0.1736111111111111111111118") + assert is_close_arb(x.laguerre_l(3, 0.25), "0.4790702160493827160493819") + assert is_close_arb(x.hermite_h(3), "-3.703703703703703703703700") + assert is_close_arb(x.legendre_p(3), "-0.4074074074074074074074072") + assert is_close_arb(x.legendre_q(3), "0.2476922409970481777113051") + assert raises(lambda: x.legendre_p(3, type=1), ValueError) + assert raises(lambda: x.legendre_q(3, type=4), ValueError) + + root = arb.legendre_p_root(5, 2) + assert is_close_arb(root, 0.0) + root_w, w = arb.legendre_p_root(5, 2, weight=True) + assert is_close_arb(root_w, 0.0) + assert is_close_arb(w, "0.5688888888888888888888887") + assert raises(lambda: arb.legendre_p_root(5, 5), ValueError) + + x = arb(2) + assert is_close_arb(arb(0.5).erfcinv(), "0.4769362762044698733814183") + assert is_close_arb(x.erfi(), "18.56480241457555259870427") + assert is_close_arb(x.fresnel_s(), "0.3434156783636982421953005") + assert is_close_arb(x.fresnel_s(normalized=False), "0.8047764893437561102962750") + assert is_close_arb(x.fresnel_c(), "0.4882534060753407545002233") + assert is_close_arb(x.fresnel_c(normalized=False), "0.4614614624332163728664738") + assert is_close_arb(x.ei(), "4.954234356001890163379494") + assert is_close_arb(x.si(), "1.605412976802694848576720") + assert is_close_arb(x.ci(), "0.4229808287748649956985648") + assert is_close_arb(x.shi(), "2.501567433354975641473370") + assert is_close_arb(x.chi(), "2.452666922646914521906127") + assert is_close_arb(arb(10).li(), "6.165599504787297937522937") + assert is_close_arb(arb(10).li(offset=True), "5.120435724669805152678346") + + assert is_close_arb(x.bessel_j(1), "0.5767248077568733872024483") + assert is_close_arb(x.bessel_y(1), "-0.1070324315409375468883694") + assert is_close_arb(x.bessel_k(1), "0.1398658818165224272845938") + assert is_close_arb(x.bessel_k(1, scaled=True), "1.033476847068688573175318") + assert is_close_arb(x.bessel_i(1), "1.590636854637329063382253") + assert is_close_arb(x.bessel_i(1, scaled=True), "0.2152692892489376591585047") + + assert is_close_arb(x.gamma_upper(2.5), "0.7303608140431147358169869") + assert is_close_arb(x.gamma_upper(2.5, regularized=1), "0.5494159513527802326058330") + assert is_close_arb(x.gamma_lower(2.5), "0.5989795741360222846566369") + assert is_close_arb(x.gamma_lower(2.5, regularized=1), "0.4505840486472197673941662") + assert is_close_arb(x.gamma_lower(2.5, regularized=2), "0.07965275907323457828282656") + assert is_close_arb(arb("0.9").beta_lower(1, 2.5), "0.3987350889359326482671957") + assert is_close_arb(arb("0.9").beta_lower(1, 2.5, regularized=True), "0.9968377223398316206679875") + assert is_close_arb(x.expint(2), "0.03753426182049045275952322") + + z = arb(0.2) + assert is_close_arb(z.hypgeom([1, 2], [3]), "1.157177565710487798620111") + assert is_close_arb(z.hypgeom([1, 2], [3], regularized=True), "0.5785887828552438993100557") + assert is_close_arb(z.hypgeom_u(1, 2.5), "11.37885908205001566617169") + assert is_close_arb(z.hypgeom_1f1(1, 2.5), "1.084782224878003156149143") + assert is_close_arb(z.hypgeom_1f1(1, 2.5, regularized=True), "0.8160304422585721469158928") + assert is_close_arb(z.hypgeom_0f1(2.5), "1.082319886462734398651672") + assert is_close_arb(z.hypgeom_0f1(2.5, regularized=True), "0.8141781413451533169519156") + assert is_close_arb(z.hypgeom_2f1(1, 2, 3), "1.157177565710487798620111") + assert is_close_arb(z.hypgeom_2f1(1, 2, 3, regularized=True), "0.5785887828552438993100557") + assert is_close_arb(z.hypgeom_2f1(1, 2, 3, ab=True, ac=True, bc=True, abc=True), "1.157177565710487798620111") + + assert is_close_arb(arb.const_sqrt_pi(), math.sqrt(math.pi)) + assert is_close_arb(arb.const_euler(), 0.5772156649015329, tol=1e-9, rel_tol=1e-9, max_width=1e-9) + assert is_close_arb(arb.const_catalan(), 0.915965594177219, tol=1e-9, rel_tol=1e-9, max_width=1e-9) + assert is_close_arb(arb.const_khinchin(), 2.6854520010653062, tol=1e-9, rel_tol=1e-9, max_width=1e-9) + assert is_close_arb(arb.const_glaisher(), 1.2824271291006226, tol=1e-9, rel_tol=1e-9, max_width=1e-9) + + assert is_close_arb(arb(27).root(3), 3.0) + assert is_close_arb(arb.gram_point(0), "17.84559954041086081682633") + assert is_close_arb(arb(100).zeta_nzeros(), 29.0) + assert is_close_arb(arb(123).backlund_s(), "0.4757920863536796196115762") + F, G = arb(1).coulomb(0.5, 0.25) + assert is_close_arb(F, "0.4283180781043541845555929") + assert is_close_arb(G, "1.218454487206367973745601") + assert is_close_arb(arb(1).coulomb_f(0.5, 0.25), "0.4283180781043541845555929") + assert is_close_arb(arb(1).coulomb_g(0.5, 0.25), "1.218454487206367973745601") + +def test_arb_internal_branches() -> None: + import flint + assert is_close_arb(arb(flint.arf(1.25)), 1.25) + assert is_close_arb(arb(flint.fmpz(7)), 7.0) + assert is_close_arb(arb(flint.fmpq(3, 4)), 0.75) + assert is_close_arb(arb(1, (1, -2)).rad(), 0.25) + assert (arb(0).is_zero()) is True + + class DummyMpf: + def __init__(self, mpf: tuple[int, int, int, int]) -> None: + self._mpf_ = mpf + + assert is_close_arb(arb(DummyMpf((0, 0, 0, 0))), 0.0) + assert (arb(DummyMpf((0, 0, 1, 0))).is_nan()) is True + assert is_close_arb(arb(DummyMpf((0, 3, -1, 2))), 1.5) + assert is_close_arb(arb(DummyMpf((1, 3, -1, 2))), -1.5) + + assert raises(lambda: arb(1).contains(object()), TypeError) # type: ignore[arg-type] + p = flint.fmpz_poly([1, 2]) # 1 + 2*x + assert is_close_arb(p(arb(3)), 7.0) + assert is_close_arb(p(1.5), 4.0) + assert raises(lambda: p(object()), TypeError) # type: ignore + + assert arb(2).repr() == "arb((0x1, 0x1))" + + x = arb(2) + bad = object() + assert raises(lambda: x - bad, TypeError) # type: ignore[operator] + assert raises(lambda: bad - x, TypeError) # type: ignore[operator] + assert raises(lambda: x * bad, TypeError) # type: ignore[operator] + assert raises(lambda: bad * x, TypeError) # type: ignore[operator] + assert raises(lambda: x / bad, TypeError) # type: ignore[operator] + assert raises(lambda: bad / x, TypeError) # type: ignore[operator] + assert raises(lambda: x ** bad, TypeError) # type: ignore[operator] + assert raises(lambda: bad ** x, TypeError) # type: ignore[operator] + assert raises(lambda: pow(x, 2, 3), TypeError) # type: ignore[misc] + assert raises(lambda: pow(2, x, 3), TypeError) # type: ignore[misc] + assert x.neg().is_finite() is True + + q = flint.fmpq(3, 4) + assert is_close_arb(arb.sin_pi_fmpq(q), math.sin(math.pi * 0.75)) + assert is_close_arb(arb.cos_pi_fmpq(q), math.cos(math.pi * 0.75)) + s, c = arb.sin_cos_pi_fmpq(q) + assert is_close_arb(s, math.sin(math.pi * 0.75)) + assert is_close_arb(c, math.cos(math.pi * 0.75)) + + assert isinstance(int(arb(1).rel_accuracy_bits()), int) + assert isinstance(int(arb(1).rel_one_accuracy_bits()), int) + assert isinstance(arb(1).mid_rad_10exp()[0], flint.fmpz) # Tests for arithmetic functions in `flint.arb`. @@ -152,7 +578,7 @@ def arb_pi(prec): # These tests assume arb.__contains__ is correct. -def test_arb_sub(): +def test_arb_sub() -> None: """`arb.__sub__` works as expected.""" arb1 = arb(2, 0.5) arb2 = arb(1, 1) @@ -163,7 +589,7 @@ def test_arb_sub(): true_interval = arb(1, 1.5) # [-0.5, 2.5] assert true_interval in actual -def test_arb_add(): +def test_arb_add() -> None: """`arb.__add__` works as expected.""" arb1 = arb(2, 1) arb2 = arb(1, 1) @@ -172,7 +598,7 @@ def test_arb_add(): true_interval = arb(3, 2) # [1, 5] assert true_interval in actual -def test_arb_mul(): +def test_arb_mul() -> None: """`arb.__mul__` works as expected.""" arb1 = arb(2, 1) arb2 = arb(1, 1) @@ -181,7 +607,7 @@ def test_arb_mul(): true_interval = arb(3, 3) # [0, 6] assert true_interval in actual -def test_arb_div(): +def test_arb_div() -> None: """`arb.__div__` works as expected.""" arb1 = arb(4, 1) arb2 = arb(2, 1) @@ -190,7 +616,7 @@ def test_arb_div(): true_interval = arb(4, 1) # [3, 5] assert true_interval in actual -def test_arb_log(): +def test_arb_log() -> None: """`arb.log` works as expected.""" midpoint = (1 + math.exp(10)) / 2 arb_val = arb(midpoint, midpoint - 1) # [1, exp(10)] @@ -199,7 +625,7 @@ def test_arb_log(): true_interval = arb(5, 5) # [0,10] assert true_interval in actual -def test_arb_exp(): +def test_arb_exp() -> None: """`arb.exp` works as expected.""" midpoint = math.log(9) / 2 arb_val = arb(midpoint, midpoint) # [0, log(9)] @@ -208,7 +634,7 @@ def test_arb_exp(): true_interval = arb(5, 4) # [1,9] assert true_interval in actual -def test_arb_max(): +def test_arb_max() -> None: """`arb.max` works as expected.""" arb1 = arb(1.5, 0.5) # [1, 2] arb2 = arb(1, 2) # [-1, 3] @@ -217,7 +643,7 @@ def test_arb_max(): true_interval = arb(2, 1) # [1, 3] assert true_interval in actual -def test_arb_min(): +def test_arb_min() -> None: """`arb.min` works as expected.""" arb1 = arb(1.5, 0.5) # [1, 2] arb2 = arb(1, 2) # [-1, 3] @@ -226,28 +652,28 @@ def test_arb_min(): true_interval = arb(0.5, 1.5) # [-1, 2] assert true_interval in actual -def test_arb_abs(): +def test_arb_abs() -> None: """`arb.__abs__` works as expected.""" arb_val = arb(1, 2) # [-1,3] actual = abs(arb_val) true_interval = arb(1.5, 1.5) assert true_interval in actual -def test_arb_neg(): +def test_arb_neg() -> None: """`arb.neg` works as expected.""" arb_val = arb(1, 2) # [-1,3] actual = arb_val.neg(exact=True) true_interval = arb(-2, 1) # [-3,1] assert true_interval in actual -def test_arb_neg_dunder(): +def test_arb_neg_dunder() -> None: """`arb.__neg__` works as expected.""" arb_val = arb(1, 2) # [-1,3] actual = -arb_val true_interval = arb(-2, 1) # [-3,1] assert true_interval in actual -def test_arb_sgn(): +def test_arb_sgn() -> None: """`arb.sgn` works as expected.""" arb1 = arb(1, 0.5) # [0.5,1.5] arb2 = arb(-1, 0.5) # [-1.5,-0.5] @@ -259,7 +685,7 @@ def test_arb_sgn(): assert_almost_equal(float(arb3.sgn().mid()), 0) assert_almost_equal(float(arb3.sgn().rad()), 1) -def test_arb_erfinv(): +def test_arb_erfinv() -> None: """`arb.erfinv` works as expected.""" midpoint = (math.erf(1 / 8) + math.erf(1 / 16)) / 2 radius = midpoint - math.erf(1 / 16) @@ -269,7 +695,7 @@ def test_arb_erfinv(): true_interval = arb(3 / 32, 1 / 32) # [1/16, 1/8] assert true_interval in actual -def test_arb_erf(): +def test_arb_erf() -> None: """`arb.erf` works as expected.""" arb_val = arb(2, 1) with ctx.workprec(100): @@ -280,7 +706,7 @@ def test_arb_erf(): ) assert true_interval in actual -def test_arb_erfc(): +def test_arb_erfc() -> None: """`arb.erfc` works as expected.""" arb_val = arb(2, 1) with ctx.workprec(100): @@ -291,14 +717,14 @@ def test_arb_erfc(): ) assert true_interval in actual -def test_arb_const_pi(): +def test_arb_const_pi() -> None: """`arb.pi` works as expected.""" with ctx.workprec(100): actual = arb.pi() interval_around_pi = arb(math.pi, 1e-10) assert actual in interval_around_pi -def test_arb_union(): +def test_arb_union() -> None: """`arb.union` works as expected.""" arb1 = arb(1, 0.5) # [0.5,1.5] arb2 = arb(3, 0.5) # [2.5,3.5] @@ -307,7 +733,7 @@ def test_arb_union(): true_interval = arb(2, 1.5) # [0.5, 3.5] assert true_interval in actual -def test_arb_sum(): +def test_arb_sum() -> None: """`arb.__sum__` works as expected.""" arb1 = arb(1, 0.5) # [0.5,1.5] arb2 = arb(2, 0.5) # [1.5,2.5] @@ -317,7 +743,7 @@ def test_arb_sum(): true_interval = arb(6, 1.5) # [4.5, 7.5] assert true_interval in actual -def test_no_tests_missing(): +def test_no_tests_missing() -> None: """Make sure all arb tests are included in all_tests.""" test_funcs = {f for name, f in globals().items() if name.startswith("test_")} untested = test_funcs - set(all_tests) @@ -337,6 +763,22 @@ def test_no_tests_missing(): test_upper, test_contains, test_hash, + test_arb_constructor, + test_arb_contains, + test_arb_comparison, + test_arb_comparison_scalars, + test_arb_comparison_invalid_type, + test_arb_pos_floor_ceil, + test_arb_abs_bounds, + test_arb_exact_conversions_and_string_options, + test_arb_mpf_mid_rad_and_interval_ops, + test_arb_arithmetic, + test_arb_functions, + test_arb_constants_and_special_values, + test_arb_unique_fmpz_and_bits, + test_arb_atan2_log_base_and_lambertw, + test_arb_special_functions, + test_arb_internal_branches, test_arb_sub, test_arb_add, test_arb_mul, @@ -355,4 +797,4 @@ def test_no_tests_missing(): test_arb_const_pi, test_arb_union, test_arb_sum, -] \ No newline at end of file +] diff --git a/src/flint/test/test_arb_mat.py b/src/flint/test/test_arb_mat.py new file mode 100644 index 00000000..553dee7a --- /dev/null +++ b/src/flint/test/test_arb_mat.py @@ -0,0 +1,256 @@ +from __future__ import annotations + +from flint import acb_mat, arb, arb_mat, ctx, fmpq_mat, fmpz_mat +from flint.test.helpers import is_close_arb, is_close_arb_mat as is_close, raises + + +class _DummyMatrix: + rows = 2 + cols = 2 + + def __getitem__(self, index: tuple[int, int]) -> float: + i, j = index + return float(i + 2 * j + 1) + + +def test_arb_mat_constructor() -> None: + z = fmpz_mat([[1, 2], [3, 4]]) + q = fmpq_mat([[1, 2], [3, 4]]) + a = arb_mat([[1, 2], [3, 4]]) + b = arb_mat(a) + assert is_close(b, [[1, 2], [3, 4]]) + assert is_close(arb_mat(z), [[1, 2], [3, 4]]) + assert is_close(arb_mat(q), [[1, 2], [3, 4]]) + assert is_close(arb_mat(_DummyMatrix()), [[1, 3], [2, 4]]) + assert isinstance(arb_mat.convert(z), arb_mat) + assert isinstance(arb_mat.convert(q), arb_mat) + assert raises(lambda: arb_mat.convert(object()), TypeError) # type: ignore[arg-type] + + assert is_close(arb_mat(2, 2), [[0, 0], [0, 0]]) + assert is_close(arb_mat(2, 2, [1, 2, 3, 4]), [[1, 2], [3, 4]]) + assert is_close(arb_mat(3, 3, 5), [[5, 0, 0], [0, 5, 0], [0, 0, 5]]) + + assert raises(lambda: arb_mat([1, 2]), TypeError) # type: ignore[arg-type,list-item] + assert raises(lambda: arb_mat([[1], [2, 3]]), ValueError) + assert raises(lambda: arb_mat(object()), TypeError) # type: ignore[call-overload] + assert raises(lambda: arb_mat(2, 2, [1, 2, 3]), ValueError) + assert raises(lambda: arb_mat(1, 2, 3, 4), ValueError) # type: ignore[call-overload] + + +def test_arb_mat_basics_and_indexing() -> None: + a = arb_mat([[1, 2], [3, 4]]) + assert a.nrows() == 2 + assert a.ncols() == 2 + assert is_close_arb(a[0, 1], 2) + a[0, 1] = 1.5 + assert is_close_arb(a[0, 1], 1.5) + assert raises(lambda: a[2, 0], ValueError) # type: ignore[index] + assert raises(lambda: a[0, 2], ValueError) # type: ignore[index] + def set_oob_1() -> None: + a[2, 0] = 1 + def set_oob_2() -> None: + a[0, 2] = 1 + def set_bad() -> None: + a[0, 0] = object() # type: ignore[assignment] + assert raises(set_oob_1, ValueError) + assert raises(set_oob_2, ValueError) + assert raises(set_bad, TypeError) + + assert is_close(a.transpose(), [[1, 3], [1.5, 4]]) + assert is_close(+a, a) + assert is_close(-a, [[-1, -1.5], [-3, -4]]) + assert raises(lambda: bool(a), NotImplementedError) + + oldpretty = ctx.pretty + try: + ctx.pretty = False + assert "arb_mat(" in repr(a) + finally: + ctx.pretty = oldpretty + assert "[" in str(a) + + +def test_arb_mat_add_sub() -> None: + a = arb_mat([[1, 2], [3, 4]]) + b = arb_mat([[4, 5], [6, 7]]) + z = fmpz_mat([[4, 5], [6, 7]]) + q = fmpq_mat([[4, 5], [6, 7]]) + + assert is_close(a + b, [[5, 7], [9, 11]]) + assert is_close(a + z, [[5, 7], [9, 11]]) + assert is_close(a + q, [[5, 7], [9, 11]]) + assert is_close(z + a, [[5, 7], [9, 11]]) + assert is_close(q + a, [[5, 7], [9, 11]]) + + assert is_close(a - b, [[-3, -3], [-3, -3]]) + assert is_close(a - z, [[-3, -3], [-3, -3]]) + assert is_close(z - a, [[3, 3], [3, 3]]) + + assert is_close(a + 2, [[3, 2], [3, 6]]) + assert is_close(2 + a, [[3, 2], [3, 6]]) + assert is_close(a - 2, [[-1, 2], [3, 2]]) + assert is_close(2 - a, [[1, -2], [-3, -2]]) + + c = a + 1j + d = 1j + a + e = a - 1j + f = 1j - a + assert isinstance(c, acb_mat) + assert isinstance(d, acb_mat) + assert isinstance(e, acb_mat) + assert isinstance(f, acb_mat) + + assert raises(lambda: a + arb_mat([[1, 2, 3]]), ValueError) + assert raises(lambda: a - arb_mat([[1, 2, 3]]), ValueError) + assert raises(lambda: a + object(), TypeError) # type: ignore[operator] + assert raises(lambda: object() + a, TypeError) # type: ignore[operator] + assert raises(lambda: a - object(), TypeError) # type: ignore[operator] + assert raises(lambda: object() - a, TypeError) # type: ignore[operator] + + +def test_arb_mat_mul_div() -> None: + a = arb_mat([[1, 2], [3, 4]]) + b = arb_mat([[4, 5], [6, 7]]) + z = fmpz_mat([[4, 5], [6, 7]]) + q = fmpq_mat([[4, 5], [6, 7]]) + + assert is_close(a * b, [[16, 19], [36, 43]]) + assert is_close(a * z, [[16, 19], [36, 43]]) + assert is_close(a * q, [[16, 19], [36, 43]]) + assert is_close(z * a, [[19, 28], [27, 40]]) + assert is_close(q * a, [[19, 28], [27, 40]]) + + assert is_close(a * 2, [[2, 4], [6, 8]]) + assert is_close(2 * a, [[2, 4], [6, 8]]) + assert is_close(a * 0.5, [[0.5, 1], [1.5, 2]]) + assert is_close(a / 2, [[0.5, 1], [1.5, 2]]) + + c = a * (1 + 2j) + d = (1 + 2j) * a + assert isinstance(c, acb_mat) + assert isinstance(d, acb_mat) + + assert raises(lambda: a * arb_mat([[1, 2, 3]]), ValueError) + assert raises(lambda: a * object(), TypeError) # type: ignore[operator] + assert raises(lambda: object() * a, TypeError) # type: ignore[operator] + nan_mat = a / 0 + assert nan_mat[0, 0].is_nan() is True + assert nan_mat[1, 1].is_nan() is True + assert raises(lambda: a / object(), TypeError) # type: ignore[operator] + + +def test_arb_mat_pow_inv_solve() -> None: + a = arb_mat([[1, 2], [3, 4]]) + assert is_close(a**2, [[7, 10], [15, 22]]) + assert raises(lambda: pow(a, 2, 3), NotImplementedError) # type: ignore[misc] + assert raises(lambda: arb_mat([[1, 2, 3]])**2, ValueError) + + ai = a.inv() + eye = a * ai + assert is_close(eye, [[1, 0], [0, 1]], tol=1e-8, rel_tol=1e-8, max_width=1e-8) + assert raises(lambda: arb_mat([[1, 2], [2, 4]]).inv(), ZeroDivisionError) + assert raises(lambda: arb_mat([[1, 2, 3]]).inv(), ValueError) + inv_ns = arb_mat([[1, 2], [2, 4]]).inv(nonstop=True) + assert inv_ns[0, 0].is_nan() is True + assert inv_ns[1, 1].is_nan() is True + + x = arb_mat([[1], [2]]) + b = a * x + assert is_close(a.solve(b), x, tol=1e-8, rel_tol=1e-8, max_width=1e-8) + assert is_close(a.solve(b, algorithm="lu"), x, tol=1e-8, rel_tol=1e-8, max_width=1e-8) + assert is_close(a.solve(b, algorithm="precond"), x, tol=1e-8, rel_tol=1e-8, max_width=1e-8) + assert is_close(a.solve(b, algorithm="approx"), x, tol=1e-8, rel_tol=1e-8, max_width=1e-8) + assert raises(lambda: a.solve(b, algorithm="bad"), ValueError) # type: ignore[arg-type] + assert raises(lambda: arb_mat([[1, 2], [2, 4]]).solve(b), ZeroDivisionError) + solve_ns = arb_mat([[1, 2], [2, 4]]).solve(b, nonstop=True) + assert solve_ns[0, 0].is_nan() is True + assert solve_ns[1, 0].is_nan() is True + assert raises(lambda: arb_mat([[1, 2, 3]]).solve(arb_mat([[1], [2], [3]])), ValueError) + assert raises(lambda: a.solve([[1], [2]]), TypeError) # type: ignore[arg-type] + + +def test_arb_mat_special_methods() -> None: + a = arb_mat([[1, 2], [3, 4]]) + assert is_close_arb(a.det(), -2) + assert is_close_arb(a.trace(), 5) + assert is_close(a.mid(), a) + assert is_close( + a.exp(), + [[51.9689561987050, 74.7365645670032], [112.104846850505, 164.073803049210]], + tol=1e-7, + rel_tol=1e-7, + max_width=1e-7, + ) + assert raises(lambda: arb_mat([[1, 2, 3]]).det(), ValueError) + assert raises(lambda: arb_mat([[1, 2, 3]]).trace(), ValueError) + assert raises(lambda: arb_mat([[1, 2, 3]]).exp(), ValueError) + + p = a.charpoly() + assert is_close_arb(p[0], -2) + assert is_close_arb(p[1], -5) + assert is_close_arb(p[2], 1) + assert raises(lambda: arb_mat([[1, 2, 3]]).charpoly(), ValueError) + + h = arb_mat.hilbert(2, 2) + assert is_close(h, [[1, 1/2], [1/2, 1/3]], tol=1e-12, rel_tol=1e-12, max_width=1e-12) + + ps = arb_mat.pascal(3, 4) + assert is_close(ps, [[1, 1, 1, 1], [1, 2, 3, 4], [1, 3, 6, 10]]) + pu = arb_mat.pascal(3, 4, 1) + assert is_close(pu, [[1, 1, 1, 1], [0, 1, 2, 3], [0, 0, 1, 3]]) + pl = arb_mat.pascal(3, 4, -1) + assert is_close(pl, [[1, 0, 0, 0], [1, 1, 0, 0], [1, 2, 1, 0]]) + + st0 = arb_mat.stirling(4, 3, 0) + assert is_close(st0, [[1, 0, 0], [0, 1, 0], [0, 1, 1], [0, 2, 3]]) + st1 = arb_mat.stirling(4, 3, 1) + assert is_close(st1, [[1, 0, 0], [0, 1, 0], [0, -1, 1], [0, 2, -3]]) + st2 = arb_mat.stirling(4, 3, 2) + assert is_close(st2, [[1, 0, 0], [0, 1, 0], [0, 1, 1], [0, 1, 3]]) + assert raises(lambda: arb_mat.stirling(2, 2, 5), ValueError) + + d = arb_mat.dct(2) + assert d.nrows() == 2 + assert d.ncols() == 2 + d2 = arb_mat.dct(2, 3) + assert d2.nrows() == 2 + assert d2.ncols() == 3 + + +def test_arb_mat_contains_overlap_chop_cmp_eig() -> None: + a = arb_mat([[1, 2], [3, 4]]) + b = (a / 3) * 3 + assert b.contains(a) is True + assert a.contains(b) is False + assert b.contains(fmpz_mat([[1, 2], [3, 4]])) is True + assert (a / 3).contains(fmpq_mat([[1, 2], [3, 4]]) / 3) is True + assert raises(lambda: a.contains(object()), TypeError) # type: ignore[arg-type] + + assert b.overlaps(a) is True + assert (a + 100).overlaps(a) is False + + c = arb_mat([[1e-20, 2], [3, -1e-20]]) + chopped = c.chop(1e-10) + assert is_close(chopped, [[0, 2], [3, 0]]) + + assert (a == arb_mat([[1, 2], [3, 4]])) is True + assert (a != arb_mat([[1, 2], [3, 4]])) is False + assert (a == fmpz_mat([[1, 2], [3, 4]])) is True + assert (a != fmpz_mat([[1, 2], [3, 5]])) is True + assert raises(lambda: a < arb_mat([[1, 2], [3, 4]]), ValueError) # type: ignore[operator] + assert (a == object()) is False + assert (a != object()) is True + + eigvals = arb_mat([[1, 0], [0, 2]]).eig() + assert len(eigvals) == 2 + assert any(v.real.contains(1) for v in eigvals) + assert any(v.real.contains(2) for v in eigvals) + + +def test_is_close_arb_mat() -> None: + x = arb_mat([[1, 2], [3, 4]]) + assert is_close(x, [[1, 2], [3, 4]]) is True + assert is_close(x, arb_mat([[1, 2], [3, 4]])) is True + assert is_close(x, [[1, 2, 3]]) is False + assert is_close(x, [[1, 2], [3, 5]]) is False + assert is_close(object(), [[1]]) is False # type: ignore[arg-type] diff --git a/src/flint/test/test_arb_poly.py b/src/flint/test/test_arb_poly.py new file mode 100644 index 00000000..20498a55 --- /dev/null +++ b/src/flint/test/test_arb_poly.py @@ -0,0 +1,254 @@ +"""Tests for python-flint's `arb_poly` type.""" + +from __future__ import annotations + +from flint import acb, acb_poly, arb, arb_poly, fmpq, fmpq_poly, fmpz, fmpz_poly +from flint.test.helpers import is_close_acb, is_close_arb, is_close_arb_poly as is_close, raises + + +def test_arb_poly_constructor_and_basic() -> None: + x = arb_poly([0, 1]) + p0 = arb_poly() + assert len(p0) == 0 + assert p0.length() == 0 + assert p0.degree() == -1 + + p = arb_poly([1, 2, 3]) + assert len(p) == 3 + assert p.length() == 3 + assert p.degree() == 2 + assert is_close(p, 3*x**2 + 2*x + 1) + assert is_close_arb(p[-1], 0) + assert p.repr() == "arb_poly([1.00000000000000, 2.00000000000000, 3.00000000000000])" + + q = arb_poly(p) + assert q is not p + assert is_close(q, p) + + assert is_close(arb_poly(5), arb_poly([5])) + assert is_close(arb_poly(fmpz_poly([1, 2])), 2*x + 1) + assert is_close(arb_poly(fmpq_poly([1, fmpq(1, 2)])), 0.5*x + 1) + + mixed = arb_poly([1, 2.5, arb(3), fmpz(4), fmpq(5, 2)]) + assert is_close(mixed, 2.5*x**4 + 4*x**3 + 3*x**2 + 2.5*x + 1) + + assert raises(lambda: arb_poly([1, object()]), TypeError) # type: ignore[list-item] + + +def test_arb_poly_setitem() -> None: + p = arb_poly([1]) + p[3] = 5 + assert p.degree() == 3 + assert is_close_arb(p[3], 5) + p[2] = arb(1.25) + assert is_close_arb(p[2], 1.25) + assert raises(lambda: p.__setitem__(-1, 1), ValueError) + + +def test_arb_poly_from_roots_and_complex_roots() -> None: + x = arb_poly([0, 1]) + p = arb_poly.from_roots([0, 1, 2]) + assert is_close(p, x*(x - 1)*(x - 2)) + assert is_close_arb(p(0), 0) + assert is_close_arb(p(1), 0) + assert is_close_arb(p(2), 0) + + one = arb_poly.from_roots([]) + assert one.degree() == 0 + assert is_close_arb(one[0], 1) + + roots = arb_poly([1, 0, 1]).complex_roots() + assert len(roots) == 2 + assert any(is_close_acb(r, acb(0, 1), tol=1e-5, max_width=1e-5) for r in roots) + assert any(is_close_acb(r, acb(0, -1), tol=1e-5, max_width=1e-5) for r in roots) + + +def test_arb_poly_evaluate() -> None: + p = arb_poly([1, 2, 3]) + ys_fast = p.evaluate([0, 1, 2], algorithm="fast") + ys_iter = p.evaluate([0, 1, 2], algorithm="iter") + + assert len(ys_fast) == 3 + assert is_close_arb(ys_fast[0], 1) + assert is_close_arb(ys_fast[1], 6) + assert is_close_arb(ys_fast[2], 17) + + assert len(ys_iter) == 3 + assert is_close_arb(ys_iter[0], 1) + assert is_close_arb(ys_iter[1], 6) + assert is_close_arb(ys_iter[2], 17) + + assert p.evaluate([], algorithm="fast") == [] + assert raises(lambda: p.evaluate([1], algorithm="bad"), AssertionError) + + +def test_arb_poly_interpolate() -> None: + x = arb_poly([0, 1]) + xs = [0, 1, 2] + ys = [1, 6, 17] + + p_fast = arb_poly.interpolate(xs, ys, algorithm="fast") + p_newton = arb_poly.interpolate(xs, ys, algorithm="newton") + p_bary = arb_poly.interpolate(xs, ys, algorithm="barycentric") + + assert is_close(p_fast, 3*x**2 + 2*x + 1) + assert is_close(p_newton, 3*x**2 + 2*x + 1) + assert is_close(p_bary, 3*x**2 + 2*x + 1) + + assert raises(lambda: arb_poly.interpolate([0], [1, 2]), ValueError) + assert raises(lambda: arb_poly.interpolate([0], [1], algorithm="bad"), AssertionError) + + +def test_arb_poly_calculus_and_unary() -> None: + x = arb_poly([0, 1]) + p = 3*x**2 + 2*x + 1 + assert is_close(p.derivative(), 6*x + 2) + assert is_close(p.integral(), x**3 + x**2 + x) + + assert (+p) is p + assert is_close(-p, -(3*x**2 + 2*x + 1)) + + +def test_arb_poly_arithmetic_real_coercions() -> None: + x = arb_poly([0, 1]) + p = 3*x**2 + 2*x + 1 + + assert is_close(p + 2, 3*x**2 + 2*x + 3) + assert is_close(2 + p, 3*x**2 + 2*x + 3) + assert is_close(p - 2, 3*x**2 + 2*x - 1) + assert is_close(2 - p, -3*x**2 - 2*x + 1) + assert is_close(p*2, 6*x**2 + 4*x + 2) + assert is_close(2*p, 6*x**2 + 4*x + 2) + + assert is_close(p + fmpz(2), 3*x**2 + 2*x + 3) + assert is_close(p + fmpq(1, 2), 3*x**2 + 2*x + 1.5) + assert is_close(p + arb(1.25), 3*x**2 + 2*x + 2.25) + assert is_close(p + fmpz_poly([1, 1]), 3*x**2 + 3*x + 2) + assert is_close(p + fmpq_poly([1, fmpq(1, 2)]), 3*x**2 + 2.5*x + 2) + assert is_close(p // 2, 1.5*x**2 + x + 0.5) + assert is_close(p % 2, 0) + q, r = divmod(p, 2) + assert is_close(q, 1.5*x**2 + x + 0.5) + assert is_close(r, 0) + + +def test_arb_poly_arithmetic_divmod_and_errors() -> None: + x = arb_poly([0, 1]) + p = 3*x**2 + 2*x + 1 + d = x + 1 + + q = p // d + r = p % d + q2, r2 = divmod(p, d) + assert is_close(q, 3*x - 1) + assert is_close(r, 2) + assert is_close(q2, 3*x - 1) + assert is_close(r2, 2) + + assert is_close(3 // d, 0) + assert is_close(3 % d, 3) + rq, rr = divmod(3, d) + assert is_close(rq, 0) + assert is_close(rr, 3) + + z = arb_poly([0]) + assert raises(lambda: p // z, ZeroDivisionError) + assert raises(lambda: p % z, ZeroDivisionError) + assert raises(lambda: divmod(p, z), ZeroDivisionError) + + +def test_arb_poly_arithmetic_notimplemented_paths() -> None: + p = arb_poly([1, 2, 3]) + + assert raises(lambda: p + object(), TypeError) # type: ignore[operator] + assert raises(lambda: p - object(), TypeError) # type: ignore[operator] + assert raises(lambda: p * object(), TypeError) # type: ignore[operator] + assert raises(lambda: p // object(), TypeError) # type: ignore[operator] + assert raises(lambda: p % object(), TypeError) # type: ignore[operator] + assert raises(lambda: divmod(p, object()), TypeError) # type: ignore[operator] + + assert raises(lambda: object() + p, TypeError) # type: ignore[operator] + assert raises(lambda: object() - p, TypeError) # type: ignore[operator] + assert raises(lambda: object() * p, TypeError) # type: ignore[operator] + assert raises(lambda: object() // p, TypeError) # type: ignore[operator] + assert raises(lambda: object() % p, TypeError) # type: ignore[operator] + assert raises(lambda: divmod(object(), p), TypeError) # type: ignore[operator] + + +def test_arb_poly_complex_coercion_branch() -> None: + p = arb_poly([1, 2, 3]) + z = 2 + 3j + a = acb(2, 3) + + r1 = p + z + r2 = z + p + r3 = p - a + r4 = a - p + r5 = p * z + r6 = z * p + + assert isinstance(r1, acb_poly) + assert isinstance(r2, acb_poly) + assert isinstance(r3, acb_poly) + assert isinstance(r4, acb_poly) + assert isinstance(r5, acb_poly) + assert isinstance(r6, acb_poly) + + +def test_arb_poly_truncate_shifts_pow() -> None: + x = arb_poly([0, 1]) + p = 3*x**2 + 2*x + 1 + + assert p.truncate(0).length() == 0 + assert is_close(p.truncate(2), 2*x + 1) + assert is_close(p.truncate(5), p) + + assert is_close(p.left_shift(0), p) + assert is_close(p.left_shift(2), x**2*p) + + assert is_close(p.right_shift(0), p) + assert is_close(p.right_shift(2), 3) + assert p.right_shift(6).length() == 0 + + assert raises(lambda: p.left_shift(-1), ValueError) + assert raises(lambda: p.right_shift(-1), ValueError) + + p3 = p**3 + assert is_close(p3, p*p*p) + assert raises(lambda: pow(p, 2, 3), NotImplementedError) # type: ignore[misc] + + +def test_arb_poly_call_paths() -> None: + p = arb_poly([1, 2, 3]) + + cp = p(arb_poly([1, 1])) + x = arb_poly([0, 1]) + assert is_close(cp, 3*x**2 + 8*x + 6) + + assert is_close_arb(p(arb(2)), 17) + assert is_close_arb(p(2), 17) + assert is_close_arb(p(2.0), 17) + assert is_close_arb(p(fmpz(2)), 17) + assert is_close_arb(p(fmpq(1, 2)), 2.75) + + fp = p(fmpz_poly([1, 1])) + fqp = p(fmpq_poly([1, 1])) + assert is_close(fp, 3*x**2 + 8*x + 6) + assert is_close(fqp, 3*x**2 + 8*x + 6) + + ac = p(acb(2, 3)) + cz = p(2 + 3j) + assert is_close_acb(ac, acb(-10, 42)) + assert is_close_acb(cz, acb(-10, 42)) + + assert raises(lambda: p(object()), TypeError) # type: ignore[call-overload] + + +def test_arb_poly_unique_fmpz_poly() -> None: + p = arb_poly([1, 2, 3]) + q = arb_poly([1.1, 2, 3]) + + up = p.unique_fmpz_poly() + assert up is not None + assert up == fmpz_poly([1, 2, 3]) + assert q.unique_fmpz_poly() is None diff --git a/src/flint/test/test_arb_series.py b/src/flint/test/test_arb_series.py new file mode 100644 index 00000000..16d15d42 --- /dev/null +++ b/src/flint/test/test_arb_series.py @@ -0,0 +1,279 @@ +"""Tests for python-flint's `arb_series` type.""" + +from __future__ import annotations + +from flint import acb_series, arb, arb_poly, arb_series, ctx, fmpq, fmpq_poly, fmpq_series, fmpz, fmpz_poly, fmpz_series +from flint.test.helpers import is_close_arb, is_close_arb_series as is_close, raises + + +def test_arb_series_constructor_and_basic() -> None: + old_cap = ctx.cap + try: + ctx.cap = 8 + p0 = arb_series() + assert len(p0) == 0 + assert p0.length() == 0 + assert p0.prec == 8 + + p = arb_series([1, 2], prec=3) + assert len(p) == 2 + assert p.length() == 2 + assert p.prec == 3 + assert p.repr() == "arb_series([1.00000000000000, 2.00000000000000], prec=3)" + assert str(p) == "1.00000000000000 + 2.00000000000000*x + O(x^3)" + + q = arb_series(p) + assert q is not p + assert is_close(q, p) + assert q.prec == p.prec + + assert is_close(arb_series(fmpz_series([1, 2], prec=4)), arb_series([1, 2], prec=4)) + assert is_close(arb_series(fmpz_poly([1, 2])), arb_series([1, 2], prec=8)) + assert is_close(arb_series(arb_poly([1, 2])), arb_series([1, 2], prec=8)) + assert is_close(arb_series(5), arb_series([5], prec=8)) + assert is_close(arb_series(fmpq(1, 2)), arb_series([0.5], prec=8)) + + assert str(arb_series([1], prec=0)) == "O(x^0)" + assert str(arb_series([1], prec=-1)) == "(invalid power series)" + + x = arb_series([1], prec=4) + x[3] = 5 + assert is_close_arb(x[3], 5) + assert is_close_arb(x[-1], 0) + assert raises(lambda: x.__setitem__(-1, 1), ValueError) + finally: + ctx.cap = old_cap + + +def test_arb_series_arithmetic_and_valuation() -> None: + old_cap = ctx.cap + try: + ctx.cap = 8 + x = arb_series([0, 1], prec=8) + one = arb_series([1], prec=8) + + assert (+x) is x + assert is_close(-x, arb_series([0, -1], prec=8)) + + assert is_close(x + 1, one + x) + assert is_close(1 + x, one + x) + assert is_close(x - 1, x + (-1)) + assert is_close(1 - x, one - x) + assert is_close((one + x)*(one - x), one - x*x) + assert is_close(2*x, x + x) + assert is_close(x*2, x + x) + assert is_close(x + fmpz(2), x + 2) + assert is_close(x + fmpq(1, 2), x + 0.5) + assert is_close(x + arb(1.25), x + 1.25) + assert is_close(x + fmpz_poly([1, 1]), arb_series([1, 2], prec=8)) + assert is_close(x + fmpq_poly([1, fmpq(1, 2)]), arb_series([1, 1.5], prec=8)) + assert is_close(x + fmpz_series([1, 1], prec=8), arb_series([1, 2], prec=8)) + assert is_close(x + fmpq_series([1, fmpq(1, 2)], prec=8), arb_series([1, 1.5], prec=8)) + + assert x.valuation() == 1 + assert arb_series([0], prec=8).valuation() == -1 + assert arb_series([0, 0, 2], prec=8).valuation() == 2 + + assert raises(lambda: x + object(), TypeError) # type: ignore[operator] + assert raises(lambda: x - object(), TypeError) # type: ignore[operator] + assert raises(lambda: x * object(), TypeError) # type: ignore[operator] + assert raises(lambda: object() + x, TypeError) # type: ignore[operator] + assert raises(lambda: object() - x, TypeError) # type: ignore[operator] + assert raises(lambda: object() * x, TypeError) # type: ignore[operator] + finally: + ctx.cap = old_cap + + +def test_arb_series_mixed_complex_paths() -> None: + old_cap = ctx.cap + try: + ctx.cap = 8 + x = arb_series([0, 1], prec=8) + r1 = x + 1j + r2 = 1j + x + r3 = x - 1j + r4 = 1j - x + r5 = x * 1j + r6 = 1j * x + assert isinstance(r1, acb_series) + assert isinstance(r2, acb_series) + assert isinstance(r3, acb_series) + assert isinstance(r4, acb_series) + assert isinstance(r5, acb_series) + assert isinstance(r6, acb_series) + finally: + ctx.cap = old_cap + + +def test_arb_series_division_and_pow() -> None: + old_cap = ctx.cap + try: + ctx.cap = 8 + x = arb_series([0, 1], prec=8) + one = arb_series([1], prec=8) + + assert is_close(x/(one + x), x - x**2 + x**3 - x**4 + x**5 - x**6 + x**7) + assert ((x*x)/(x*(one + x))).prec == 7 + assert is_close((x*x)/(x*(one + x)), arb_series([0, 1, -1, 1, -1, 1, -1], prec=7)) + assert (arb_series([0], prec=8)/(one + x)).prec == 8 + assert is_close(x/2, arb_series([0, 0.5], prec=8)) + assert is_close(2/(one + x), arb_series([2, -2, 2, -2, 2, -2, 2, -2], prec=8)) + assert raises(lambda: x / arb_series([0], prec=8), ZeroDivisionError) + assert raises(lambda: one / x, ValueError) + assert raises(lambda: 2 / x, ValueError) + bad = arb_series([arb(0, 1), 1], prec=8) + assert raises(lambda: x / bad, ValueError) + assert raises(lambda: x / object(), TypeError) # type: ignore[operator] + assert raises(lambda: object() / x, TypeError) # type: ignore[operator] + + assert is_close((one + x)**2, one + 2*x + x*x) + y = 2**x + assert is_close_arb(y[0], 1) + assert is_close_arb(y[1], 0.6931471805599453, tol=1e-12, rel_tol=1e-12) + assert raises(lambda: pow(x, 2, 3), NotImplementedError) # type: ignore[misc] + assert raises(lambda: x ** object(), TypeError) # type: ignore[operator] + assert raises(lambda: object() ** x, TypeError) # type: ignore[operator] + finally: + ctx.cap = old_cap + + +def test_arb_series_call_reversion_inv() -> None: + old_cap = ctx.cap + try: + ctx.cap = 8 + x = arb_series([0, 1], prec=8) + one = arb_series([1], prec=8) + assert is_close((one + x)(x), one + x) + assert raises(lambda: (one + x)(one + x), ValueError) + assert raises(lambda: (one + x)(1), TypeError) # type: ignore[arg-type] + + r = (x + x*x).reversion() + assert is_close((x + x*x)(r), x) + assert raises(lambda: (one + x).reversion(), ValueError) + + inv = (one + x).inv() + assert is_close((one + x)*inv, one) + assert raises(lambda: arb_series([0], prec=8).inv(), ZeroDivisionError) + finally: + ctx.cap = old_cap + + +def test_arb_series_elementary_functions() -> None: + old_cap = ctx.cap + try: + ctx.cap = 8 + x = arb_series([0, 1], prec=8) + one = arb_series([1], prec=8) + x2 = arb_series([0, 1], prec=2) + one2 = arb_series([1], prec=2) + x3 = arb_series([0, 1], prec=3) + one3 = arb_series([1], prec=3) + x7 = arb_series([0, 1], prec=7) + + assert is_close((one3 + x3 + x3*x3).derivative(), [1, 2]) + assert is_close((one2 + x2).integral(), [0, 1, 1/2]) + assert is_close((one + x).sqrt(), [1, 1/2, -1/8, 1/16, -5/128, 7/256, -21/1024, 33/2048]) + assert is_close((one + x).rsqrt(), [1, -1/2, 3/8, -5/16, 35/128, -63/256, 231/1024, -429/2048]) + assert is_close(x.exp(), [1, 1, 1/2, 1/6, 1/24, 1/120, 1/720, 1/5040]) + assert is_close((one + x).log(), [0, 1, -1/2, 1/3, -1/4, 1/5, -1/6, 1/7]) + assert is_close(x.atan(), [0, 1, 0, -1/3, 0, 1/5, 0, -1/7]) + assert is_close(x.sin(), [0, 1, 0, -1/6, 0, 1/120, 0, -1/5040]) + assert is_close(x7.cos(), [1, 0, -1/2, 0, 1/24, 0, -1/720]) + assert is_close(x.tan(), [0, 1, 0, 1/3, 0, 2/15, 0, 17/315]) + assert is_close((one + x).sqrt()*(one + x).sqrt(), one + x) + assert is_close((one + x).sqrt()*(one + x).rsqrt(), one) + assert is_close(x.exp().log(), x) + assert is_close((one + x).log().exp(), one + x) + assert is_close(x.atan().tan(), x) + assert is_close(x.asin().sin(), x) + assert is_close(x.acos().cos(), x) + + s, c = x.sin_cos() + assert is_close(s, [0, 1, 0, -1/6, 0, 1/120, 0, -1/5040]) + assert is_close(c, x.cos()) + assert is_close(s*s + c*c, one) + + pi = arb.pi() + assert is_close(x.sin_pi(), [0, pi, 0, -(pi**3)/6, 0, (pi**5)/120, 0, -(pi**7)/5040]) + assert is_close(x7.cos_pi(), [1, 0, -(pi**2)/2, 0, (pi**4)/24, 0, -(pi**6)/720]) + sp, cp = x.sin_cos_pi() + assert is_close(sp, [0, pi, 0, -(pi**3)/6, 0, (pi**5)/120, 0, -(pi**7)/5040]) + assert is_close(cp, x.cos_pi()) + assert is_close(x.sin()/x.cos(), x.tan()) + assert (1 + x).cot_pi().prec == 8 + finally: + ctx.cap = old_cap + + +def test_arb_series_special_functions() -> None: + old_cap = ctx.cap + try: + ctx.cap = 8 + x = arb_series([0, 1], prec=8) + one = arb_series([1], prec=8) + + assert is_close((one + x).gamma()*(one + x).rgamma(), one) + assert is_close((one + x).lgamma().exp(), (one + x).gamma()) + assert is_close(x.rising(3), 2*x + 3*x*x + x**3) + assert is_close_arb((2 + x).zeta()[0], arb.zeta(arb(2)), tol=1e-8, rel_tol=1e-8) + assert (1 + x).riemann_siegel_theta().prec == 8 + assert (14 + x).riemann_siegel_z().prec == 8 + + assert is_close(x.erf() + x.erfc(), one) + assert x.erfi().prec == 8 + fs, fc = x.fresnel() + assert is_close(fs, x.fresnel_s()) + assert is_close(fc, x.fresnel_c()) + + assert (one + x).ei().prec == 8 + assert is_close_arb(x.si()[0], 0) + assert is_close_arb((one + x).ci()[0], arb(0.337403922900968), tol=1e-8, rel_tol=1e-8) + assert is_close_arb(x.shi()[0], 0) + assert is_close_arb((one + x).chi()[0], arb(0.837866940980208), tol=1e-8, rel_tol=1e-8) + assert (2 + x).li().prec == 8 + assert (2 + x).li(offset=True).prec == 8 + finally: + ctx.cap = old_cap + + +def test_arb_series_airy_coulomb_gamma_beta_lambertw() -> None: + old_cap = ctx.cap + try: + ctx.cap = 8 + x = arb_series([0, 1], prec=8) + + ai, aip, bi, bip = x.airy() + assert is_close(ai, x.airy_ai()) + assert is_close(aip, x.airy_ai_prime()) + assert is_close(bi, x.airy_bi()) + assert is_close(bip, x.airy_bi_prime()) + + f, g = x.coulomb(0, 0) + assert is_close(f, x.coulomb_f(0, 0)) + assert str(g) == str(x.coulomb_g(0, 0)) + + up = arb_series.gamma_upper(2, x) + lo = arb_series.gamma_lower(2, x) + assert is_close(up + lo, arb_series([1], prec=8)) + assert arb_series.gamma_upper(2, x, regularized=1).prec == 8 + assert arb_series.gamma_lower(2, x, regularized=1).prec == 8 + assert arb_series.beta_lower(2, 3, x).prec == 8 + assert arb_series.beta_lower(2, 3, x, regularized=1).prec == 8 + + w0 = x.lambertw() + wm1 = x.lambertw(-1) + assert is_close(w0.exp()*w0, x) + assert wm1.prec == 8 + assert raises(lambda: x.lambertw(2), ValueError) + finally: + ctx.cap = old_cap + + +def test_arb_series_find_roots() -> None: + roots = arb_series.find_roots(lambda t: t.sin(), -1, 1, maxn=200) + assert roots == [(arb(-1), arb(1))] + assert arb_series.find_roots(lambda t: t + 2, -1, 1, maxn=200) == [] + assert len(arb_series.find_roots(lambda t: t*t - 0.1, -1, 1, maxn=200)) == 2 + assert raises(lambda: arb_series.find_roots(lambda t: t, 0, 1, maxn=20), ValueError) + assert raises(lambda: arb_series.find_roots(lambda t: t.sin(), -1, 1, maxn=1), ValueError) + assert raises(lambda: arb_series.find_roots(lambda t: (t - 1)*(t - 33 / 32), 0, 2, maxn=200), ValueError) diff --git a/src/flint/test/test_dirichlet.py b/src/flint/test/test_dirichlet.py new file mode 100644 index 00000000..441b9270 --- /dev/null +++ b/src/flint/test/test_dirichlet.py @@ -0,0 +1,67 @@ +from __future__ import annotations + +from flint import dirichlet_char, dirichlet_group, fmpz +from flint.test.helpers import is_close_acb, raises + + +def test_dirichlet_group_basics() -> None: + g = dirichlet_group(5) + assert g.size() == 4 + assert g.q == 5 + assert g.exponent() == 4 + assert repr(g) == "Dirichlet group mod q = 5" + assert str(g) == "Dirichlet group mod q = 5" + + assert raises(lambda: dirichlet_group(0), AssertionError) + + +def test_dirichlet_char_properties() -> None: + chi = dirichlet_char(7, 3) + + assert isinstance(chi.group(), dirichlet_group) + assert chi.index() == 1 + assert chi.modulus() == 7 + assert chi.number() == 3 + assert chi.order() == 6 + assert chi.is_real() is False + assert chi.is_primitive() is True + assert chi.conductor() == 7 + assert chi.is_principal() is False + assert chi.parity() == 1 + + assert repr(chi) == "dirichlet_char(7, 3)" + assert str(chi) == "dirichlet_char(7, 3)" + + assert raises(lambda: dirichlet_char(8, 2), AssertionError) + + +def test_dirichlet_char_values_and_mul() -> None: + chi = dirichlet_char(7, 3) + principal = dirichlet_char(7, 1) + chi5 = dirichlet_char(7, 5) + + assert is_close_acb(chi(7), 0) + assert is_close_acb(chi(2), -0.5 + 0.866025403784439j, tol=1e-12, rel_tol=1e-12) + assert is_close_acb(chi(fmpz(2)), -0.5 + 0.866025403784439j, tol=1e-12, rel_tol=1e-12) + + assert chi.chi_exponent(7) is None + assert chi.chi_exponent(2) == 2 + assert principal.chi_exponent(2) == 0 + + prod = chi * chi5 + assert prod.number() == 1 + for n in [1, 2, 3, 4, 5, 6, 7, 8]: + assert is_close_acb(prod(n), chi(n) * chi5(n), tol=1e-12, rel_tol=1e-12) + + assert raises(lambda: chi * dirichlet_char(5, 1), AssertionError) + assert raises(lambda: chi(object()), TypeError) # type: ignore[arg-type] + assert raises(lambda: chi.chi_exponent(object()), TypeError) # type: ignore[arg-type] + + +def test_dirichlet_char_special_functions() -> None: + chi = dirichlet_char(7, 3) + assert is_close_acb(chi.l_function(2), 0.902247025301257 + 0.232548981277895j, tol=1e-12, rel_tol=1e-12) + assert is_close_acb(chi.l(2), chi.l_function(2), tol=1e-12, rel_tol=1e-12) + + zeta_chi = dirichlet_char(1, 1) + assert is_close_acb(zeta_chi.hardy_z(1), -0.736305462867318, tol=1e-12, rel_tol=1e-12) diff --git a/src/flint/test/test_docstrings.py b/src/flint/test/test_docstrings.py index d5b5a51d..66de7210 100644 --- a/src/flint/test/test_docstrings.py +++ b/src/flint/test/test_docstrings.py @@ -57,6 +57,7 @@ def find_doctests(module): # The below definitions are only useful when pytest is a) installed, and b) being currently run. # We don't want to impose pytest on those that just want to use `python -m flint.test` +runner: doctest.DocTestRunner | None try: import pytest @@ -101,9 +102,6 @@ def test_docstrings(test): runner.run(test) except ImportError: - class PyTestDocTestRunner(doctest.DocTestRunner): - pass - runner = None def test_docstrings(test): diff --git a/src/flint/test/test_fmpq_vec.py b/src/flint/test/test_fmpq_vec.py new file mode 100644 index 00000000..4c78cb22 --- /dev/null +++ b/src/flint/test/test_fmpq_vec.py @@ -0,0 +1,55 @@ +"""Tests for python-flint's `fmpq_vec` type.""" + +from __future__ import annotations + +from flint import fmpq, fmpq_vec +from flint.test.helpers import raises + + +def test_fmpq_vec_construct_and_iter() -> None: + v = fmpq_vec(3) + assert len(v) == 3 + assert str(v) == "['0', '0', '0']" + assert repr(v) == "fmpq_vec(['0', '0', '0'], 3)" + + v[0] = fmpq(1, 2) + v[1] = 2 + v[2] = fmpq(-3, 4) + assert str(v[0]) == "1/2" + assert str(v[1]) == "2" + assert str(v[2]) == "-3/4" + assert [str(x) for x in v] == ["1/2", "2", "-3/4"] + assert v.str() == "['1/2', '2', '-3/4']" + + w = fmpq_vec([fmpq(5, 6), 7, fmpq(8, 9)]) + assert len(w) == 3 + assert [str(x) for x in w] == ["5/6", "7", "8/9"] + assert str(w) == "['5/6', '7', '8/9']" + assert repr(w) == "fmpq_vec(['5/6', '7', '8/9'], 3)" + + +def test_fmpq_vec_double_indirect_and_errors() -> None: + v = fmpq_vec(2, double_indirect=True) + v[0] = fmpq(10, 3) + v[1] = fmpq(11, 5) + assert [str(x) for x in v] == ["10/3", "11/5"] + + assert raises(lambda: v[2], IndexError) + assert raises(lambda: v[-1], IndexError) + + def get_bad_index() -> None: + _ = v[1.25] # type: ignore[index] + + def set_bad_index() -> None: + v[1.25] = fmpq(7, 3) # type: ignore[index] + + def set_bad_value() -> None: + v[0] = object() # type: ignore[assignment] + + def set_oob() -> None: + v[2] = 1 + + assert raises(get_bad_index, TypeError) + assert raises(set_bad_index, TypeError) + assert raises(set_bad_value, TypeError) + assert raises(set_oob, IndexError) diff --git a/src/flint/test/test_fmpz_vec.py b/src/flint/test/test_fmpz_vec.py new file mode 100644 index 00000000..c64f8784 --- /dev/null +++ b/src/flint/test/test_fmpz_vec.py @@ -0,0 +1,62 @@ +"""Tests for python-flint's `fmpz_vec` type.""" + +from __future__ import annotations + +from flint import fmpz, fmpz_vec +from flint.test.helpers import raises + + +def test_fmpz_vec_construct_int_and_iter() -> None: + v = fmpz_vec(3) + assert len(v) == 3 + assert str(v) == "['0', '0', '0']" + assert repr(v) == "fmpz_vec(['0', '0', '0'], 3)" + + v[0] = 1 + v[1] = fmpz(2) + v[2] = -3 + assert int(v[0]) == 1 + assert int(v[1]) == 2 + assert int(v[2]) == -3 + assert [int(x) for x in v] == [1, 2, -3] + assert v.str() == "['1', '2', '-3']" + + w = fmpz_vec([4, 5, 6]) + assert len(w) == 3 + assert [int(x) for x in w] == [4, 5, 6] + assert str(w) == "['4', '5', '6']" + assert repr(w) == "fmpz_vec(['4', '5', '6'], 3)" + + +def test_fmpz_vec_double_indirect_and_errors() -> None: + v = fmpz_vec(2, double_indirect=True) + v[0] = 10 + v[1] = 11 + assert [int(x) for x in v] == [10, 11] + + assert raises(lambda: v[2], IndexError) + assert raises(lambda: v[-1], IndexError) + + def get_bad_index() -> None: + _ = v[1.25] # type: ignore[index] + + def set_bad_index() -> None: + v[1.25] = 7 # type: ignore[index] + + def set_bad_value() -> None: + v[0] = object() # type: ignore[assignment] + + def set_oob() -> None: + v[2] = 1 + + assert raises(get_bad_index, TypeError) + assert raises(set_bad_index, TypeError) + assert raises(set_bad_value, TypeError) + assert raises(set_oob, IndexError) + + +def test_fmpz_vec_constructor_invalid_inputs() -> None: + assert raises(lambda: fmpz_vec(-1), ValueError) + assert raises(lambda: fmpz_vec(-1, double_indirect=True), ValueError) + assert raises(lambda: fmpz_vec(1 << 62), OverflowError) + assert raises(lambda: fmpz_vec(object()), TypeError) # type: ignore[arg-type] diff --git a/src/flint/types/acb.pyi b/src/flint/types/acb.pyi new file mode 100644 index 00000000..fd68bcc3 --- /dev/null +++ b/src/flint/types/acb.pyi @@ -0,0 +1,291 @@ +from collections.abc import Callable, Iterable +from typing import Protocol, overload + +from ..flint_base.flint_base import flint_scalar +from .arb import arb +from .arf import arf +from .fmpq import fmpq as fmpq_t +from .fmpz import fmpz as fmpz_t + + +_str = str +ifmpz = int | fmpz_t +ifmpq = int | fmpz_t | fmpq_t +iarf = int | float | fmpz_t | fmpq_t | arf +iarb = int | float | fmpz_t | fmpq_t | arf | arb +iacb = int | float | complex | fmpz_t | fmpq_t | arf | arb | acb + + +class _MpcLike(Protocol): + _mpc_: tuple[object, object] + + +class acb(flint_scalar): + @overload + def __init__(self) -> None: ... + @overload + def __init__(self, real: iacb | tuple[ifmpz, ifmpz] | _str | _MpcLike, imag: iarb | tuple[ifmpz, ifmpz] | _str | None = ...) -> None: ... + + def is_zero(self) -> bool: ... + def is_finite(self) -> bool: ... + def is_exact(self) -> bool: ... + + @property + def real(self) -> arb: ... + @property + def imag(self) -> arb: ... + @property + def _mpc_(self) -> tuple[object, object]: ... + + def __contains__(self, other: iacb, /) -> bool: ... + def contains(self, other: iacb, /) -> bool: ... + def contains_interior(self, other: iacb, /) -> bool: ... + def overlaps(self, other: iacb, /) -> bool: ... + def contains_integer(self) -> bool: ... + + def mid(self) -> acb: ... + def rad(self) -> arb: ... + def complex_rad(self) -> acb: ... + + def repr(self) -> _str: ... + def str(self, *args: object, **kwargs: object) -> _str: ... + def __str__(self) -> _str: ... + def __repr__(self) -> _str: ... + + def __complex__(self) -> complex: ... + + def __pos__(self) -> acb: ... + def __neg__(self) -> acb: ... + def neg(self, exact: bool = False) -> acb: ... + def conjugate(self, exact: bool = False) -> acb: ... + + def __abs__(self) -> arb: ... + def abs_lower(self) -> arb: ... + def abs_upper(self) -> arb: ... + def csgn(self) -> arb: ... + def sgn(self) -> acb: ... + def arg(self) -> arb: ... + + def __eq__(self, other: object, /) -> bool: ... + def __ne__(self, other: object, /) -> bool: ... + + def __add__(self, other: iacb, /) -> acb: ... + def __radd__(self, other: iacb, /) -> acb: ... + def __sub__(self, other: iacb, /) -> acb: ... + def __rsub__(self, other: iacb, /) -> acb: ... + def __mul__(self, other: iacb, /) -> acb: ... + def __rmul__(self, other: iacb, /) -> acb: ... + def __truediv__(self, other: iacb, /) -> acb: ... + def __rtruediv__(self, other: iacb, /) -> acb: ... + def __pow__(self, other: iacb, modulus: None = ..., /) -> acb: ... + def __rpow__(self, other: iacb, modulus: None = ..., /) -> acb: ... + + def union(self, t: iacb) -> acb: ... + + def pow(self, t: iacb, analytic: bool = False) -> acb: ... + def log(self, analytic: bool = False) -> acb: ... + def log1p(self) -> acb: ... + def asin(self) -> acb: ... + def acos(self) -> acb: ... + def atan(self) -> acb: ... + def asinh(self) -> acb: ... + def acosh(self) -> acb: ... + def atanh(self) -> acb: ... + + def agm(self, t: iacb | None = None) -> acb: ... + + def gamma(self) -> acb: ... + def rgamma(self) -> acb: ... + def lgamma(self) -> acb: ... + def digamma(self) -> acb: ... + def zeta(self, a: iacb | None = None) -> acb: ... + def lerch_phi(self, s: iacb, a: iacb) -> acb: ... + def dirichlet_l(self, chi: object) -> acb: ... + + @staticmethod + def pi() -> acb: ... + + def sqrt(self, analytic: bool = False) -> acb: ... + def rsqrt(self, analytic: bool = False) -> acb: ... + def exp(self) -> acb: ... + def exp_pi_i(self) -> acb: ... + def expm1(self) -> acb: ... + + def sin(self) -> acb: ... + def cos(self) -> acb: ... + def sin_cos(self) -> tuple[acb, acb]: ... + def tan(self) -> acb: ... + def cot(self) -> acb: ... + def sin_pi(self) -> acb: ... + def cos_pi(self) -> acb: ... + def sin_cos_pi(self) -> tuple[acb, acb]: ... + def tan_pi(self) -> acb: ... + def cot_pi(self) -> acb: ... + def sec(self) -> acb: ... + def csc(self) -> acb: ... + + def sinh(self) -> acb: ... + def cosh(self) -> acb: ... + def sinh_cosh(self) -> tuple[acb, acb]: ... + def tanh(self) -> acb: ... + def coth(self) -> acb: ... + def sech(self) -> acb: ... + def csch(self) -> acb: ... + + def sinc(self) -> acb: ... + def sinc_pi(self) -> acb: ... + + def rising(self, n: iacb) -> acb: ... + def rising2(self, n: int) -> tuple[acb, acb]: ... + def polylog(self, s: iacb) -> acb: ... + + def erf(self) -> acb: ... + def erfc(self) -> acb: ... + def erfi(self) -> acb: ... + + @staticmethod + def modular_theta(z: iacb, tau: iacb, r: int = 0) -> tuple[acb, acb, acb, acb]: ... + def modular_eta(self) -> acb: ... + def modular_j(self) -> acb: ... + def modular_lambda(self) -> acb: ... + def modular_delta(self) -> acb: ... + + def elliptic_k(self) -> acb: ... + def elliptic_e(self) -> acb: ... + + def unique_fmpz(self) -> fmpz_t | None: ... + + def gamma_upper(self, s: iacb, regularized: int = 0) -> acb: ... + def gamma_lower(self, s: iacb, regularized: int = 0) -> acb: ... + def beta_lower(self, a: iacb, b: iacb, regularized: int = 0) -> acb: ... + def expint(self, s: iacb) -> acb: ... + + def rel_accuracy_bits(self) -> int: ... + def rel_one_accuracy_bits(self) -> int: ... + def bits(self) -> int: ... + + def ei(self) -> acb: ... + def si(self) -> acb: ... + def ci(self) -> acb: ... + def shi(self) -> acb: ... + def chi(self) -> acb: ... + def li(self, offset: bool = False) -> acb: ... + + def hypgeom_2f1( + self, + a: iacb, + b: iacb, + c: iacb, + regularized: bool = False, + ab: bool = False, + ac: bool = False, + bc: bool = False, + abc: bool = False, + ) -> acb: ... + + def chebyshev_t(self, n: iacb) -> acb: ... + def chebyshev_u(self, n: iacb) -> acb: ... + def jacobi_p(self, n: iacb, a: iacb, b: iacb) -> acb: ... + def gegenbauer_c(self, n: iacb, m: iacb) -> acb: ... + def laguerre_l(self, n: iacb, m: iacb = 0) -> acb: ... + def hermite_h(self, n: iacb) -> acb: ... + def legendre_p(self, n: iacb, m: iacb = 0, type: int = 2) -> acb: ... + def legendre_q(self, n: iacb, m: iacb = 0, type: int = 2) -> acb: ... + + @staticmethod + def spherical_y(n: int, m: int, theta: iacb, phi: iacb) -> acb: ... + + def airy_ai(self, derivative: int = 0) -> acb: ... + def airy_bi(self, derivative: int = 0) -> acb: ... + def airy(self) -> tuple[acb, acb, acb, acb]: ... + + def lambertw(self, branch: ifmpz = 0, left: bool = False, middle: bool = False) -> acb: ... + + def real_abs(self, analytic: bool = False) -> acb: ... + def real_sgn(self, analytic: bool = False) -> acb: ... + def real_heaviside(self, analytic: bool = False) -> acb: ... + def real_floor(self, analytic: bool = False) -> acb: ... + def real_ceil(self, analytic: bool = False) -> acb: ... + def real_max(self, t: iacb, analytic: bool = False) -> acb: ... + def real_min(self, t: iacb, analytic: bool = False) -> acb: ... + def real_sqrt(self, analytic: bool = False) -> acb: ... + + @staticmethod + def stieltjes(n: ifmpz, a: iacb = 1) -> acb: ... + + def bernoulli_poly(self, n: int) -> acb: ... + + @staticmethod + def dft(vec: Iterable[iacb], inverse: bool = False) -> list[acb]: ... + + def fresnel_s(self, normalized: bool = True) -> acb: ... + def fresnel_c(self, normalized: bool = True) -> acb: ... + def log_sin_pi(self) -> acb: ... + + @staticmethod + def elliptic_rf(x: iacb, y: iacb, z: iacb) -> acb: ... + @staticmethod + def elliptic_rc(x: iacb, y: iacb) -> acb: ... + @staticmethod + def elliptic_rj(x: iacb, y: iacb, z: iacb, p: iacb) -> acb: ... + @staticmethod + def elliptic_rd(x: iacb, y: iacb, z: iacb) -> acb: ... + @staticmethod + def elliptic_rg(x: iacb, y: iacb, z: iacb) -> acb: ... + + @staticmethod + def elliptic_f(phi: iacb, m: iacb, pi: bool = False) -> acb: ... + @staticmethod + def elliptic_e_inc(phi: iacb, m: iacb, pi: bool = False) -> acb: ... + @staticmethod + def elliptic_pi(n: iacb, m: iacb) -> acb: ... + @staticmethod + def elliptic_pi_inc(n: iacb, phi: iacb, m: iacb, pi: bool = False) -> acb: ... + + def elliptic_p(self, tau: iacb) -> acb: ... + def elliptic_zeta(self, tau: iacb) -> acb: ... + def elliptic_sigma(self, tau: iacb) -> acb: ... + def elliptic_inv_p(self, tau: iacb) -> acb: ... + def elliptic_roots(self) -> tuple[acb, acb, acb]: ... + def elliptic_invariants(self) -> tuple[acb, acb]: ... + + def dirichlet_eta(self) -> acb: ... + def polygamma(self, s: iacb) -> acb: ... + def log_barnes_g(self) -> acb: ... + def barnes_g(self) -> acb: ... + + def hypgeom_0f1(self, a: iacb, regularized: bool = False) -> acb: ... + def hypgeom(self, a: list[iacb], b: list[iacb], regularized: bool = False, n: int = -1) -> acb: ... + def hypgeom_u(self, a: iacb, b: iacb, n: int = -1, asymp: bool = False) -> acb: ... + def hypgeom_1f1(self, a: iacb, b: iacb, regularized: bool = False) -> acb: ... + + def bessel_j(self, n: iacb) -> acb: ... + def bessel_y(self, n: iacb) -> acb: ... + def bessel_k(self, n: iacb, scaled: bool = False) -> acb: ... + def bessel_i(self, n: iacb, scaled: bool = False) -> acb: ... + + def root(self, n: int) -> acb: ... + + @staticmethod + def zeta_zero(n: ifmpz) -> acb: ... + @staticmethod + def zeta_zeros(n: ifmpz, num: int) -> list[acb]: ... + + @staticmethod + def integral( + func: Callable[[acb, bool], object], + a: iacb, + b: iacb, + params: object | None = None, + rel_tol: iarb | None = None, + abs_tol: iarb | None = None, + deg_limit: int | None = None, + eval_limit: int | None = None, + depth_limit: int | None = None, + use_heap: bool | None = None, + verbose: bool | None = None, + ) -> acb: ... + + def coulomb(self, l: iacb, eta: iacb) -> tuple[acb, acb, acb, acb]: ... + def coulomb_f(self, l: iacb, eta: iacb) -> acb: ... + def coulomb_g(self, l: iacb, eta: iacb) -> acb: ... diff --git a/src/flint/types/acb.pyx b/src/flint/types/acb.pyx index cc3b149c..3a9758fb 100644 --- a/src/flint/types/acb.pyx +++ b/src/flint/types/acb.pyx @@ -187,7 +187,7 @@ cdef class acb(flint_scalar): cdef bint res cdef int ttype if not (op == 2 or op == 3): - raise ValueError("comparing complex numbers") + raise TypeError("comparing complex numbers") ttype = acb_set_any_ref(tval, t) if ttype == FMPZ_UNKNOWN: return NotImplemented diff --git a/src/flint/types/acb_mat.pyi b/src/flint/types/acb_mat.pyi new file mode 100644 index 00000000..87a8736b --- /dev/null +++ b/src/flint/types/acb_mat.pyi @@ -0,0 +1,103 @@ +from __future__ import annotations + +from collections.abc import Iterable, Sequence +from typing import Any, Literal, Protocol, overload + +from flint.flint_base.flint_base import flint_mat +from flint.types.acb import acb +from flint.types.acb_poly import acb_poly +from flint.types.arb import arb +from flint.types.arb_mat import arb_mat +from flint.types.fmpq import fmpq +from flint.types.fmpq_mat import fmpq_mat +from flint.types.fmpz import fmpz +from flint.types.fmpz_mat import fmpz_mat + +_str = str +acb_scalar = int | float | complex | fmpz | fmpq | arb | acb +iacb = acb_scalar | str + + +class _MatrixLike(Protocol): + rows: int + cols: int + + def __getitem__(self, index: tuple[int, int], /) -> object: ... + + +class acb_mat(flint_mat[acb]): + @classmethod + def convert(cls, x: object, /) -> acb_mat: ... + + @overload + def __init__(self, val: acb_mat | arb_mat | fmpz_mat | fmpq_mat | Sequence[Sequence[iacb]] | _MatrixLike, /) -> None: ... + @overload + def __init__(self, m: int, n: int, /) -> None: ... + @overload + def __init__(self, m: int, n: int, entries: iacb | Iterable[iacb], /) -> None: ... + + def __bool__(self) -> bool: ... + def nrows(self) -> int: ... + def ncols(self) -> int: ... + def __getitem__(self, index: tuple[int, int], /) -> acb: ... + def __setitem__(self, index: tuple[int, int], value: iacb, /) -> None: ... + + def transpose(self) -> acb_mat: ... + def conjugate(self) -> acb_mat: ... + def det(self) -> acb: ... + def __pos__(self) -> acb_mat: ... + def __neg__(self) -> acb_mat: ... + + def __add__(self, other: acb_mat | arb_mat | fmpz_mat | fmpq_mat | acb_scalar, /) -> acb_mat: ... + def __radd__(self, other: acb_mat | arb_mat | fmpz_mat | fmpq_mat | acb_scalar, /) -> acb_mat: ... + def __sub__(self, other: acb_mat | arb_mat | fmpz_mat | fmpq_mat | acb_scalar, /) -> acb_mat: ... + def __rsub__(self, other: acb_mat | arb_mat | fmpz_mat | fmpq_mat | acb_scalar, /) -> acb_mat: ... + def __mul__(self, other: acb_mat | arb_mat | fmpz_mat | fmpq_mat | acb_scalar, /) -> acb_mat: ... + def __rmul__(self, other: acb_mat | arb_mat | fmpz_mat | fmpq_mat | acb_scalar, /) -> acb_mat: ... + def __truediv__(self, other: acb_scalar, /) -> acb_mat: ... + def __pow__(self, exponent: int, modulo: object | None = None, /) -> acb_mat: ... + + def inv(self, nonstop: bool = False) -> acb_mat: ... + def solve( + self, + rhs: acb_mat | arb_mat | fmpz_mat | fmpq_mat, + nonstop: bool = False, + algorithm: Literal["lu", "precond", "approx"] | None = None, + ) -> acb_mat: ... + def exp(self) -> acb_mat: ... + def charpoly(self) -> acb_poly: ... + def mid(self) -> acb_mat: ... + def trace(self) -> acb: ... + + @classmethod + def dft(cls, n: int, m: int = -1, /) -> acb_mat: ... + + def overlaps(self, other: acb_mat, /) -> bool: ... + def contains(self, other: acb_mat | arb_mat | fmpz_mat | fmpq_mat, /) -> bool: ... + def chop(self, tol: iacb, /) -> acb_mat: ... + + def __eq__(self, other: object, /) -> bool: ... + def __ne__(self, other: object, /) -> bool: ... + + @property + def real(self) -> arb_mat: ... + @property + def imag(self) -> arb_mat: ... + + def eig( + self, + left: bool = False, + right: bool = False, + multiple: bool = False, + algorithm: Literal["rump", "vdhoeven_mourrain", "approx"] | None = None, + tol: iacb | None = None, + maxiter: int = 0, + nonstop: bool = False, + ) -> Any: ... + + def theta(self, z: acb_mat, square: bool = False) -> acb_mat: ... + + def str(self, n: int = 0, radius: bool = True, more: bool = False, condense: int = 0) -> _str: ... + def repr(self) -> _str: ... + def __str__(self) -> _str: ... + def __repr__(self) -> _str: ... diff --git a/src/flint/types/acb_mat.pyx b/src/flint/types/acb_mat.pyx index 5bf18681..ae65fe71 100644 --- a/src/flint/types/acb_mat.pyx +++ b/src/flint/types/acb_mat.pyx @@ -50,6 +50,13 @@ cdef acb_mat_coerce_scalar(x, y): return x, any_as_acb(y) return NotImplemented, NotImplemented +cdef acb_mat_convert_operand(object cls, object x): + if typecheck(x, cls): + return x + if typecheck(x, fmpz_mat) or typecheck(x, fmpq_mat) or typecheck(x, arb_mat): + return cls(x) + return NotImplemented + cdef class acb_mat(flint_mat): """ Represents a matrix over the complex numbers. @@ -68,28 +75,16 @@ cdef class acb_mat(flint_mat): def __dealloc__(self): acb_mat_clear(self.val) - @classmethod - def convert_operand(cls, x): - """ - Attempts to convert *x* to an *acb_mat*, returning NotImplemented - if unsuccessful. - """ - if typecheck(x, cls): - return x - if typecheck(x, fmpz_mat) or typecheck(x, fmpq_mat) or typecheck(x, arb_mat): - return cls(x) - return NotImplemented - @classmethod def convert(cls, x): """ Attempts to convert *x* to an *acb_mat*, raising TypeError if unsuccessful. """ - x = cls.convert_operand(x) - if x is NotImplemented: + y = acb_mat_convert_operand(cls, x) + if y is NotImplemented: raise TypeError("unable to convert type %s to type %s" % (type(x), cls)) - return x + return y @cython.embedsignature(False) def __init__(self, *args): @@ -262,10 +257,10 @@ cdef class acb_mat(flint_mat): def __add__(s, t): cdef long m, n if not isinstance(t, acb_mat): - s, t = acb_mat_coerce_operands(s, t) - if s is NotImplemented: - return s - return s + t + u, v = acb_mat_coerce_operands(s, t) + if u is NotImplemented: + return NotImplemented + return u + v m = (s).nrows() n = (s).ncols() @@ -277,18 +272,18 @@ cdef class acb_mat(flint_mat): return u def __radd__(s, t): - s, t = acb_mat_coerce_operands(s, t) - if s is NotImplemented: - return s - return t + s + u, v = acb_mat_coerce_operands(s, t) + if u is NotImplemented: + return NotImplemented + return v + u def __sub__(s, t): cdef long m, n if not isinstance(t, acb_mat): - s, t = acb_mat_coerce_operands(s, t) - if s is NotImplemented: - return s - return s - t + u, v = acb_mat_coerce_operands(s, t) + if u is NotImplemented: + return NotImplemented + return u - v m = (s).nrows() n = (s).ncols() @@ -300,10 +295,10 @@ cdef class acb_mat(flint_mat): return u def __rsub__(s, t): - s, t = acb_mat_coerce_operands(s, t) - if s is NotImplemented: - return s - return t - s + u, v = acb_mat_coerce_operands(s, t) + if u is NotImplemented: + return NotImplemented + return v - u def _scalar_mul_(s, acb t): cdef acb_mat u @@ -318,10 +313,10 @@ cdef class acb_mat(flint_mat): c, d = acb_mat_coerce_scalar(s, t) if c is not NotImplemented: return c._scalar_mul_(d) - s, t = acb_mat_coerce_operands(s, t) - if s is NotImplemented: - return s - return s * t + p, q = acb_mat_coerce_operands(s, t) + if p is NotImplemented: + return NotImplemented + return p * q if acb_mat_ncols((s).val) != acb_mat_nrows((t).val): raise ValueError("incompatible shapes for matrix multiplication") @@ -334,10 +329,10 @@ cdef class acb_mat(flint_mat): c, d = acb_mat_coerce_scalar(s, t) if c is not NotImplemented: return c._scalar_mul_(d) - s, t = acb_mat_coerce_operands(s, t) - if s is NotImplemented: - return s - return t * s + u, v = acb_mat_coerce_operands(s, t) + if u is NotImplemented: + return NotImplemented + return v * u def _scalar_div_(s, acb t): cdef acb_mat u @@ -347,25 +342,21 @@ cdef class acb_mat(flint_mat): return u def __truediv__(s, t): - if typecheck(s, acb_mat): - s, t = acb_mat_coerce_scalar(s, t) - if s is NotImplemented: - return s - return s._scalar_div_(t) - return NotImplemented + u, v = acb_mat_coerce_scalar(s, t) + if u is NotImplemented: + return NotImplemented + return u._scalar_div_(v) def __pow__(s, e, m): cdef acb_mat u cdef ulong exp cdef long n - if not typecheck(s, acb_mat): - return NotImplemented exp = e n = acb_mat_nrows((s).val) if n != acb_mat_ncols((s).val): raise ValueError("matrix must be square") if m is not None: - raise NotImplementedError("modular matrix exponentiation") + raise TypeError("modular matrix exponentiation is not supported") u = acb_mat.__new__(acb_mat) acb_mat_init((u).val, n, n) acb_mat_pow_ui((u).val, (s).val, exp, getprec()) @@ -616,9 +607,10 @@ cdef class acb_mat(flint_mat): if not (op == 2 or op == 3): raise ValueError("comparing matrices") if type(s) is not type(t): - s, t = acb_mat_coerce_operands(s, t) - if s is NotImplemented: - return s + u, v = acb_mat_coerce_operands(s, t) + if u is NotImplemented: + return NotImplemented + s, t = u, v if op == 2: res = acb_mat_eq((s).val, (t).val) else: diff --git a/src/flint/types/acb_poly.pyi b/src/flint/types/acb_poly.pyi new file mode 100644 index 00000000..ff558897 --- /dev/null +++ b/src/flint/types/acb_poly.pyi @@ -0,0 +1,82 @@ +from __future__ import annotations + +from collections.abc import Sequence +from typing import Any, overload + +from ..flint_base.flint_base import flint_poly +from .acb import acb +from .arb import arb +from .arb_poly import arb_poly +from .fmpq import fmpq +from .fmpq_poly import fmpq_poly +from .fmpz import fmpz +from .fmpz_poly import fmpz_poly + + +ifmpz = int | fmpz +ifmpq = int | fmpz | fmpq +iarf = int | float | fmpz | fmpq | arb +iacb = int | float | complex | str | fmpz | fmpq | arb | acb +iacb_poly = acb_poly | iacb | fmpz_poly | fmpq_poly | arb_poly + + +class acb_poly(flint_poly[acb]): + def __init__( + self, + val: acb_poly | arb_poly | fmpz_poly | fmpq_poly | Sequence[iacb] | iacb | None = ..., + ) -> None: ... + + def __len__(self) -> int: ... + def length(self) -> int: ... + def degree(self) -> int: ... + + def __getitem__(self, i: int, /) -> acb: ... + def __setitem__(self, i: int, x: iacb, /) -> None: ... + + def repr(self) -> str: ... + + @classmethod + def from_roots(cls, roots: Sequence[iacb]) -> acb_poly: ... + + def evaluate(self, xs: Sequence[iacb], algorithm: str = "fast") -> list[acb]: ... + + @classmethod + def interpolate(cls, xs: Sequence[iacb], ys: Sequence[iacb], algorithm: str = "fast") -> acb_poly: ... + + def derivative(self) -> acb_poly: ... + def integral(self) -> acb_poly: ... + + def __pos__(self) -> acb_poly: ... + def __neg__(self) -> acb_poly: ... + + def __add__(self, other: iacb_poly, /) -> acb_poly: ... + def __radd__(self, other: iacb_poly, /) -> acb_poly: ... + def __sub__(self, other: iacb_poly, /) -> acb_poly: ... + def __rsub__(self, other: iacb_poly, /) -> acb_poly: ... + def __mul__(self, other: iacb_poly, /) -> acb_poly: ... + def __rmul__(self, other: iacb_poly, /) -> acb_poly: ... + + def __floordiv__(self, other: iacb_poly, /) -> acb_poly: ... + def __rfloordiv__(self, other: iacb_poly, /) -> acb_poly: ... + def __mod__(self, other: iacb_poly, /) -> acb_poly: ... + def __rmod__(self, other: iacb_poly, /) -> acb_poly: ... + def __divmod__(self, other: iacb_poly, /) -> tuple[acb_poly, acb_poly]: ... + def __rdivmod__(self, other: iacb_poly, /) -> tuple[acb_poly, acb_poly]: ... + + def truncate(self, n: int, /) -> acb_poly: ... + def left_shift(self, n: int, /) -> acb_poly: ... + def right_shift(self, n: int, /) -> acb_poly: ... + + def __pow__(self, exp: int, mod: None = ..., /) -> acb_poly: ... + + @overload + def __call__(self, t: acb_poly, /) -> acb_poly: ... + @overload + def __call__(self, t: fmpz_poly | fmpq_poly | arb_poly, /) -> acb_poly: ... + @overload + def __call__(self, t: iacb, /) -> acb: ... + + def unique_fmpz_poly(self) -> fmpz_poly | None: ... + def roots(self, tol: iarf | None = None, maxprec: int | None = None) -> list[acb]: ... # type: ignore[override] + def complex_roots(self, tol: iarf | None = None, maxprec: int | None = None) -> list[acb]: ... + def root_bound(self) -> arb: ... diff --git a/src/flint/types/acb_poly.pyx b/src/flint/types/acb_poly.pyx index b52de19e..dc4a87a2 100644 --- a/src/flint/types/acb_poly.pyx +++ b/src/flint/types/acb_poly.pyx @@ -208,58 +208,58 @@ cdef class acb_poly(flint_poly): def __add__(s, t): if not isinstance(t, acb_poly): - s, t = acb_poly_coerce_operands(s, t) - if s is NotImplemented: - return s - return s + t + u, v = acb_poly_coerce_operands(s, t) + if u is NotImplemented: + return u + return u + v u = acb_poly.__new__(acb_poly) acb_poly_add((u).val, (s).val, (t).val, getprec()) return u def __radd__(s, t): - s, t = acb_poly_coerce_operands(s, t) - if s is NotImplemented: - return s - return t + s + u, v = acb_poly_coerce_operands(s, t) + if u is NotImplemented: + return u + return v + u def __sub__(s, t): if not isinstance(t, acb_poly): - s, t = acb_poly_coerce_operands(s, t) - if s is NotImplemented: - return s - return s - t + u, v = acb_poly_coerce_operands(s, t) + if u is NotImplemented: + return u + return u - v u = acb_poly.__new__(acb_poly) acb_poly_sub((u).val, (s).val, (t).val, getprec()) return u def __rsub__(s, t): - s, t = acb_poly_coerce_operands(s, t) - if s is NotImplemented: - return s - return t - s + u, v = acb_poly_coerce_operands(s, t) + if u is NotImplemented: + return u + return v - u def __mul__(s, t): if not isinstance(t, acb_poly): - s, t = acb_poly_coerce_operands(s, t) - if s is NotImplemented: - return s - return s * t + u, v = acb_poly_coerce_operands(s, t) + if u is NotImplemented: + return u + return u * v u = acb_poly.__new__(acb_poly) acb_poly_mul((u).val, (s).val, (t).val, getprec()) return u def __rmul__(s, t): - s, t = acb_poly_coerce_operands(s, t) - if s is NotImplemented: - return s - return t * s + u, v = acb_poly_coerce_operands(s, t) + if u is NotImplemented: + return u + return v * u def __floordiv__(s, t): if not isinstance(t, acb_poly): - s, t = acb_poly_coerce_operands(s, t) - if s is NotImplemented: - return s - return s // t + u, v = acb_poly_coerce_operands(s, t) + if u is NotImplemented: + return u + return u // v q = acb_poly.__new__(acb_poly) r = acb_poly.__new__(acb_poly) if acb_poly_divrem((q).val, (r).val, @@ -269,17 +269,17 @@ cdef class acb_poly(flint_poly): raise ZeroDivisionError("acb_poly leading coefficient must be nonzero") def __rfloordiv__(s, t): - s, t = acb_poly_coerce_operands(s, t) - if s is NotImplemented: - return s - return t // s + u, v = acb_poly_coerce_operands(s, t) + if u is NotImplemented: + return u + return v // u def __mod__(s, t): if not isinstance(t, acb_poly): - s, t = acb_poly_coerce_operands(s, t) - if s is NotImplemented: - return s - return s % t + u, v = acb_poly_coerce_operands(s, t) + if u is NotImplemented: + return u + return u % v q = acb_poly.__new__(acb_poly) r = acb_poly.__new__(acb_poly) if acb_poly_divrem((q).val, (r).val, @@ -289,17 +289,17 @@ cdef class acb_poly(flint_poly): raise ZeroDivisionError("acb_poly leading coefficient must be nonzero") def __rmod__(s, t): - s, t = acb_poly_coerce_operands(s, t) - if s is NotImplemented: - return s - return t % s + u, v = acb_poly_coerce_operands(s, t) + if u is NotImplemented: + return u + return v % u def __divmod__(s, t): if not isinstance(t, acb_poly): - s, t = acb_poly_coerce_operands(s, t) - if s is NotImplemented: - return s - return divmod(s, t) + u, v = acb_poly_coerce_operands(s, t) + if u is NotImplemented: + return u + return divmod(u, v) q = acb_poly.__new__(acb_poly) r = acb_poly.__new__(acb_poly) if acb_poly_divrem((q).val, (r).val, @@ -309,10 +309,10 @@ cdef class acb_poly(flint_poly): raise ZeroDivisionError("acb_poly leading coefficient must be nonzero") def __rdivmod__(s, t): - s, t = acb_poly_coerce_operands(s, t) - if s is NotImplemented: - return s - return divmod(t, s) + u, v = acb_poly_coerce_operands(s, t) + if u is NotImplemented: + return u + return divmod(v, u) def truncate(self, slong n): r""" diff --git a/src/flint/types/acb_series.pyi b/src/flint/types/acb_series.pyi new file mode 100644 index 00000000..44ee7f3e --- /dev/null +++ b/src/flint/types/acb_series.pyi @@ -0,0 +1,123 @@ +from __future__ import annotations + +from collections.abc import Sequence + +from ..flint_base.flint_base import flint_series +from .acb import acb +from .acb_poly import acb_poly +from .arb import arb +from .arb_poly import arb_poly +from .arb_series import arb_series +from .dirichlet import dirichlet_char +from .fmpq import fmpq +from .fmpq_poly import fmpq_poly +from .fmpq_series import fmpq_series +from .fmpz import fmpz +from .fmpz_poly import fmpz_poly +from .fmpz_series import fmpz_series + +ifmpz = int | fmpz +ifmpq = int | fmpz | fmpq +iarf = int | float | fmpz | fmpq | arb +iacb = int | float | complex | str | fmpz | fmpq | arb | acb +iacb_series = acb_series | iacb | fmpz_poly | fmpq_poly | fmpz_series | fmpq_series | arb_poly | arb_series | acb_poly + + +class acb_series(flint_series[acb]): + def __init__(self, val: acb_series | arb_series | fmpz_series | fmpq_series | fmpz_poly | fmpq_poly | acb_poly | Sequence[iacb] | iacb | None = ..., prec: int | None = ...) -> None: ... + + @property + def prec(self) -> int: ... + + def __len__(self) -> int: ... + def length(self) -> int: ... + def __getitem__(self, i: int, /) -> acb: ... + def __setitem__(self, i: int, x: iacb, /) -> None: ... + + def repr(self, **kwargs: object) -> str: ... + def str(self, *args: object, **kwargs: object) -> str: ... + + def __pos__(self) -> acb_series: ... + def __neg__(self) -> acb_series: ... + def __add__(self, other: iacb_series, /) -> acb_series: ... + def __radd__(self, other: iacb_series, /) -> acb_series: ... + def __sub__(self, other: iacb_series, /) -> acb_series: ... + def __rsub__(self, other: iacb_series, /) -> acb_series: ... + def __mul__(self, other: iacb_series, /) -> acb_series: ... + def __rmul__(self, other: iacb_series, /) -> acb_series: ... + def __truediv__(self, other: iacb_series, /) -> acb_series: ... + def __rtruediv__(self, other: iacb_series, /) -> acb_series: ... + def __pow__(self, other: iacb_series, mod: None = ..., /) -> acb_series: ... + def __rpow__(self, other: iacb_series, mod: None = ..., /) -> acb_series: ... + + def valuation(self) -> int: ... + + def __call__(self, other: acb_series, /) -> acb_series: ... + def reversion(self) -> acb_series: ... + def inv(self) -> acb_series: ... + def derivative(self) -> acb_series: ... + def integral(self) -> acb_series: ... + + def sqrt(self) -> acb_series: ... + def rsqrt(self) -> acb_series: ... + def exp(self) -> acb_series: ... + def log(self) -> acb_series: ... + def atan(self) -> acb_series: ... + def sin(self) -> acb_series: ... + def cos(self) -> acb_series: ... + def sin_cos(self) -> tuple[acb_series, acb_series]: ... + def sin_pi(self) -> acb_series: ... + def cos_pi(self) -> acb_series: ... + def sin_cos_pi(self) -> tuple[acb_series, acb_series]: ... + def cot_pi(self) -> acb_series: ... + def tan(self) -> acb_series: ... + + def gamma(self) -> acb_series: ... + def rgamma(self) -> acb_series: ... + def lgamma(self) -> acb_series: ... + def rising(self, n: int, /) -> acb_series: ... + def zeta(self, a: iacb = ..., deflate: bool = ...) -> acb_series: ... + def dirichlet_l(self, chi: dirichlet_char | tuple[int, int], deflate: bool = ...) -> acb_series: ... + + @classmethod + def polylog(cls, s: iacb_series, z: iacb) -> acb_series: ... + + def agm(self, t: iacb_series | None = ...) -> acb_series: ... + def elliptic_k(self) -> acb_series: ... + def elliptic_p(self, tau: iacb, /) -> acb_series: ... + + def erf(self) -> acb_series: ... + def erfc(self) -> acb_series: ... + def erfi(self) -> acb_series: ... + + @classmethod + def gamma_upper(cls, s: iacb, z: iacb_series, regularized: int = ...) -> acb_series: ... + @classmethod + def gamma_lower(cls, s: iacb, z: iacb_series, regularized: int = ...) -> acb_series: ... + @classmethod + def beta_lower(cls, a: iacb, b: iacb, z: iacb_series, regularized: int = ...) -> acb_series: ... + @classmethod + def hypgeom(cls, a: Sequence[iacb_series], b: Sequence[iacb_series], z: iacb_series, n: int = ..., regularized: bool = ...) -> acb_series: ... + + def airy_ai(self) -> acb_series: ... + def airy_ai_prime(self) -> acb_series: ... + def airy_bi(self) -> acb_series: ... + def airy_bi_prime(self) -> acb_series: ... + def airy(self) -> tuple[acb_series, acb_series, acb_series, acb_series]: ... + + def modular_theta(self, tau: iacb, /) -> tuple[acb_series, acb_series, acb_series, acb_series]: ... + def coulomb(self, l: iacb, eta: iacb) -> tuple[acb_series, acb_series, acb_series, acb_series]: ... + def coulomb_f(self, l: iacb, eta: iacb) -> acb_series: ... + def coulomb_g(self, l: iacb, eta: iacb) -> acb_series: ... + + def fresnel(self, normalized: bool = ...) -> tuple[acb_series, acb_series]: ... + def fresnel_s(self, normalized: bool = ...) -> acb_series: ... + def fresnel_c(self, normalized: bool = ...) -> acb_series: ... + def ei(self) -> acb_series: ... + def si(self) -> acb_series: ... + def ci(self) -> acb_series: ... + def shi(self) -> acb_series: ... + def chi(self) -> acb_series: ... + def li(self, offset: bool = ...) -> acb_series: ... + + def lambertw(self, branch: int = ...) -> acb_series: ... diff --git a/src/flint/types/acb_series.pyx b/src/flint/types/acb_series.pyx index f7ddd895..251fb023 100644 --- a/src/flint/types/acb_series.pyx +++ b/src/flint/types/acb_series.pyx @@ -72,8 +72,13 @@ cdef class acb_series(flint_series): elif typecheck(val, fmpz_series): acb_poly_set_fmpz_poly(self.val, (val).val, getprec()) self._prec = min((val)._prec, getcap()) + elif typecheck(val, fmpq_series): + acb_poly_set_fmpq_poly(self.val, (val).val, getprec()) + self._prec = min((val)._prec, getcap()) elif typecheck(val, fmpz_poly): acb_poly_set_fmpz_poly(self.val, (val).val, getprec()) + elif typecheck(val, fmpq_poly): + acb_poly_set_fmpq_poly(self.val, (val).val, getprec()) elif typecheck(val, acb_poly): acb_poly_set(self.val, (val).val) elif typecheck(val, list): @@ -152,10 +157,10 @@ cdef class acb_series(flint_series): def __add__(s, t): cdef long cap if not isinstance(t, acb_series): - s, t = acb_series_coerce_operands(s, t) - if s is NotImplemented: - return s - return s + t + u, v = acb_series_coerce_operands(s, t) + if u is NotImplemented: + return u + return u + v u = acb_series.__new__(acb_series) cap = getcap() @@ -168,18 +173,18 @@ cdef class acb_series(flint_series): return u def __radd__(s, t): - s, t = acb_series_coerce_operands(s, t) - if s is NotImplemented: - return s - return t + s + u, v = acb_series_coerce_operands(s, t) + if u is NotImplemented: + return u + return v + u def __sub__(s, t): cdef long cap if not isinstance(t, acb_series): - s, t = acb_series_coerce_operands(s, t) - if s is NotImplemented: - return s - return s - t + u, v = acb_series_coerce_operands(s, t) + if u is NotImplemented: + return u + return u - v u = acb_series.__new__(acb_series) cap = getcap() @@ -192,18 +197,18 @@ cdef class acb_series(flint_series): return u def __rsub__(s, t): - s, t = acb_series_coerce_operands(s, t) - if s is NotImplemented: - return s - return t - s + u, v = acb_series_coerce_operands(s, t) + if u is NotImplemented: + return u + return v - u def __mul__(s, t): cdef long cap if not isinstance(t, acb_series): - s, t = acb_series_coerce_operands(s, t) - if s is NotImplemented: - return s - return s * t + u, v = acb_series_coerce_operands(s, t) + if u is NotImplemented: + return u + return u * v u = acb_series.__new__(acb_series) cap = getcap() @@ -215,10 +220,10 @@ cdef class acb_series(flint_series): return u def __rmul__(s, t): - s, t = acb_series_coerce_operands(s, t) - if s is NotImplemented: - return s - return t * s + u, v = acb_series_coerce_operands(s, t) + if u is NotImplemented: + return u + return v * u cpdef valuation(self): cdef long i @@ -234,10 +239,10 @@ cdef class acb_series(flint_series): cdef acb_poly_t stmp, ttmp if not isinstance(t, acb_series): - s, t = acb_series_coerce_operands(s, t) - if s is NotImplemented: - return s - return s / t + u, v = acb_series_coerce_operands(s, t) + if u is NotImplemented: + return u + return u / v cap = getcap() cap = min(cap, (s)._prec) @@ -249,7 +254,7 @@ cdef class acb_series(flint_series): u = acb_series.__new__(acb_series) if (s).length() == 0: - u.cap = cap + (u)._prec = cap return u sval = (s).valuation() @@ -277,20 +282,20 @@ cdef class acb_series(flint_series): return u def __rtruediv__(s, t): - s, t = acb_series_coerce_operands(s, t) - if s is NotImplemented: - return s - return t / s + u, v = acb_series_coerce_operands(s, t) + if u is NotImplemented: + return u + return v / u def __pow__(s, t, mod): cdef long cap if mod is not None: raise NotImplementedError("modular exponentiation") if not isinstance(t, acb_series): - s, t = acb_series_coerce_operands(s, t) - if s is NotImplemented: - return s - return s ** t + u, v = acb_series_coerce_operands(s, t) + if u is NotImplemented: + return u + return u ** v u = acb_series.__new__(acb_series) cap = getcap() @@ -302,10 +307,10 @@ cdef class acb_series(flint_series): return u def __rpow__(s, t): - s, t = acb_series_coerce_operands(s, t) - if s is NotImplemented: - return s - return t ** s + u, v = acb_series_coerce_operands(s, t) + if u is NotImplemented: + return u + return v ** u def __call__(s, t): cdef long cap diff --git a/src/flint/types/acb_theta.pyi b/src/flint/types/acb_theta.pyi new file mode 100644 index 00000000..37f1e1e3 --- /dev/null +++ b/src/flint/types/acb_theta.pyi @@ -0,0 +1,6 @@ +from __future__ import annotations + +from flint.types.acb_mat import acb_mat + + +def acb_theta(z: acb_mat, tau: acb_mat, square: bool | int = False) -> acb_mat: ... diff --git a/src/flint/types/arb.pyi b/src/flint/types/arb.pyi new file mode 100644 index 00000000..6f477e4c --- /dev/null +++ b/src/flint/types/arb.pyi @@ -0,0 +1,281 @@ +from typing import Literal, Protocol, overload + +from ..flint_base.flint_base import flint_scalar +from .arf import arf +from .fmpq import fmpq as fmpq_t +from .fmpz import fmpz as fmpz_t + + +_str = str +ifmpz = int | fmpz_t +ifmpq = int | fmpz_t | fmpq_t +iarf = int | float | fmpz_t | fmpq_t | arf +iarb = int | float | fmpz_t | fmpq_t | arf | arb + + +class _MpfLike(Protocol): + _mpf_: tuple[int, int, int, int] + + +class arb(flint_scalar): + def __init__( + self, + mid: iarb | tuple[ifmpz, ifmpz] | _str | _MpfLike | None = ..., + rad: iarb | tuple[ifmpz, ifmpz] | _str | None = ..., + ) -> None: ... + + def is_zero(self) -> bool: ... + def is_finite(self) -> bool: ... + def is_nan(self) -> bool: ... + def is_exact(self) -> bool: ... + def is_integer(self) -> bool: ... + + def man_exp(self) -> tuple[fmpz_t, fmpz_t]: ... + def fmpq(self) -> fmpq_t: ... + def fmpz(self) -> fmpz_t: ... + + def mid(self) -> arb: ... + def rad(self) -> arb: ... + def abs_lower(self) -> arb: ... + def abs_upper(self) -> arb: ... + def lower(self) -> arb: ... + def upper(self) -> arb: ... + def mid_rad_10exp(self, n: int = 0) -> tuple[fmpz_t, fmpz_t, fmpz_t]: ... + + @property + def _mpf_(self) -> tuple[int, int, int, int]: ... + + def repr(self) -> _str: ... + def str(self, n: int = 0, radius: bool = True, more: bool = False, condense: int = 0) -> _str: ... + def __str__(self) -> _str: ... + def __repr__(self) -> _str: ... + + def __float__(self) -> float: ... + def __eq__(self, other: object, /) -> bool: ... + def __lt__(self, other: iarb, /) -> bool: ... + def __le__(self, other: iarb, /) -> bool: ... + def __gt__(self, other: iarb, /) -> bool: ... + def __ge__(self, other: iarb, /) -> bool: ... + def __hash__(self) -> int: ... + + def __contains__(self, other: iarb, /) -> bool: ... + def contains(self, other: iarb, /) -> bool: ... + def contains_interior(self, other: iarb, /) -> bool: ... + def overlaps(self, other: iarb, /) -> bool: ... + def contains_integer(self) -> bool: ... + + @property + def real(self) -> arb: ... + @property + def imag(self) -> arb: ... + + def __pos__(self) -> arb: ... + def __neg__(self) -> arb: ... + def neg(self, exact: bool = False) -> arb: ... + def __abs__(self) -> arb: ... + def sgn(self) -> arb: ... + + def __add__(self, other: iarb, /) -> arb: ... + def __radd__(self, other: iarb, /) -> arb: ... + def __sub__(self, other: iarb, /) -> arb: ... + def __rsub__(self, other: iarb, /) -> arb: ... + def __mul__(self, other: iarb, /) -> arb: ... + def __rmul__(self, other: iarb, /) -> arb: ... + def __truediv__(self, other: iarb, /) -> arb: ... + def __rtruediv__(self, other: iarb, /) -> arb: ... + def __pow__(self, other: iarb, modulus: None = ..., /) -> arb: ... + def __rpow__(self, other: iarb, modulus: None = ..., /) -> arb: ... + + def floor(self) -> arb: ... + def ceil(self) -> arb: ... + + def sqrt(self) -> arb: ... + def rsqrt(self) -> arb: ... + def exp(self) -> arb: ... + def expm1(self) -> arb: ... + def log(self) -> arb: ... + def log1p(self) -> arb: ... + def log_base(self, b: int) -> arb: ... + + def sin(self) -> arb: ... + def cos(self) -> arb: ... + def sin_cos(self) -> tuple[arb, arb]: ... + def sin_pi(self) -> arb: ... + def cos_pi(self) -> arb: ... + def sin_cos_pi(self) -> tuple[arb, arb]: ... + def tan(self) -> arb: ... + def cot(self) -> arb: ... + def tan_pi(self) -> arb: ... + def cot_pi(self) -> arb: ... + @staticmethod + def sin_pi_fmpq(s: fmpq_t) -> arb: ... + @staticmethod + def cos_pi_fmpq(s: fmpq_t) -> arb: ... + @staticmethod + def sin_cos_pi_fmpq(s: fmpq_t) -> tuple[arb, arb]: ... + def sec(self) -> arb: ... + def csc(self) -> arb: ... + def sinc(self) -> arb: ... + def sinc_pi(self) -> arb: ... + + def atan(self) -> arb: ... + @staticmethod + def atan2(s: iarb, t: iarb) -> arb: ... + def acos(self) -> arb: ... + def asin(self) -> arb: ... + def atanh(self) -> arb: ... + def asinh(self) -> arb: ... + def acosh(self) -> arb: ... + + def sinh(self) -> arb: ... + def cosh(self) -> arb: ... + def sinh_cosh(self) -> tuple[arb, arb]: ... + def tanh(self) -> arb: ... + def coth(self) -> arb: ... + def sech(self) -> arb: ... + def csch(self) -> arb: ... + + def gamma(self) -> arb: ... + @staticmethod + def gamma_fmpq(s: fmpq_t) -> arb: ... + def rgamma(self) -> arb: ... + def lgamma(self) -> arb: ... + def digamma(self) -> arb: ... + def rising(self, n: iarb) -> arb: ... + @staticmethod + def rising_fmpq_ui(s: fmpq_t, n: int) -> arb: ... + def rising2(self, n: int) -> tuple[arb, arb]: ... + + def zeta(self, a: iarb | None = None) -> arb: ... + def agm(self, t: iarb = 1) -> arb: ... + + @staticmethod + def bernoulli(n: int) -> arb: ... + @staticmethod + def bell_number(n: int) -> arb: ... + @staticmethod + def partitions_p(n: int) -> arb: ... + def bernoulli_poly(self, n: int) -> arb: ... + + def fac(self) -> arb: ... + @staticmethod + def fac_ui(n: int) -> arb: ... + def bin(self, k: int) -> arb: ... + @staticmethod + def bin_uiui(n: int, k: int) -> arb: ... + @staticmethod + def fib(n: int) -> arb: ... + + def polylog(self, s: iarb) -> arb: ... + + def airy_ai(self, derivative: int = 0) -> arb: ... + def airy_bi(self, derivative: int = 0) -> arb: ... + def airy(self) -> tuple[arb, arb, arb, arb]: ... + @staticmethod + def airy_ai_zero(n: int, derivative: int = 0) -> arb: ... + @staticmethod + def airy_bi_zero(n: int, derivative: int = 0) -> arb: ... + + def chebyshev_t(self, n: iarb) -> arb: ... + def chebyshev_u(self, n: iarb) -> arb: ... + def jacobi_p(self, n: iarb, a: iarb, b: iarb) -> arb: ... + def gegenbauer_c(self, n: iarb, m: iarb) -> arb: ... + def laguerre_l(self, n: iarb, m: iarb = 0) -> arb: ... + def hermite_h(self, n: iarb) -> arb: ... + def legendre_p(self, n: iarb, m: iarb = 0, type: int = 2) -> arb: ... + def legendre_q(self, n: iarb, m: iarb = 0, type: int = 2) -> arb: ... + @staticmethod + @overload + @staticmethod + def legendre_p_root(n: int, k: int, weight: Literal[False] = False) -> arb: ... + @overload + @staticmethod + def legendre_p_root(n: int, k: int, weight: Literal[True]) -> tuple[arb, arb]: ... + + def erf(self) -> arb: ... + def erfc(self) -> arb: ... + def erfinv(self) -> arb: ... + def erfcinv(self) -> arb: ... + def erfi(self) -> arb: ... + + def fresnel_s(self, normalized: bool = True) -> arb: ... + def fresnel_c(self, normalized: bool = True) -> arb: ... + def ei(self) -> arb: ... + def si(self) -> arb: ... + def ci(self) -> arb: ... + def shi(self) -> arb: ... + def chi(self) -> arb: ... + def li(self, offset: bool = False) -> arb: ... + + def bessel_j(self, n: iarb) -> arb: ... + def bessel_y(self, n: iarb) -> arb: ... + def bessel_k(self, n: iarb, scaled: bool = False) -> arb: ... + def bessel_i(self, n: iarb, scaled: bool = False) -> arb: ... + + def gamma_upper(self, s: iarb, regularized: int = 0) -> arb: ... + def gamma_lower(self, s: iarb, regularized: int = 0) -> arb: ... + def beta_lower(self, a: iarb, b: iarb, regularized: int = 0) -> arb: ... + def expint(self, s: iarb) -> arb: ... + + def hypgeom(self, a: list[iarb], b: list[iarb], regularized: bool = False) -> arb: ... + def hypgeom_u(self, a: iarb, b: iarb) -> arb: ... + def hypgeom_1f1(self, a: iarb, b: iarb, regularized: bool = False) -> arb: ... + def hypgeom_0f1(self, a: iarb, regularized: bool = False) -> arb: ... + def hypgeom_2f1( + self, + a: iarb, + b: iarb, + c: iarb, + regularized: bool = False, + ab: bool = False, + ac: bool = False, + bc: bool = False, + abc: bool = False, + ) -> arb: ... + + @staticmethod + def pi() -> arb: ... + @staticmethod + def const_sqrt_pi() -> arb: ... + @staticmethod + def const_log2() -> arb: ... + @staticmethod + def const_log10() -> arb: ... + @staticmethod + def const_euler() -> arb: ... + @staticmethod + def const_catalan() -> arb: ... + @staticmethod + def const_e() -> arb: ... + @staticmethod + def const_khinchin() -> arb: ... + @staticmethod + def const_glaisher() -> arb: ... + + @staticmethod + def pos_inf() -> arb: ... + @staticmethod + def neg_inf() -> arb: ... + @staticmethod + def nan() -> arb: ... + + def unique_fmpz(self) -> fmpz_t | None: ... + def rel_accuracy_bits(self) -> int: ... + def rel_one_accuracy_bits(self) -> int: ... + def bits(self) -> int: ... + def lambertw(self, branch: int = 0) -> arb: ... + + def nonnegative_part(self) -> arb: ... + def union(self, t: iarb) -> arb: ... + def intersection(self, t: iarb) -> arb: ... + def min(self, t: iarb) -> arb: ... + def max(self, t: iarb) -> arb: ... + def root(self, n: int) -> arb: ... + + @staticmethod + def gram_point(n: int) -> arb: ... + def zeta_nzeros(self) -> arb: ... + def backlund_s(self) -> arb: ... + def coulomb(self, l: iarb, eta: iarb) -> tuple[arb, arb]: ... + def coulomb_f(self, l: iarb, eta: iarb) -> arb: ... + def coulomb_g(self, l: iarb, eta: iarb) -> arb: ... diff --git a/src/flint/types/arb.pyx b/src/flint/types/arb.pyx index dbb5c066..56b9ba07 100644 --- a/src/flint/types/arb.pyx +++ b/src/flint/types/arb.pyx @@ -501,10 +501,8 @@ cdef class arb(flint_scalar): cdef bint res cdef arb_struct sval[1] cdef arb_struct tval[1] - cdef int stype, ttype - stype = arb_set_any_ref(sval, s) - if stype == FMPZ_UNKNOWN: - return NotImplemented + cdef int _stype, ttype + _stype = arb_set_any_ref(sval, s) ttype = arb_set_any_ref(tval, t) if ttype == FMPZ_UNKNOWN: return NotImplemented @@ -521,8 +519,6 @@ cdef class arb(flint_scalar): res = arb_gt(sval, tval) elif op == 5: res = arb_ge(sval, tval) - if stype == FMPZ_TMP: - arb_clear(sval) if ttype == FMPZ_TMP: arb_clear(tval) return res diff --git a/src/flint/types/arb_mat.pyi b/src/flint/types/arb_mat.pyi new file mode 100644 index 00000000..bc75be47 --- /dev/null +++ b/src/flint/types/arb_mat.pyi @@ -0,0 +1,106 @@ +from __future__ import annotations + +from collections.abc import Iterable, Sequence +from typing import Any, Literal, Protocol, overload + +from flint.flint_base.flint_base import flint_mat +from flint.types.acb import acb +from flint.types.arb import arb +from flint.types.arb_poly import arb_poly +from flint.types.fmpq import fmpq +from flint.types.fmpq_mat import fmpq_mat +from flint.types.fmpz import fmpz +from flint.types.fmpz_mat import fmpz_mat + +_str = str +iarb = int | float | str | fmpz | fmpq | arb + + +class _MatrixLike(Protocol): + rows: int + cols: int + + def __getitem__(self, index: tuple[int, int], /) -> object: ... + + +class arb_mat(flint_mat[arb]): + @classmethod + def convert(cls, x: object, /) -> arb_mat: ... + + @overload + def __init__(self, val: arb_mat | fmpz_mat | fmpq_mat | Sequence[Sequence[iarb]] | _MatrixLike, /) -> None: ... + @overload + def __init__(self, m: int, n: int, /) -> None: ... + @overload + def __init__(self, m: int, n: int, entries: iarb | Iterable[iarb], /) -> None: ... + + def __bool__(self) -> bool: ... + def nrows(self) -> int: ... + def ncols(self) -> int: ... + def __getitem__(self, index: tuple[int, int], /) -> arb: ... + def __setitem__(self, index: tuple[int, int], value: iarb, /) -> None: ... + def transpose(self) -> arb_mat: ... + def det(self) -> arb: ... + def __pos__(self) -> arb_mat: ... + def __neg__(self) -> arb_mat: ... + + @overload + def __add__(self, other: arb_mat | fmpz_mat | fmpq_mat | iarb, /) -> arb_mat: ... + @overload + def __add__(self, other: complex | acb, /) -> flint_mat: ... + @overload + def __radd__(self, other: arb_mat | fmpz_mat | fmpq_mat | iarb, /) -> arb_mat: ... + @overload + def __radd__(self, other: complex | acb, /) -> flint_mat: ... + @overload + def __sub__(self, other: arb_mat | fmpz_mat | fmpq_mat | iarb, /) -> arb_mat: ... + @overload + def __sub__(self, other: complex | acb, /) -> flint_mat: ... + @overload + def __rsub__(self, other: arb_mat | fmpz_mat | fmpq_mat | iarb, /) -> arb_mat: ... + @overload + def __rsub__(self, other: complex | acb, /) -> flint_mat: ... + @overload + def __mul__(self, other: arb_mat | fmpz_mat | fmpq_mat | iarb, /) -> arb_mat: ... + @overload + def __mul__(self, other: complex | acb, /) -> flint_mat: ... + @overload + def __rmul__(self, other: arb_mat | fmpz_mat | fmpq_mat | iarb, /) -> arb_mat: ... + @overload + def __rmul__(self, other: complex | acb, /) -> flint_mat: ... + def __truediv__(self, other: iarb, /) -> arb_mat: ... + def __pow__(self, exponent: int, modulo: object | None = None, /) -> arb_mat: ... + + def inv(self, nonstop: bool = False) -> arb_mat: ... + def solve( + self, + rhs: arb_mat | fmpz_mat | fmpq_mat, + nonstop: bool = False, + algorithm: Literal["lu", "precond", "approx"] | None = None, + ) -> arb_mat: ... + def exp(self) -> arb_mat: ... + def charpoly(self) -> arb_poly: ... + def mid(self) -> arb_mat: ... + def trace(self) -> arb: ... + + @classmethod + def hilbert(cls, n: int, m: int, /) -> arb_mat: ... + @classmethod + def pascal(cls, n: int, m: int, triangular: int = 0, /) -> arb_mat: ... + @classmethod + def stirling(cls, n: int, m: int, kind: int = 0, /) -> arb_mat: ... + @classmethod + def dct(cls, n: int, m: int = -1, /) -> arb_mat: ... + + def overlaps(self, other: arb_mat, /) -> bool: ... + def contains(self, other: arb_mat | fmpz_mat | fmpq_mat, /) -> bool: ... + def chop(self, tol: iarb, /) -> arb_mat: ... + + def __eq__(self, other: object, /) -> bool: ... + def __ne__(self, other: object, /) -> bool: ... + def eig(self, *args: Any, **kwargs: Any) -> Any: ... + + def str(self, n: int = 0, radius: bool = True, more: bool = False, condense: int = 0) -> _str: ... + def repr(self) -> _str: ... + def __str__(self) -> _str: ... + def __repr__(self) -> _str: ... diff --git a/src/flint/types/arb_mat.pyx b/src/flint/types/arb_mat.pyx index ed8f13ab..04ab95cc 100644 --- a/src/flint/types/arb_mat.pyx +++ b/src/flint/types/arb_mat.pyx @@ -42,6 +42,13 @@ cdef arb_mat_coerce_scalar(x, y): return acb_mat(x), any_as_acb(y) return NotImplemented, NotImplemented +cdef arb_mat_convert_operand(object cls, object x): + if typecheck(x, cls): + return x + if typecheck(x, fmpz_mat) or typecheck(x, fmpq_mat): + return cls(x) + return NotImplemented + cdef class arb_mat(flint_mat): """ Represents a matrix over the real numbers. @@ -66,25 +73,13 @@ cdef class arb_mat(flint_mat): def __dealloc__(self): arb_mat_clear(self.val) - @classmethod - def convert_operand(cls, x): - """ - Attempts to convert *x* to an *arb_mat*, returning NotImplemented - if unsuccessful. - """ - if typecheck(x, cls): - return x - if typecheck(x, fmpz_mat) or typecheck(x, fmpq_mat): - return cls(x) - return NotImplemented - @classmethod def convert(cls, x): """ Attempts to convert *x* to an *arb_mat*, raising TypeError if unsuccessful. """ - x = cls.convert_operand(x) + x = arb_mat_convert_operand(cls, x) if x is NotImplemented: raise TypeError("unable to convert type %s to type %s" % (type(x), cls)) return x @@ -241,10 +236,10 @@ cdef class arb_mat(flint_mat): def __add__(s, t): cdef long m, n if not isinstance(t, arb_mat): - s, t = arb_mat_coerce_operands(s, t) - if s is NotImplemented: - return s - return s + t + u, v = arb_mat_coerce_operands(s, t) + if u is NotImplemented: + return NotImplemented + return u + v m = (s).nrows() n = (s).ncols() @@ -256,18 +251,18 @@ cdef class arb_mat(flint_mat): return u def __radd__(s, t): - s, t = arb_mat_coerce_operands(s, t) - if s is NotImplemented: - return s - return t + s + u, v = arb_mat_coerce_operands(s, t) + if u is NotImplemented: + return NotImplemented + return v + u def __sub__(s, t): cdef long m, n if not isinstance(t, arb_mat): - s, t = arb_mat_coerce_operands(s, t) - if s is NotImplemented: - return s - return s - t + u, v = arb_mat_coerce_operands(s, t) + if u is NotImplemented: + return NotImplemented + return u - v m = (s).nrows() n = (s).ncols() @@ -279,10 +274,10 @@ cdef class arb_mat(flint_mat): return u def __rsub__(s, t): - s, t = arb_mat_coerce_operands(s, t) - if s is NotImplemented: - return s - return t - s + u, v = arb_mat_coerce_operands(s, t) + if u is NotImplemented: + return NotImplemented + return v - u def _scalar_mul_(s, arb t): cdef arb_mat u @@ -297,10 +292,10 @@ cdef class arb_mat(flint_mat): c, d = arb_mat_coerce_scalar(s, t) if c is not NotImplemented: return c._scalar_mul_(d) - s, t = arb_mat_coerce_operands(s, t) - if s is NotImplemented: - return s - return s * t + c, d = arb_mat_coerce_operands(s, t) + if c is NotImplemented: + return NotImplemented + return c * d if arb_mat_ncols((s).val) != arb_mat_nrows((t).val): raise ValueError("incompatible shapes for matrix multiplication") @@ -313,10 +308,10 @@ cdef class arb_mat(flint_mat): c, d = arb_mat_coerce_scalar(s, t) if c is not NotImplemented: return c._scalar_mul_(d) - s, t = arb_mat_coerce_operands(s, t) - if s is NotImplemented: - return s - return t * s + u, v = arb_mat_coerce_operands(s, t) + if u is NotImplemented: + return NotImplemented + return v * u def _scalar_div_(s, arb t): cdef arb_mat u @@ -326,10 +321,10 @@ cdef class arb_mat(flint_mat): return u def __truediv__(s, t): - s, t = arb_mat_coerce_scalar(s, t) - if s is NotImplemented: - return s - return s._scalar_div_(t) + u, v = arb_mat_coerce_scalar(s, t) + if u is NotImplemented: + return NotImplemented + return u._scalar_div_(v) def __pow__(s, e, m): cdef arb_mat u @@ -699,9 +694,10 @@ cdef class arb_mat(flint_mat): if not (op == 2 or op == 3): raise ValueError("comparing matrices") if type(s) is not type(t): - s, t = arb_mat_coerce_operands(s, t) - if s is NotImplemented: - return s + u, v = arb_mat_coerce_operands(s, t) + if u is NotImplemented: + return NotImplemented + s, t = u, v if op == 2: res = arb_mat_eq((s).val, (t).val) else: diff --git a/src/flint/types/arb_poly.pyi b/src/flint/types/arb_poly.pyi new file mode 100644 index 00000000..97459b22 --- /dev/null +++ b/src/flint/types/arb_poly.pyi @@ -0,0 +1,125 @@ +from __future__ import annotations + +from collections.abc import Sequence +from typing import Any, overload + +from ..flint_base.flint_base import flint_poly +from .acb import acb +from .acb_poly import acb_poly +from .arb import arb +from .fmpq import fmpq +from .fmpq_poly import fmpq_poly +from .fmpz import fmpz +from .fmpz_poly import fmpz_poly + + +ifmpz = int | fmpz +ifmpq = int | fmpz | fmpq +iarf = int | float | fmpz | fmpq | arb +iarb_poly = arb_poly | int | float | fmpz | fmpq | arb | fmpz_poly | fmpq_poly + + +class arb_poly(flint_poly[arb]): + def __init__(self, val: arb_poly | fmpz_poly | fmpq_poly | Sequence[iarf] | iarf | None = ...) -> None: ... + + def __len__(self) -> int: ... + def length(self) -> int: ... + def degree(self) -> int: ... + + def __getitem__(self, i: int, /) -> arb: ... + def __setitem__(self, i: int, x: iarf, /) -> None: ... + + def repr(self) -> str: ... + + @classmethod + def from_roots(cls, roots: Sequence[iarf]) -> arb_poly: ... + + def complex_roots(self, **kwargs: Any) -> list[Any]: ... + + def evaluate(self, xs: Sequence[iarf], algorithm: str = "fast") -> list[arb]: ... + + @classmethod + def interpolate(cls, xs: Sequence[iarf], ys: Sequence[iarf], algorithm: str = "fast") -> arb_poly: ... + + def derivative(self) -> arb_poly: ... + def integral(self) -> arb_poly: ... + + def __pos__(self) -> arb_poly: ... + def __neg__(self) -> arb_poly: ... + + @overload # type: ignore[override] + def __add__(self, other: iarb_poly, /) -> arb_poly: ... + @overload + def __add__(self, other: acb | complex, /) -> acb_poly: ... + + @overload + def __radd__(self, other: iarb_poly, /) -> arb_poly: ... + @overload + def __radd__(self, other: acb | complex, /) -> acb_poly: ... + + @overload # type: ignore[override] + def __sub__(self, other: iarb_poly, /) -> arb_poly: ... + @overload + def __sub__(self, other: acb | complex, /) -> acb_poly: ... + + @overload + def __rsub__(self, other: iarb_poly, /) -> arb_poly: ... + @overload + def __rsub__(self, other: acb | complex, /) -> acb_poly: ... + + @overload # type: ignore[override] + def __mul__(self, other: iarb_poly, /) -> arb_poly: ... + @overload + def __mul__(self, other: acb | complex, /) -> acb_poly: ... + + @overload + def __rmul__(self, other: iarb_poly, /) -> arb_poly: ... + @overload + def __rmul__(self, other: acb | complex, /) -> acb_poly: ... + + @overload # type: ignore[override] + def __floordiv__(self, other: iarb_poly, /) -> arb_poly: ... + @overload + def __floordiv__(self, other: acb | complex, /) -> acb_poly: ... + + @overload + def __rfloordiv__(self, other: iarb_poly, /) -> arb_poly: ... + @overload + def __rfloordiv__(self, other: acb | complex, /) -> acb_poly: ... + + @overload # type: ignore[override] + def __mod__(self, other: iarb_poly, /) -> arb_poly: ... + @overload + def __mod__(self, other: acb | complex, /) -> acb_poly: ... + + @overload + def __rmod__(self, other: iarb_poly, /) -> arb_poly: ... + @overload + def __rmod__(self, other: acb | complex, /) -> acb_poly: ... + + @overload # type: ignore[override] + def __divmod__(self, other: iarb_poly, /) -> tuple[arb_poly, arb_poly]: ... + @overload + def __divmod__(self, other: acb | complex, /) -> tuple[acb_poly, acb_poly]: ... + + @overload + def __rdivmod__(self, other: iarb_poly, /) -> tuple[arb_poly, arb_poly]: ... + @overload + def __rdivmod__(self, other: acb | complex, /) -> tuple[acb_poly, acb_poly]: ... + + def truncate(self, n: int, /) -> arb_poly: ... + def left_shift(self, n: int, /) -> arb_poly: ... + def right_shift(self, n: int, /) -> arb_poly: ... + + def __pow__(self, exp: int, mod: None = ..., /) -> arb_poly: ... + + @overload + def __call__(self, t: arb_poly, /) -> arb_poly: ... + @overload + def __call__(self, t: fmpz_poly | fmpq_poly, /) -> arb_poly: ... + @overload + def __call__(self, t: iarf, /) -> arb: ... + @overload + def __call__(self, t: acb | complex, /) -> acb: ... + + def unique_fmpz_poly(self) -> fmpz_poly | None: ... diff --git a/src/flint/types/arb_poly.pyx b/src/flint/types/arb_poly.pyx index f00889ff..de94f1ff 100644 --- a/src/flint/types/arb_poly.pyx +++ b/src/flint/types/arb_poly.pyx @@ -206,58 +206,58 @@ cdef class arb_poly(flint_poly): def __add__(s, t): if not isinstance(t, arb_poly): - s, t = arb_poly_coerce_operands(s, t) - if s is NotImplemented: - return s - return s + t + u, v = arb_poly_coerce_operands(s, t) + if u is NotImplemented: + return u + return u + v u = arb_poly.__new__(arb_poly) arb_poly_add((u).val, (s).val, (t).val, getprec()) return u def __radd__(s, t): - s, t = arb_poly_coerce_operands(s, t) - if s is NotImplemented: - return s - return t + s + u, v = arb_poly_coerce_operands(s, t) + if u is NotImplemented: + return u + return v + u def __sub__(s, t): if not isinstance(t, arb_poly): - s, t = arb_poly_coerce_operands(s, t) - if s is NotImplemented: - return s - return s - t + u, v = arb_poly_coerce_operands(s, t) + if u is NotImplemented: + return u + return u - v u = arb_poly.__new__(arb_poly) arb_poly_sub((u).val, (s).val, (t).val, getprec()) return u def __rsub__(s, t): - s, t = arb_poly_coerce_operands(s, t) - if s is NotImplemented: - return s - return t - s + u, v = arb_poly_coerce_operands(s, t) + if u is NotImplemented: + return u + return v - u def __mul__(s, t): if not isinstance(t, arb_poly): - s, t = arb_poly_coerce_operands(s, t) - if s is NotImplemented: - return s - return s * t + u, v = arb_poly_coerce_operands(s, t) + if u is NotImplemented: + return u + return u * v u = arb_poly.__new__(arb_poly) arb_poly_mul((u).val, (s).val, (t).val, getprec()) return u def __rmul__(s, t): - s, t = arb_poly_coerce_operands(s, t) - if s is NotImplemented: - return s - return t * s + u, v = arb_poly_coerce_operands(s, t) + if u is NotImplemented: + return u + return v * u def __floordiv__(s, t): if not isinstance(t, arb_poly): - s, t = arb_poly_coerce_operands(s, t) - if s is NotImplemented: - return s - return s // t + u, v = arb_poly_coerce_operands(s, t) + if u is NotImplemented: + return u + return u // v q = arb_poly.__new__(arb_poly) r = arb_poly.__new__(arb_poly) if arb_poly_divrem((q).val, (r).val, @@ -267,17 +267,17 @@ cdef class arb_poly(flint_poly): raise ZeroDivisionError("arb_poly leading coefficient must be nonzero") def __rfloordiv__(s, t): - s, t = arb_poly_coerce_operands(s, t) - if s is NotImplemented: - return s - return t // s + u, v = arb_poly_coerce_operands(s, t) + if u is NotImplemented: + return u + return v // u def __mod__(s, t): if not isinstance(t, arb_poly): - s, t = arb_poly_coerce_operands(s, t) - if s is NotImplemented: - return s - return s % t + u, v = arb_poly_coerce_operands(s, t) + if u is NotImplemented: + return u + return u % v q = arb_poly.__new__(arb_poly) r = arb_poly.__new__(arb_poly) if arb_poly_divrem((q).val, (r).val, @@ -287,17 +287,17 @@ cdef class arb_poly(flint_poly): raise ZeroDivisionError("arb_poly leading coefficient must be nonzero") def __rmod__(s, t): - s, t = arb_poly_coerce_operands(s, t) - if s is NotImplemented: - return s - return t % s + u, v = arb_poly_coerce_operands(s, t) + if u is NotImplemented: + return u + return v % u def __divmod__(s, t): if not isinstance(t, arb_poly): - s, t = arb_poly_coerce_operands(s, t) - if s is NotImplemented: - return s - return divmod(s, t) + u, v = arb_poly_coerce_operands(s, t) + if u is NotImplemented: + return u + return divmod(u, v) q = arb_poly.__new__(arb_poly) r = arb_poly.__new__(arb_poly) if arb_poly_divrem((q).val, (r).val, @@ -307,10 +307,10 @@ cdef class arb_poly(flint_poly): raise ZeroDivisionError("arb_poly leading coefficient must be nonzero") def __rdivmod__(s, t): - s, t = arb_poly_coerce_operands(s, t) - if s is NotImplemented: - return s - return divmod(t, s) + u, v = arb_poly_coerce_operands(s, t) + if u is NotImplemented: + return u + return divmod(v, u) def truncate(self, slong n): r""" diff --git a/src/flint/types/arb_series.pyi b/src/flint/types/arb_series.pyi new file mode 100644 index 00000000..0c255b87 --- /dev/null +++ b/src/flint/types/arb_series.pyi @@ -0,0 +1,158 @@ +from __future__ import annotations + +from collections.abc import Callable, Sequence +from typing import Any, overload + +from ..flint_base.flint_base import flint_series +from .acb import acb +from .acb_poly import acb_poly +from .acb_series import acb_series +from .arb import arb +from .arb_poly import arb_poly +from .fmpq import fmpq +from .fmpq_poly import fmpq_poly +from .fmpq_series import fmpq_series +from .fmpz import fmpz +from .fmpz_poly import fmpz_poly +from .fmpz_series import fmpz_series + + +ifmpz = int | fmpz +ifmpq = int | fmpz | fmpq +iarf = int | float | fmpz | fmpq | arb +iarf_series = arb_series | iarf | fmpz_poly | fmpq_poly | fmpz_series | fmpq_series | arb_poly +iacb_series = acb_series | acb | complex | acb_poly + + +class arb_series(flint_series[arb]): + def __init__(self, val: arb_series | fmpz_series | fmpq_series | fmpz_poly | fmpq_poly | arb_poly | Sequence[iarf] | iarf | None = ..., prec: int | None = ...) -> None: ... + + @property + def prec(self) -> int: ... + + def __len__(self) -> int: ... + def length(self) -> int: ... + def __getitem__(self, i: int, /) -> arb: ... + def __setitem__(self, i: int, x: iarf, /) -> None: ... + + def repr(self, **kwargs: object) -> str: ... + def str(self, *args: object, **kwargs: object) -> str: ... + + def __pos__(self) -> arb_series: ... + def __neg__(self) -> arb_series: ... + @overload + def __add__(self, other: iarf_series, /) -> arb_series: ... + @overload + def __add__(self, other: iacb_series, /) -> acb_series: ... + + @overload + def __radd__(self, other: iarf_series, /) -> arb_series: ... + @overload + def __radd__(self, other: iacb_series, /) -> acb_series: ... + + @overload + def __sub__(self, other: iarf_series, /) -> arb_series: ... + @overload + def __sub__(self, other: iacb_series, /) -> acb_series: ... + + @overload + def __rsub__(self, other: iarf_series, /) -> arb_series: ... + @overload + def __rsub__(self, other: iacb_series, /) -> acb_series: ... + + @overload + def __mul__(self, other: iarf_series, /) -> arb_series: ... + @overload + def __mul__(self, other: iacb_series, /) -> acb_series: ... + + @overload + def __rmul__(self, other: iarf_series, /) -> arb_series: ... + @overload + def __rmul__(self, other: iacb_series, /) -> acb_series: ... + + @overload + def __truediv__(self, other: iarf_series, /) -> arb_series: ... + @overload + def __truediv__(self, other: iacb_series, /) -> acb_series: ... + + @overload + def __rtruediv__(self, other: iarf_series, /) -> arb_series: ... + @overload + def __rtruediv__(self, other: iacb_series, /) -> acb_series: ... + + @overload + def __pow__(self, other: iarf_series, mod: None = ..., /) -> arb_series: ... + @overload + def __pow__(self, other: iacb_series, mod: None = ..., /) -> acb_series: ... + + @overload + def __rpow__(self, other: iarf_series, mod: None = ..., /) -> arb_series: ... + @overload + def __rpow__(self, other: iacb_series, mod: None = ..., /) -> acb_series: ... + + def valuation(self) -> int: ... + + def __call__(self, other: arb_series, /) -> arb_series: ... + def reversion(self) -> arb_series: ... + def inv(self) -> arb_series: ... + def derivative(self) -> arb_series: ... + def integral(self) -> arb_series: ... + + def sqrt(self) -> arb_series: ... + def rsqrt(self) -> arb_series: ... + def exp(self) -> arb_series: ... + def log(self) -> arb_series: ... + def atan(self) -> arb_series: ... + def asin(self) -> arb_series: ... + def acos(self) -> arb_series: ... + def sin(self) -> arb_series: ... + def cos(self) -> arb_series: ... + def sin_cos(self) -> tuple[arb_series, arb_series]: ... + def sin_pi(self) -> arb_series: ... + def cos_pi(self) -> arb_series: ... + def sin_cos_pi(self) -> tuple[arb_series, arb_series]: ... + def cot_pi(self) -> arb_series: ... + def tan(self) -> arb_series: ... + + def gamma(self) -> arb_series: ... + def rgamma(self) -> arb_series: ... + def lgamma(self) -> arb_series: ... + def rising(self, n: int, /) -> arb_series: ... + def zeta(self, a: iarf = ..., deflate: bool = ...) -> arb_series: ... + def riemann_siegel_theta(self) -> arb_series: ... + def riemann_siegel_z(self) -> arb_series: ... + + def erf(self) -> arb_series: ... + def erfc(self) -> arb_series: ... + def erfi(self) -> arb_series: ... + def fresnel(self, normalized: bool = ...) -> tuple[arb_series, arb_series]: ... + def fresnel_s(self, normalized: bool = ...) -> arb_series: ... + def fresnel_c(self, normalized: bool = ...) -> arb_series: ... + def ei(self) -> arb_series: ... + def si(self) -> arb_series: ... + def ci(self) -> arb_series: ... + def shi(self) -> arb_series: ... + def chi(self) -> arb_series: ... + def li(self, offset: bool = ...) -> arb_series: ... + + def airy_ai(self) -> arb_series: ... + def airy_ai_prime(self) -> arb_series: ... + def airy_bi(self) -> arb_series: ... + def airy_bi_prime(self) -> arb_series: ... + def airy(self) -> tuple[arb_series, arb_series, arb_series, arb_series]: ... + + def coulomb(self, l: iarf, eta: iarf) -> tuple[arb_series, arb_series]: ... + def coulomb_f(self, l: iarf, eta: iarf) -> arb_series: ... + def coulomb_g(self, l: iarf, eta: iarf) -> arb_series: ... + + @classmethod + def gamma_upper(cls, s: iarf, z: iarf_series, regularized: int = ...) -> arb_series: ... + @classmethod + def gamma_lower(cls, s: iarf, z: iarf_series, regularized: int = ...) -> arb_series: ... + @classmethod + def beta_lower(cls, a: iarf, b: iarf, z: iarf_series, regularized: int = ...) -> arb_series: ... + + def lambertw(self, branch: int = ...) -> arb_series: ... + + @staticmethod + def find_roots(f: Callable[[arb_series], arb_series], a: iarf, b: iarf, maxn: int = ...) -> list[tuple[arb, arb]]: ... diff --git a/src/flint/types/arb_series.pyx b/src/flint/types/arb_series.pyx index 016070c5..458e3711 100644 --- a/src/flint/types/arb_series.pyx +++ b/src/flint/types/arb_series.pyx @@ -54,8 +54,13 @@ cdef class arb_series(flint_series): elif typecheck(val, fmpz_series): arb_poly_set_fmpz_poly(self.val, (val).val, getprec()) self._prec = min((val)._prec, getcap()) + elif typecheck(val, fmpq_series): + arb_poly_set_fmpq_poly(self.val, (val).val, getprec()) + self._prec = min((val)._prec, getcap()) elif typecheck(val, fmpz_poly): arb_poly_set_fmpz_poly(self.val, (val).val, getprec()) + elif typecheck(val, fmpq_poly): + arb_poly_set_fmpq_poly(self.val, (val).val, getprec()) elif typecheck(val, arb_poly): arb_poly_set(self.val, (val).val) elif typecheck(val, list): @@ -134,10 +139,10 @@ cdef class arb_series(flint_series): def __add__(s, t): cdef long cap if not isinstance(t, arb_series): - s, t = arb_series_coerce_operands(s, t) - if s is NotImplemented: - return s - return s + t + u, v = arb_series_coerce_operands(s, t) + if u is NotImplemented: + return u + return u + v u = arb_series.__new__(arb_series) cap = getcap() cap = min(cap, (s)._prec) @@ -149,18 +154,18 @@ cdef class arb_series(flint_series): return u def __radd__(s, t): - s, t = arb_series_coerce_operands(s, t) - if s is NotImplemented: - return s - return t + s + u, v = arb_series_coerce_operands(s, t) + if u is NotImplemented: + return u + return v + u def __sub__(s, t): cdef long cap if not isinstance(t, arb_series): - s, t = arb_series_coerce_operands(s, t) - if s is NotImplemented: - return s - return s - t + u, v = arb_series_coerce_operands(s, t) + if u is NotImplemented: + return u + return u - v u = arb_series.__new__(arb_series) cap = getcap() cap = min(cap, (s)._prec) @@ -172,18 +177,18 @@ cdef class arb_series(flint_series): return u def __rsub__(s, t): - s, t = arb_series_coerce_operands(s, t) - if s is NotImplemented: - return s - return t - s + u, v = arb_series_coerce_operands(s, t) + if u is NotImplemented: + return u + return v - u def __mul__(s, t): cdef long cap if not isinstance(t, arb_series): - s, t = arb_series_coerce_operands(s, t) - if s is NotImplemented: - return s - return s * t + u, v = arb_series_coerce_operands(s, t) + if u is NotImplemented: + return u + return u * v u = arb_series.__new__(arb_series) cap = getcap() cap = min(cap, (s)._prec) @@ -195,10 +200,10 @@ cdef class arb_series(flint_series): return u def __rmul__(s, t): - s, t = arb_series_coerce_operands(s, t) - if s is NotImplemented: - return s - return t * s + u, v = arb_series_coerce_operands(s, t) + if u is NotImplemented: + return u + return v * u cpdef valuation(self): cdef long i @@ -213,10 +218,10 @@ cdef class arb_series(flint_series): cdef long cap, sval, tval cdef arb_poly_t stmp, ttmp if not isinstance(t, arb_series): - s, t = arb_series_coerce_operands(s, t) - if s is NotImplemented: - return s - return s / t + u, v = arb_series_coerce_operands(s, t) + if u is NotImplemented: + return u + return u / v cap = getcap() cap = min(cap, (s)._prec) @@ -228,7 +233,7 @@ cdef class arb_series(flint_series): u = arb_series.__new__(arb_series) if (s).length() == 0: - u.cap = cap + (u)._prec = cap return u sval = (s).valuation() @@ -256,20 +261,20 @@ cdef class arb_series(flint_series): return u def __rtruediv__(s, t): - s, t = arb_series_coerce_operands(s, t) - if s is NotImplemented: - return s - return t / s + u, v = arb_series_coerce_operands(s, t) + if u is NotImplemented: + return u + return v / u def __pow__(s, t, mod): cdef long cap if mod is not None: raise NotImplementedError("modular exponentiation") if not isinstance(t, arb_series): - s, t = arb_series_coerce_operands(s, t) - if s is NotImplemented: - return s - return s ** t + u, v = arb_series_coerce_operands(s, t) + if u is NotImplemented: + return u + return u ** v u = arb_series.__new__(arb_series) cap = getcap() @@ -281,10 +286,10 @@ cdef class arb_series(flint_series): return u def __rpow__(s, t, mod): - s, t = arb_series_coerce_operands(s, t) - if s is NotImplemented: - return s - return t ** s + u, v = arb_series_coerce_operands(s, t) + if u is NotImplemented: + return u + return v ** u def __call__(s, t): cdef long cap diff --git a/src/flint/types/arf.pyi b/src/flint/types/arf.pyi new file mode 100644 index 00000000..c008c4da --- /dev/null +++ b/src/flint/types/arf.pyi @@ -0,0 +1,57 @@ +from typing import overload + +from ..flint_base.flint_base import flint_scalar +from .fmpz import fmpz +from .fmpq import fmpq + + +_str = str +ifmpz = int | fmpz +ifmpq = int | fmpz | fmpq +iarf_cmp = int | float | fmpz | fmpq | arf +iarf_arith = int | float | fmpz | arf + + +class arf(flint_scalar): + @overload + def __init__(self) -> None: ... + @overload + def __init__(self, arg: iarf_cmp | tuple[ifmpz, ifmpz] | _str, /) -> None: ... + + def is_finite(self) -> bool: ... + def is_pos_inf(self) -> bool: ... + def is_neg_inf(self) -> bool: ... + def is_nan(self) -> bool: ... + def is_zero(self) -> bool: ... + + def man_exp(self) -> tuple[fmpz, fmpz]: ... + + def _repr_str(self) -> _str: ... + def _dec_str(self, num_digits: int | None = None) -> _str: ... + + def __str__(self) -> _str: ... + def __repr__(self) -> _str: ... + + def __bool__(self) -> bool: ... + def __float__(self) -> float: ... + def __int__(self) -> int: ... + + def as_integer_ratio(self) -> tuple[int, int]: ... + def __eq__(self, other: object, /) -> bool: ... + def __lt__(self, other: iarf_cmp, /) -> bool: ... + def __le__(self, other: iarf_cmp, /) -> bool: ... + def __gt__(self, other: iarf_cmp, /) -> bool: ... + def __ge__(self, other: iarf_cmp, /) -> bool: ... + + def __neg__(self) -> arf: ... + def __pos__(self) -> arf: ... + def __abs__(self) -> arf: ... + + def __add__(self, other: iarf_arith, /) -> arf: ... + def __radd__(self, other: iarf_arith, /) -> arf: ... + def __sub__(self, other: iarf_arith, /) -> arf: ... + def __rsub__(self, other: iarf_arith, /) -> arf: ... + def __mul__(self, other: iarf_arith, /) -> arf: ... + def __rmul__(self, other: iarf_arith, /) -> arf: ... + def __truediv__(self, other: iarf_arith, /) -> arf: ... + def __rtruediv__(self, other: iarf_arith, /) -> arf: ... diff --git a/src/flint/types/arf.pyx b/src/flint/types/arf.pyx index d4dbea42..d1096ae6 100644 --- a/src/flint/types/arf.pyx +++ b/src/flint/types/arf.pyx @@ -5,9 +5,12 @@ from flint.utils.typecheck cimport typecheck from flint.utils.conversion cimport prec_to_dps from flint.types.fmpz cimport fmpz from flint.types.fmpz cimport any_as_fmpz +from flint.types.fmpq cimport fmpq from flint.types.arb cimport arb from flint.flintlib.functions.arf cimport * +from flint.flintlib.functions.fmpq cimport fmpq_numref, fmpq_denref +from flint.flintlib.types.arf cimport ARF_RND_DOWN ctx = thectx @@ -43,6 +46,8 @@ cdef class arf: arf_set_fmpz(self.val, (val).val) elif typecheck(val, arf): arf_set(self.val, (val).val) + elif typecheck(val, fmpq): + arf_fmpz_div_fmpz(self.val, fmpq_numref((val).val), fmpq_denref((val).val), getprec(), thectx.rnd) elif typecheck(val, float): arf_set_d(self.val, PyFloat_AsDouble(val)) elif typecheck(val, tuple): @@ -125,22 +130,98 @@ cdef class arf: def __str__(self): return self._dec_str() + def __bool__(self): + return not arf_is_zero(self.val) + + def __float__(self): + return arf_get_d(self.val, thectx.rnd) + + def __int__(self): + cdef fmpz z + if arf_is_nan(self.val): + raise ValueError("cannot convert NaN to integer") + elif arf_is_inf(self.val): + raise OverflowError("cannot convert infinity to integer") + z = fmpz.__new__(fmpz) + arf_get_fmpz(z.val, self.val, ARF_RND_DOWN) + return int(z) + + def as_integer_ratio(self): + if arf_is_nan(self.val): + raise ValueError("cannot convert NaN to integer ratio") + elif arf_is_inf(self.val): + raise OverflowError("cannot convert infinity to integer ratio") + man, exp = self.man_exp() + if exp >= 0: + return int(man << exp), 1 + return int(man), int(fmpz(1) << (-exp)) + def __richcmp__(s, t, int op): cdef bint res = 0 - if not typecheck(t, arf): - t = arf(t) - if op == 2: - res = arf_equal((s).val, (t).val) - elif op == 3: - res = not arf_equal((s).val, (t).val) - elif op == 0: - res = arf_cmp((s).val, (t).val) < 0 + cdef int cmp + cdef object z + cdef arf_t v, num_arf + + if typecheck(t, arf): + if op == 2: + return bool(arf_equal((s).val, (t).val)) + if op == 3: + return not arf_equal((s).val, (t).val) + cmp = arf_cmp((s).val, (t).val) + elif typecheck(t, float): + if op == 2: + return bool(arf_equal_d((s).val, PyFloat_AsDouble(t))) + if op == 3: + return not arf_equal_d((s).val, PyFloat_AsDouble(t)) + cmp = arf_cmp_d((s).val, PyFloat_AsDouble(t)) + elif typecheck(t, fmpq): + if arf_is_nan((s).val): + if op == 3: + return True + return False + if arf_is_pos_inf((s).val): + cmp = 1 + elif arf_is_neg_inf((s).val): + cmp = -1 + else: + # Compare exactly by cross-multiplying with positive denominator: + # s ? (n/d) <=> s*d ? n + arf_init(v) + arf_init(num_arf) + arf_mul_fmpz(v, (s).val, fmpq_denref((t).val), ARF_PREC_EXACT, ARF_RND_DOWN) + arf_set_fmpz(num_arf, fmpq_numref((t).val)) + cmp = arf_cmp(v, num_arf) + arf_clear(v) + arf_clear(num_arf) + if op == 2: + return cmp == 0 + if op == 3: + return cmp != 0 + else: + z = any_as_fmpz(t) + if z is NotImplemented: + return NotImplemented + arf_init(v) + arf_set_fmpz(v, (z).val) + if op == 2: + res = arf_equal((s).val, v) + arf_clear(v) + return res + if op == 3: + res = not arf_equal((s).val, v) + arf_clear(v) + return res + cmp = arf_cmp((s).val, v) + arf_clear(v) + + if op == 0: + res = cmp < 0 elif op == 1: - res = arf_cmp((s).val, (t).val) <= 0 + res = cmp <= 0 elif op == 4: - res = arf_cmp((s).val, (t).val) > 0 + res = cmp > 0 elif op == 5: - res = arf_cmp((s).val, (t).val) >= 0 + res = cmp >= 0 return res def __pos__(self): @@ -160,29 +241,255 @@ cdef class arf: return res def __add__(s, t): - if not typecheck(t, arf): - return NotImplemented - u = arf.__new__(arf) - arf_add((u).val, (s).val, (t).val, getprec(), thectx.rnd) - return u + cdef slong t_si + cdef arf_t v + cdef arf u + if typecheck(t, arf): + u = arf.__new__(arf) + arf_add((u).val, (s).val, (t).val, getprec(), thectx.rnd) + return u + elif typecheck(t, int): + try: + t_si = t + except OverflowError: + pass + else: + u = arf.__new__(arf) + arf_add_si((u).val, (s).val, t_si, getprec(), thectx.rnd) + return u + elif typecheck(t, float): + u = arf.__new__(arf) + arf_init(v) + arf_set_d(v, PyFloat_AsDouble(t)) + arf_add((u).val, (s).val, v, getprec(), thectx.rnd) + arf_clear(v) + return u + z = any_as_fmpz(t) + if z is not NotImplemented: + u = arf.__new__(arf) + arf_add_fmpz((u).val, (s).val, (z).val, getprec(), thectx.rnd) + return u + return NotImplemented + + def __radd__(s, t): + cdef slong t_si + cdef arf_t v + cdef arf u + if typecheck(t, arf): + u = arf.__new__(arf) + arf_add((u).val, (t).val, (s).val, getprec(), thectx.rnd) + return u + elif typecheck(t, int): + try: + t_si = t + except OverflowError: + pass + else: + u = arf.__new__(arf) + arf_add_si((u).val, (s).val, t_si, getprec(), thectx.rnd) + return u + elif typecheck(t, float): + u = arf.__new__(arf) + arf_init(v) + arf_set_d(v, PyFloat_AsDouble(t)) + arf_add((u).val, v, (s).val, getprec(), thectx.rnd) + arf_clear(v) + return u + z = any_as_fmpz(t) + if z is not NotImplemented: + u = arf.__new__(arf) + arf_add_fmpz((u).val, (s).val, (z).val, getprec(), thectx.rnd) + return u + return NotImplemented def __sub__(s, t): - if not typecheck(t, arf): - return NotImplemented - u = arf.__new__(arf) - arf_sub((u).val, (s).val, (t).val, getprec(), thectx.rnd) - return u + cdef slong t_si + cdef arf_t v + cdef arf u + if typecheck(t, arf): + u = arf.__new__(arf) + arf_sub((u).val, (s).val, (t).val, getprec(), thectx.rnd) + return u + elif typecheck(t, int): + try: + t_si = t + except OverflowError: + pass + else: + u = arf.__new__(arf) + arf_sub_si((u).val, (s).val, t_si, getprec(), thectx.rnd) + return u + elif typecheck(t, float): + u = arf.__new__(arf) + arf_init(v) + arf_set_d(v, PyFloat_AsDouble(t)) + arf_sub((u).val, (s).val, v, getprec(), thectx.rnd) + arf_clear(v) + return u + z = any_as_fmpz(t) + if z is not NotImplemented: + u = arf.__new__(arf) + arf_sub_fmpz((u).val, (s).val, (z).val, getprec(), thectx.rnd) + return u + return NotImplemented + + def __rsub__(s, t): + cdef slong t_si + cdef arf_t v + cdef arf u + if typecheck(t, arf): + u = arf.__new__(arf) + arf_sub((u).val, (t).val, (s).val, getprec(), thectx.rnd) + return u + elif typecheck(t, int): + try: + t_si = t + except OverflowError: + pass + else: + u = arf.__new__(arf) + arf_init(v) + arf_set_si(v, t_si) + arf_sub((u).val, v, (s).val, getprec(), thectx.rnd) + arf_clear(v) + return u + elif typecheck(t, float): + u = arf.__new__(arf) + arf_init(v) + arf_set_d(v, PyFloat_AsDouble(t)) + arf_sub((u).val, v, (s).val, getprec(), thectx.rnd) + arf_clear(v) + return u + z = any_as_fmpz(t) + if z is not NotImplemented: + u = arf.__new__(arf) + arf_init(v) + arf_set_fmpz(v, (z).val) + arf_sub((u).val, v, (s).val, getprec(), thectx.rnd) + arf_clear(v) + return u + return NotImplemented def __mul__(s, t): - if not typecheck(t, arf): - return NotImplemented - u = arf.__new__(arf) - arf_mul((u).val, (s).val, (t).val, getprec(), thectx.rnd) - return u - - def __div__(s, t): - if not typecheck(t, arf): - return NotImplemented - u = arf.__new__(arf) - arf_div((u).val, (s).val, (t).val, getprec(), thectx.rnd) - return u + cdef slong t_si + cdef arf_t v + cdef arf u + if typecheck(t, arf): + u = arf.__new__(arf) + arf_mul((u).val, (s).val, (t).val, getprec(), thectx.rnd) + return u + elif typecheck(t, int): + try: + t_si = t + except OverflowError: + pass + else: + u = arf.__new__(arf) + arf_mul_si((u).val, (s).val, t_si, getprec(), thectx.rnd) + return u + elif typecheck(t, float): + u = arf.__new__(arf) + arf_init(v) + arf_set_d(v, PyFloat_AsDouble(t)) + arf_mul((u).val, (s).val, v, getprec(), thectx.rnd) + arf_clear(v) + return u + z = any_as_fmpz(t) + if z is not NotImplemented: + u = arf.__new__(arf) + arf_mul_fmpz((u).val, (s).val, (z).val, getprec(), thectx.rnd) + return u + return NotImplemented + + def __rmul__(s, t): + cdef slong t_si + cdef arf_t v + cdef arf u + if typecheck(t, arf): + u = arf.__new__(arf) + arf_mul((u).val, (t).val, (s).val, getprec(), thectx.rnd) + return u + elif typecheck(t, int): + try: + t_si = t + except OverflowError: + pass + else: + u = arf.__new__(arf) + arf_mul_si((u).val, (s).val, t_si, getprec(), thectx.rnd) + return u + elif typecheck(t, float): + u = arf.__new__(arf) + arf_init(v) + arf_set_d(v, PyFloat_AsDouble(t)) + arf_mul((u).val, v, (s).val, getprec(), thectx.rnd) + arf_clear(v) + return u + z = any_as_fmpz(t) + if z is not NotImplemented: + u = arf.__new__(arf) + arf_mul_fmpz((u).val, (s).val, (z).val, getprec(), thectx.rnd) + return u + return NotImplemented + + def __truediv__(s, t): + cdef slong t_si + cdef arf_t v + cdef arf u + if typecheck(t, arf): + u = arf.__new__(arf) + arf_div((u).val, (s).val, (t).val, getprec(), thectx.rnd) + return u + elif typecheck(t, int): + try: + t_si = t + except OverflowError: + pass + else: + u = arf.__new__(arf) + arf_div_si((u).val, (s).val, t_si, getprec(), thectx.rnd) + return u + elif typecheck(t, float): + u = arf.__new__(arf) + arf_init(v) + arf_set_d(v, PyFloat_AsDouble(t)) + arf_div((u).val, (s).val, v, getprec(), thectx.rnd) + arf_clear(v) + return u + z = any_as_fmpz(t) + if z is not NotImplemented: + u = arf.__new__(arf) + arf_div_fmpz((u).val, (s).val, (z).val, getprec(), thectx.rnd) + return u + return NotImplemented + + def __rtruediv__(s, t): + cdef slong t_si + cdef arf_t v + cdef arf u + if typecheck(t, arf): + u = arf.__new__(arf) + arf_div((u).val, (t).val, (s).val, getprec(), thectx.rnd) + return u + elif typecheck(t, int): + try: + t_si = t + except OverflowError: + pass + else: + u = arf.__new__(arf) + arf_si_div((u).val, t_si, (s).val, getprec(), thectx.rnd) + return u + elif typecheck(t, float): + u = arf.__new__(arf) + arf_init(v) + arf_set_d(v, PyFloat_AsDouble(t)) + arf_div((u).val, v, (s).val, getprec(), thectx.rnd) + arf_clear(v) + return u + z = any_as_fmpz(t) + if z is not NotImplemented: + u = arf.__new__(arf) + arf_fmpz_div((u).val, (z).val, (s).val, getprec(), thectx.rnd) + return u + return NotImplemented diff --git a/src/flint/types/dirichlet.pyi b/src/flint/types/dirichlet.pyi new file mode 100644 index 00000000..a9fb0d1c --- /dev/null +++ b/src/flint/types/dirichlet.pyi @@ -0,0 +1,36 @@ +from __future__ import annotations + +from flint.types.acb import acb +from flint.types.fmpz import fmpz + + +class dirichlet_group: + def __init__(self, q: int, /) -> None: ... + def size(self) -> int: ... + @property + def q(self) -> fmpz: ... + def exponent(self) -> fmpz: ... + def __repr__(self) -> str: ... + def __str__(self) -> str: ... + + +class dirichlet_char: + def __init__(self, q: int, l: int = 1, /) -> None: ... + def group(self) -> dirichlet_group: ... + def index(self) -> int: ... + def modulus(self) -> fmpz: ... + def number(self) -> int: ... + def order(self) -> int: ... + def is_real(self) -> bool: ... + def is_primitive(self) -> bool: ... + def conductor(self) -> int: ... + def is_principal(self) -> bool: ... + def parity(self) -> int: ... + def __repr__(self) -> str: ... + def __str__(self) -> str: ... + def __mul__(self, other: dirichlet_char, /) -> dirichlet_char: ... + def __call__(self, n: int | fmpz, /) -> acb: ... + def chi_exponent(self, n: int | fmpz, /) -> fmpz | None: ... + def l_function(self, s: int | float | complex | str | acb | fmpz, /) -> acb: ... + def l(self, s: int | float | complex | str | acb | fmpz, /) -> acb: ... + def hardy_z(self, s: int | float | complex | str | acb | fmpz, /) -> acb: ... diff --git a/src/flint/types/fmpq_mat.pyi b/src/flint/types/fmpq_mat.pyi new file mode 100644 index 00000000..6d96007c --- /dev/null +++ b/src/flint/types/fmpq_mat.pyi @@ -0,0 +1,62 @@ +from __future__ import annotations + +from collections.abc import Iterable, Iterator, Sequence +from typing import Literal, overload + +from flint.flint_base.flint_base import flint_mat +from flint.types.fmpq import fmpq, ifmpq +from flint.types.fmpq_poly import fmpq_poly +from flint.types.fmpz import fmpz +from flint.types.fmpz_mat import fmpz_mat + +_str = str + +class fmpq_mat(flint_mat[fmpq]): + @overload + def __init__(self, val: fmpq_mat | fmpz_mat | Sequence[Sequence[ifmpq]], /) -> None: ... + @overload + def __init__(self, m: int, n: int, /) -> None: ... + @overload + def __init__(self, m: int, n: int, entries: Iterable[ifmpq], /) -> None: ... + + def __bool__(self) -> bool: ... + def __eq__(self, other: object, /) -> bool: ... + def __ne__(self, other: object, /) -> bool: ... + + def __getitem__(self, index: tuple[int, int], /) -> fmpq: ... + def __setitem__(self, index: tuple[int, int], value: ifmpq, /) -> None: ... + def __iter__(self) -> Iterator[fmpq]: ... + def entries(self) -> list[fmpq]: ... + def table(self) -> list[list[fmpq]]: ... + def tolist(self) -> list[list[fmpq]]: ... + + def det(self) -> fmpq: ... + def __pos__(self) -> fmpq_mat: ... + def __neg__(self) -> fmpq_mat: ... + def __add__(self, other: fmpq_mat | fmpz_mat, /) -> fmpq_mat: ... + def __sub__(self, other: fmpq_mat | fmpz_mat, /) -> fmpq_mat: ... + def __mul__(self, other: fmpq_mat | fmpz_mat | ifmpq, /) -> fmpq_mat: ... + def __rmul__(self, other: fmpz_mat | ifmpq, /) -> fmpq_mat: ... + def __truediv__(self, other: ifmpq, /) -> fmpq_mat: ... + + def inv(self) -> fmpq_mat: ... + def transpose(self) -> fmpq_mat: ... + def solve( + self, + other: fmpq_mat | fmpz_mat, + algorithm: Literal["fflu", "dixon"] | None = None, + /, + ) -> fmpq_mat: ... + def rref(self, inplace: bool = False, /) -> tuple[fmpq_mat, int]: ... + def rank(self) -> int: ... + @classmethod + def hilbert(cls, n: int, m: int, /) -> fmpq_mat: ... + def numer_denom(self) -> tuple[fmpz_mat, fmpz]: ... + def charpoly(self) -> fmpq_poly: ... + def minpoly(self) -> fmpq_poly: ... + def __pow__(self, exponent: int, modulo: None = None, /) -> fmpq_mat: ... + + def str(self, *args: object, **kwargs: object) -> _str: ... + def repr(self) -> _str: ... + def __str__(self) -> _str: ... + def __repr__(self) -> _str: ... diff --git a/src/flint/types/fmpq_mat.pyx b/src/flint/types/fmpq_mat.pyx index 364e6726..dbafdf16 100644 --- a/src/flint/types/fmpq_mat.pyx +++ b/src/flint/types/fmpq_mat.pyx @@ -100,9 +100,6 @@ cdef class fmpq_mat(flint_mat): cdef bint r if op != 2 and op != 3: raise TypeError("matrices cannot be ordered") - s = any_as_fmpq_mat(s) - if t is NotImplemented: - return s t = any_as_fmpq_mat(t) if t is NotImplemented: return t @@ -269,9 +266,6 @@ cdef class fmpq_mat(flint_mat): def __truediv__(s, t): return fmpq_mat._div_(s, t) - def __div__(s, t): - return fmpq_mat._div_(s, t) - def inv(self): """ Returns the inverse matrix of *self*. diff --git a/src/flint/types/fmpq_vec.pyi b/src/flint/types/fmpq_vec.pyi new file mode 100644 index 00000000..9cbad5ed --- /dev/null +++ b/src/flint/types/fmpq_vec.pyi @@ -0,0 +1,23 @@ +from __future__ import annotations + +from collections.abc import Iterator, Sequence + +from .fmpq import fmpq, ifmpq + +_str = str + + +class fmpq_vec: + def __init__(self, iterable_or_len: Sequence[ifmpq] | int, double_indirect: bool = False) -> None: ... + + def __getitem__(self, x: int, /) -> fmpq: ... + def __setitem__(self, x: int, y: ifmpq, /) -> None: ... + + def __len__(self) -> int: ... + def __iter__(self) -> Iterator[fmpq]: ... + + def str(self, *args: object) -> _str: ... + def repr(self, *args: object) -> _str: ... + + def __str__(self) -> _str: ... + def __repr__(self) -> _str: ... diff --git a/src/flint/types/fmpz_mat.pyi b/src/flint/types/fmpz_mat.pyi new file mode 100644 index 00000000..38a0f89a --- /dev/null +++ b/src/flint/types/fmpz_mat.pyi @@ -0,0 +1,115 @@ +from __future__ import annotations + +from collections.abc import Iterable, Iterator, Sequence +from typing import Literal, overload + +from flint.flint_base.flint_base import flint_mat +from flint.types.fmpq import fmpq +from flint.types.fmpz import fmpz, ifmpz +from flint.types.fmpz_poly import fmpz_poly + +_str = str + +class fmpz_mat(flint_mat[fmpz]): + @overload + def __init__(self, val: fmpz_mat | Sequence[Sequence[ifmpz]], /) -> None: ... + @overload + def __init__(self, m: int, n: int, /) -> None: ... + @overload + def __init__(self, m: int, n: int, entries: Iterable[ifmpz], /) -> None: ... + + def __bool__(self) -> bool: ... + def __eq__(self, other: object, /) -> bool: ... + def __ne__(self, other: object, /) -> bool: ... + + def __getitem__(self, index: tuple[int, int], /) -> fmpz: ... + def __setitem__(self, index: tuple[int, int], value: ifmpz, /) -> None: ... + def __iter__(self) -> Iterator[fmpz]: ... + def entries(self) -> list[fmpz]: ... + def table(self) -> list[list[fmpz]]: ... + def tolist(self) -> list[list[fmpz]]: ... + + def det(self) -> fmpz: ... + def __pos__(self) -> fmpz_mat: ... + def __neg__(self) -> fmpz_mat: ... + def __add__(self, other: fmpz_mat, /) -> fmpz_mat: ... + def __sub__(self, other: fmpz_mat, /) -> fmpz_mat: ... + @overload + def __mul__(self, other: fmpz_mat | ifmpz, /) -> fmpz_mat: ... + @overload + def __mul__(self, other: fmpq, /) -> flint_mat: ... + @overload + def __rmul__(self, other: ifmpz, /) -> fmpz_mat: ... + @overload + def __rmul__(self, other: fmpq, /) -> flint_mat: ... + def __truediv__(self, other: ifmpz, /) -> fmpz_mat: ... + def __pow__(self, exponent: int, modulo: None = None, /) -> fmpz_mat: ... + + def is_square(self) -> bool: ... + def is_empty(self) -> bool: ... + def is_zero(self) -> bool: ... + def is_one(self) -> bool: ... + def is_neg_one(self) -> bool: ... + def is_upper_triangular(self) -> bool: ... + def is_lower_triangular(self) -> bool: ... + def is_diagonal(self) -> bool: ... + def is_scalar(self) -> bool: ... + def is_hadamard(self) -> bool: ... + def is_hnf(self) -> bool: ... + def is_snf(self) -> bool: ... + + @classmethod + def hadamard(cls, n: int, /) -> fmpz_mat: ... + @classmethod + def randtest(cls, m: int, n: int, bits: int, /) -> fmpz_mat: ... + @classmethod + def randbits(cls, m: int, n: int, bits: int, /) -> fmpz_mat: ... + @classmethod + def randrank(cls, m: int, n: int, rank: int, bits: int, /) -> fmpz_mat: ... + + def rank(self) -> int: ... + @overload + def inv(self, integer: Literal[False] = False, /) -> flint_mat: ... + @overload + def inv(self, integer: Literal[True], /) -> fmpz_mat: ... + def transpose(self) -> fmpz_mat: ... + @overload + def solve(self, other: fmpz_mat, integer: Literal[False] = False, /) -> flint_mat: ... + @overload + def solve(self, other: fmpz_mat, integer: Literal[True], /) -> fmpz_mat: ... + def _fflu(self) -> tuple[fmpz_mat, fmpz, list[int], int]: ... + def fflu(self) -> tuple[fmpz_mat, fmpz_mat, fmpz_mat, fmpz_mat]: ... + def rref(self, inplace: bool = False, /) -> tuple[fmpz_mat, fmpz, int]: ... + def nullspace(self) -> tuple[fmpz_mat, int]: ... + @overload + def lll( + self, + transform: Literal[False] = False, + delta: float = 0.99, + eta: float = 0.51, + rep: str = "zbasis", + gram: str = "approx", + /, + ) -> fmpz_mat: ... + @overload + def lll( + self, + transform: Literal[True], + delta: float = 0.99, + eta: float = 0.51, + rep: str = "zbasis", + gram: str = "approx", + /, + ) -> tuple[fmpz_mat, fmpz_mat]: ... + @overload + def hnf(self, transform: Literal[False] = False, /) -> fmpz_mat: ... + @overload + def hnf(self, transform: Literal[True], /) -> tuple[fmpz_mat, fmpz_mat]: ... + def snf(self) -> fmpz_mat: ... + def charpoly(self) -> fmpz_poly: ... + def minpoly(self) -> fmpz_poly: ... + + def str(self, *args: object, **kwargs: object) -> _str: ... + def repr(self) -> _str: ... + def __str__(self) -> _str: ... + def __repr__(self) -> _str: ... diff --git a/src/flint/types/fmpz_mod_mat.pyi b/src/flint/types/fmpz_mod_mat.pyi new file mode 100644 index 00000000..d5f2981a --- /dev/null +++ b/src/flint/types/fmpz_mod_mat.pyi @@ -0,0 +1,55 @@ +from __future__ import annotations + +from collections.abc import Sequence +from typing import overload + +from flint.flint_base.flint_base import flint_mat +from flint.types.fmpz import fmpz +from flint.types.fmpz_mat import fmpz_mat +from flint.types.fmpz_mod import fmpz_mod, fmpz_mod_ctx, ifmpz_mod +from flint.types.fmpz_mod_poly import fmpz_mod_poly +from flint.types.nmod_mat import nmod_mat + +class fmpz_mod_mat(flint_mat[fmpz_mod]): + @overload + def __init__(self, m: int, n: int, entries: Sequence[ifmpz_mod], ctx: fmpz_mod_ctx, /) -> None: ... + @overload + def __init__(self, m: int, n: int, ctx: fmpz_mod_ctx, /) -> None: ... + @overload + def __init__(self, val: Sequence[Sequence[ifmpz_mod]], ctx: fmpz_mod_ctx, /) -> None: ... + @overload + def __init__(self, val: fmpz_mod_mat | fmpz_mat | nmod_mat, ctx: fmpz_mod_ctx, /) -> None: ... + @overload + def __init__(self, val: fmpz_mod_mat | nmod_mat, /) -> None: ... + + def nrows(self) -> int: ... + def ncols(self) -> int: ... + def modulus(self) -> fmpz: ... + def repr(self) -> str: ... + def entries(self) -> list[fmpz_mod]: ... + def __getitem__(self, index: tuple[int, int], /) -> fmpz_mod: ... + def __setitem__(self, index: tuple[int, int], value: ifmpz_mod, /) -> None: ... + + def __bool__(self) -> bool: ... + def __eq__(self, other: object, /) -> bool: ... + def __ne__(self, other: object, /) -> bool: ... + def __pos__(self) -> fmpz_mod_mat: ... + def __neg__(self) -> fmpz_mod_mat: ... + + def __add__(self, other: fmpz_mod_mat | fmpz_mat | nmod_mat, /) -> fmpz_mod_mat: ... + def __radd__(self, other: fmpz_mod_mat | fmpz_mat | nmod_mat, /) -> fmpz_mod_mat: ... + def __sub__(self, other: fmpz_mod_mat | fmpz_mat | nmod_mat, /) -> fmpz_mod_mat: ... + def __rsub__(self, other: fmpz_mod_mat | fmpz_mat | nmod_mat, /) -> fmpz_mod_mat: ... + def __mul__(self, other: fmpz_mod_mat | fmpz_mat | nmod_mat | ifmpz_mod, /) -> fmpz_mod_mat: ... + def __rmul__(self, other: fmpz_mod_mat | fmpz_mat | nmod_mat | ifmpz_mod, /) -> fmpz_mod_mat: ... + def __pow__(self, other: int, /) -> fmpz_mod_mat: ... + def __truediv__(self, other: ifmpz_mod, /) -> fmpz_mod_mat: ... + + def inv(self) -> fmpz_mod_mat: ... + def det(self) -> fmpz_mod: ... + def charpoly(self) -> fmpz_mod_poly: ... + def minpoly(self) -> fmpz_mod_poly: ... + def transpose(self) -> fmpz_mod_mat: ... + def solve(self, rhs: fmpz_mod_mat | nmod_mat, /) -> fmpz_mod_mat: ... + def rank(self) -> int: ... + def rref(self, inplace: bool = False, /) -> tuple[fmpz_mod_mat, int]: ... diff --git a/src/flint/types/fmpz_poly.pyi b/src/flint/types/fmpz_poly.pyi index 75612f8c..e40b6232 100644 --- a/src/flint/types/fmpz_poly.pyi +++ b/src/flint/types/fmpz_poly.pyi @@ -3,6 +3,9 @@ from flint.flint_base.flint_base import flint_poly from flint.types.fmpz import fmpz, ifmpz from flint.types.fmpq import fmpq from flint.types.fmpq_poly import fmpq_poly +from flint.types.arf import arf +from flint.types.arb import arb +from flint.types.acb import acb ifmpz_poly = fmpz_poly | ifmpz @@ -33,10 +36,10 @@ class fmpz_poly(flint_poly[fmpz]): def __call__(self, other: fmpz_poly, /) -> fmpz_poly: ... @overload def __call__(self, other: fmpq_poly, /) -> fmpq_poly: ... - # @overload - # def __call__(self, other: arb, /) -> acb: ... - # @overload - # def __call__(self, other: acb, /) -> acb: ... + @overload + def __call__(self, other: float | arf | arb, /) -> arb: ... + @overload + def __call__(self, other: acb, /) -> acb: ... def derivative(self) -> fmpz_poly: ... def __pos__(self) -> fmpz_poly: ... def __neg__(self) -> fmpz_poly: ... diff --git a/src/flint/types/fmpz_vec.pyi b/src/flint/types/fmpz_vec.pyi new file mode 100644 index 00000000..e76b3f78 --- /dev/null +++ b/src/flint/types/fmpz_vec.pyi @@ -0,0 +1,23 @@ +from __future__ import annotations + +from collections.abc import Iterator, Sequence + +from .fmpz import fmpz, ifmpz + +_str = str + + +class fmpz_vec: + def __init__(self, iterable_or_len: Sequence[ifmpz] | int, double_indirect: bool = False) -> None: ... + + def __getitem__(self, x: int, /) -> fmpz: ... + def __setitem__(self, x: int, y: ifmpz, /) -> None: ... + + def __len__(self) -> int: ... + def __iter__(self) -> Iterator[fmpz]: ... + + def str(self, *args: object) -> _str: ... + def repr(self, *args: object) -> _str: ... + + def __str__(self) -> _str: ... + def __repr__(self) -> _str: ... diff --git a/src/flint/types/fmpz_vec.pyx b/src/flint/types/fmpz_vec.pyx index 0b76f01c..b7167061 100644 --- a/src/flint/types/fmpz_vec.pyx +++ b/src/flint/types/fmpz_vec.pyx @@ -1,5 +1,6 @@ from flint.flintlib.functions.fmpz cimport fmpz_struct, fmpz_set, fmpz_init_set from flint.flintlib.functions.fmpz_vec cimport _fmpz_vec_init, _fmpz_vec_clear +from libc.stdint cimport SIZE_MAX from flint.types.fmpz cimport fmpz, any_as_fmpz @@ -8,16 +9,23 @@ cimport libc.stdlib cdef class fmpz_vec: def __cinit__(self, iterable_or_len, bint double_indirect=False): if isinstance(iterable_or_len, int): - self.length = iterable_or_len + py_length = iterable_or_len + if py_length < 0: + raise ValueError("length must be >= 0") else: - self.length = len(iterable_or_len) + py_length = len(iterable_or_len) + + if py_length > (SIZE_MAX) // sizeof(fmpz_struct): + raise OverflowError("length is too large") + + self.length = py_length self.val = _fmpz_vec_init(self.length) if double_indirect: self.double_indirect = libc.stdlib.malloc(self.length * sizeof(fmpz_struct *)) if self.double_indirect is NULL: - raise MemoryError("malloc returned a null pointer") + raise MemoryError("malloc returned a null pointer") # pragma: no cover for i in range(self.length): self.double_indirect[i] = &self.val[i] diff --git a/src/flint/types/meson.build b/src/flint/types/meson.build index ac191e49..b894ffd9 100644 --- a/src/flint/types/meson.build +++ b/src/flint/types/meson.build @@ -7,43 +7,44 @@ pyfiles = [ 'fmpz_poly.pyi', 'fmpz_series.pyi', 'fmpz_mpoly.pyi', - # 'fmpz_mat.pyi', - # 'fmpz_vec.pyi', + 'fmpz_mat.pyi', + 'fmpz_vec.pyi', 'fmpq.pyi', 'fmpq_poly.pyi', 'fmpq_series.pyi', 'fmpq_mpoly.pyi', - # 'fmpq_mat.pyi', - # 'fmpq_vec.pyi', + 'fmpq_mat.pyi', + 'fmpq_vec.pyi', 'nmod.pyi', 'nmod_poly.pyi', 'nmod_mpoly.pyi', - # 'nmod_mat.pyi', + 'nmod_mat.pyi', # 'nmod_series.pyi', 'fmpz_mod.pyi', 'fmpz_mod_poly.pyi', 'fmpz_mod_mpoly.pyi', - # 'fmpz_mod_mat.pyi', + 'fmpz_mod_mat.pyi', 'fq_default.pyi', 'fq_default_poly.pyi', - # 'arf.pyi', + 'arf.pyi', + 'arb.pyi', + 'acb.pyi', # - # 'arb.pyi', - # 'arb_poly.pyi', - # 'arb_mat.pyi', - # 'arb_series.pyi', + 'arb_poly.pyi', + 'arb_mat.pyi', + 'arb_series.pyi', # - # 'acb.pyi', - # 'acb_poly.pyi', - # 'acb_mat.pyi', - # 'acb_series.pyi', + 'acb_poly.pyi', + 'acb_mat.pyi', + 'acb_theta.pyi', + 'acb_series.pyi', # - # 'dirichlet.pyi', + 'dirichlet.pyi', # # '_gr.pyi', ] diff --git a/src/flint/types/nmod_mat.pyi b/src/flint/types/nmod_mat.pyi new file mode 100644 index 00000000..ff5463c3 --- /dev/null +++ b/src/flint/types/nmod_mat.pyi @@ -0,0 +1,64 @@ +from __future__ import annotations + +from collections.abc import Iterable, Iterator, Sequence +from typing import overload + +from flint.flint_base.flint_base import flint_mat +from flint.types.fmpz import fmpz +from flint.types.fmpz_mat import fmpz_mat +from flint.types.nmod import inmod, nmod +from flint.types.nmod_poly import nmod_poly + +_str = str + +class nmod_mat(flint_mat[nmod]): + @overload + def __init__(self, val: nmod_mat, /) -> None: ... + @overload + def __init__(self, val: fmpz_mat | Sequence[Sequence[inmod]], mod: int, /) -> None: ... + @overload + def __init__(self, m: int, n: int, mod: int, /) -> None: ... + @overload + def __init__(self, m: int, n: int, entries: Iterable[inmod], mod: int, /) -> None: ... + + def __bool__(self) -> bool: ... + def __eq__(self, other: object, /) -> bool: ... + def __ne__(self, other: object, /) -> bool: ... + + def nrows(self) -> int: ... + def ncols(self) -> int: ... + def modulus(self) -> int: ... + @classmethod + def randtest(cls, m: int, n: int, mod: int, /) -> nmod_mat: ... + + def __getitem__(self, index: tuple[int, int], /) -> nmod: ... + def __setitem__(self, index: tuple[int, int], value: inmod, /) -> None: ... + def __iter__(self) -> Iterator[nmod]: ... + def entries(self) -> list[nmod]: ... + def table(self) -> list[list[nmod]]: ... + def tolist(self) -> list[list[nmod]]: ... + + def repr(self) -> _str: ... + def __str__(self) -> _str: ... + def __repr__(self) -> _str: ... + + def __pos__(self) -> nmod_mat: ... + def __neg__(self) -> nmod_mat: ... + def __add__(self, other: nmod_mat | fmpz_mat, /) -> nmod_mat: ... + def __radd__(self, other: nmod_mat | fmpz_mat, /) -> nmod_mat: ... + def __sub__(self, other: nmod_mat | fmpz_mat, /) -> nmod_mat: ... + def __rsub__(self, other: nmod_mat | fmpz_mat, /) -> nmod_mat: ... + def __mul__(self, other: nmod_mat | fmpz_mat | inmod, /) -> nmod_mat: ... + def __rmul__(self, other: nmod_mat | fmpz_mat | inmod, /) -> nmod_mat: ... + def __truediv__(self, other: inmod, /) -> nmod_mat: ... + def __pow__(self, exponent: int, modulo: None = None, /) -> nmod_mat: ... + + def det(self) -> nmod: ... + def inv(self) -> nmod_mat: ... + def transpose(self) -> nmod_mat: ... + def solve(self, other: nmod_mat | fmpz_mat, /) -> nmod_mat: ... + def rref(self, inplace: bool = False, /) -> tuple[nmod_mat, int]: ... + def rank(self) -> int: ... + def nullspace(self) -> tuple[nmod_mat, int]: ... + def charpoly(self) -> nmod_poly: ... + def minpoly(self) -> nmod_poly: ... diff --git a/src/flint/types/nmod_mat.pyx b/src/flint/types/nmod_mat.pyx index 3da5108a..a1348582 100644 --- a/src/flint/types/nmod_mat.pyx +++ b/src/flint/types/nmod_mat.pyx @@ -358,9 +358,6 @@ cdef class nmod_mat(flint_mat): def __truediv__(s, t): return nmod_mat._div_(s, t) - def __div__(s, t): - return nmod_mat._div_(s, t) - def det(self): """ Returns the determinant of self as an nmod.