Repository: mpmath/mpmath (python)

Issue:
Support different number syntaxes
```
These should work:

'oo' and '-oo' for infinity

'Inf', 'INF', 'NaN', etc

'2+3*I' or '2+4I' (SymPy and Mathematica style imaginary unit)

'2/3 + 4/5j' (rational parts for a complex number)
```

Original issue for #184: http://code.google.com/p/mpmath/issues/detail?id=144
Original author: https://code.google.com/u/111502149103757882156/


Pull request:
Misc fixes
* adjust defaults for rnd: 'd' -> round_fast, 'f' -> round_floor
* make explicit rnd=round_fast for to_int()
* set defaults rnd=round_fast for mpf_bernoulli*
* clear unused imports in mpmath/libmp/*.py
* use prec=0 in libmp/*.py
* drop libmp.HASH_MODULUS
* drop libmp.HASH_BITS
* drop libmp.math_float_inf
* adjust nan's hashing for Python versions >= 3.10
* use to_man_exp() in mpf.man_exp to raise exceptions
* improve coverage
* add missing type cast in from_npfloat() and test
* add type-cast for mantissa in ``_mpf_`` 4-tuple (mpf constructor)
* turn DeprecationWarning's into errors in CI
* fail test suite if xfail'ed test - pass
* support more number syntaxes, closes #184

Patch:
diff --git a/mpmath/ctx_mp.py b/mpmath/ctx_mp.py
index 6ac233f..d382a02 100644
--- a/mpmath/ctx_mp.py
+++ b/mpmath/ctx_mp.py
@@ -19,8 +19,13 @@
                     mpf_sub, mpf_twinprime, repr_dps, to_str)
 
 
-get_complex = re.compile(r'^\(?(?P<re>[\+\-]?\d*(\.\d*)?(e[\+\-]?\d+)?)??'
-                         r'(?P<im>[\+\-]?\d*(\.\d*)?(e[\+\-]?\d+)?j)?\)?$')
+get_complex = re.compile(r"""
+    \(?
+    (?P<re>[+-]?(\d*(\.\d*)?(e[+-]?\d+)?|\d+/\d+))??
+    (?P<im>[+-]?(\d*(\.\d*)?(e[+-]?\d+)?|\d+/\d+)\*?[ji])?
+    \)?$
+""", re.VERBOSE | re.IGNORECASE)
+
 
 def __getattr__(name):
     if name == 'mpnumeric':
@@ -572,13 +577,12 @@ def nstr(ctx, x, n=6, **kwargs):
 
     def _convert_fallback(ctx, x, strings):
         if strings and isinstance(x, str):
-            if 'j' in x.lower():
-                x = x.lower().replace(' ', '')
-                match = get_complex.match(x)
+            match = get_complex.match(x.replace(' ', ''))
+            if match:
                 re = match.group('re')
                 if not re:
                     re = 0
-                im = match.group('im').rstrip('j')
+                im = match.group('im').rstrip('jiJI*')
                 return ctx.mpc(ctx.convert(re), ctx.convert(im))
         if hasattr(x, "_mpi_"):
             a, b = x._mpi_
diff --git a/mpmath/ctx_mp_python.py b/mpmath/ctx_mp_python.py
index c6fffde..4fdcbb7 100644
--- a/mpmath/ctx_mp_python.py
+++ b/mpmath/ctx_mp_python.py
@@ -13,7 +13,7 @@
                     mpf_mod, mpf_mul, mpf_mul_int, mpf_neg, mpf_pos, mpf_pow,
                     mpf_pow_int, mpf_rdiv_int, mpf_sub, mpf_sum, normalize,
                     prec_to_dps, round_nearest, to_fixed, to_float, to_int,
-                    to_rational, to_str)
+                    to_man_exp, to_rational, to_str)
 
 
 new = object.__new__
@@ -59,7 +59,7 @@ def __new__(cls, val=fzero, **kwargs):
             val = val._mpf_
         elif type(val) is tuple:
             if len(val) == 4:
-                pass
+                val = val[0], MPZ(val[1]), *val[2:]
             elif len(val) == 2:
                 v._mpf_ = from_man_exp(val[0], val[1], prec, rounding)
                 return v
@@ -116,7 +116,7 @@ def mpf_convert_lhs(cls, x):
             return cls.context.make_mpf(x)
         return x
 
-    man_exp = property(lambda self: self._mpf_[1:3])
+    man_exp = property(lambda self: to_man_exp(self._mpf_))
     man = property(lambda self: self._mpf_[1])
     exp = property(lambda self: self._mpf_[2])
     bc = property(lambda self: self._mpf_[3])
diff --git a/mpmath/libmp/__init__.py b/mpmath/libmp/__init__.py
index 6275b6e..56a8e34 100644
--- a/mpmath/libmp/__init__.py
+++ b/mpmath/libmp/__init__.py
@@ -1,5 +1,5 @@
-from .backend import (BACKEND, HASH_BITS, HASH_MODULUS, MPQ, MPZ, MPZ_FIVE,
-                      MPZ_ONE, MPZ_THREE, MPZ_TWO, MPZ_ZERO, gmpy, int_types)
+from .backend import (BACKEND, MPQ, MPZ, MPZ_FIVE, MPZ_ONE, MPZ_THREE, MPZ_TWO,
+                      MPZ_ZERO, gmpy, int_types)
 from .gammazeta import (apery_fixed, bernfrac, catalan_fixed, euler_fixed,
                         glaisher_fixed, khinchin_fixed, mertens_fixed,
                         mpc_altzeta, mpc_factorial, mpc_gamma, mpc_harmonic,
@@ -45,16 +45,16 @@
 from .libmpf import (ComplexResult, dps_to_prec, fhalf, finf, fnan, fninf,
                      fnone, fnzero, fone, from_Decimal, from_float, from_int,
                      from_man_exp, from_npfloat, from_rational, from_str, ften,
-                     ftwo, fzero, math_float_inf, mpf_abs, mpf_add, mpf_ceil,
-                     mpf_cmp, mpf_div, mpf_eq, mpf_floor, mpf_frac, mpf_frexp,
-                     mpf_ge, mpf_gt, mpf_hash, mpf_hypot, mpf_le, mpf_lt,
-                     mpf_mod, mpf_mul, mpf_mul_int, mpf_neg, mpf_nint,
-                     mpf_perturb, mpf_pos, mpf_pow_int, mpf_rand, mpf_rdiv_int,
-                     mpf_shift, mpf_sign, mpf_sqrt, mpf_sub, mpf_sum,
-                     normalize, prec_to_dps, repr_dps, round_ceiling,
-                     round_down, round_floor, round_int, round_nearest,
-                     round_up, str_to_man_exp, to_digits_exp, to_fixed,
-                     to_float, to_int, to_man_exp, to_rational, to_str)
+                     ftwo, fzero, mpf_abs, mpf_add, mpf_ceil, mpf_cmp, mpf_div,
+                     mpf_eq, mpf_floor, mpf_frac, mpf_frexp, mpf_ge, mpf_gt,
+                     mpf_hash, mpf_hypot, mpf_le, mpf_lt, mpf_mod, mpf_mul,
+                     mpf_mul_int, mpf_neg, mpf_nint, mpf_perturb, mpf_pos,
+                     mpf_pow_int, mpf_rand, mpf_rdiv_int, mpf_shift, mpf_sign,
+                     mpf_sqrt, mpf_sub, mpf_sum, normalize, prec_to_dps,
+                     repr_dps, round_ceiling, round_down, round_floor,
+                     round_int, round_nearest, round_up, str_to_man_exp,
+                     to_digits_exp, to_fixed, to_float, to_int, to_man_exp,
+                     to_rational, to_str)
 from .libmpi import (mpci_abs, mpci_add, mpci_cos, mpci_div, mpci_exp,
                      mpci_factorial, mpci_gamma, mpci_log, mpci_loggamma,
                      mpci_mul, mpci_neg, mpci_pos, mpci_pow, mpci_rgamma,
diff --git a/mpmath/libmp/backend.py b/mpmath/libmp/backend.py
index 235d5ec..cd370e9 100644
--- a/mpmath/libmp/backend.py
+++ b/mpmath/libmp/backend.py
@@ -24,8 +24,6 @@
 BACKEND = 'python'
 MPZ = int
 MPQ = Fraction
-HASH_MODULUS = sys.hash_info.modulus
-HASH_BITS = 31 if sys.hash_info.width == 32 else 61
 
 
 if 'MPMATH_NOGMPY' not in os.environ:
diff --git a/mpmath/libmp/gammazeta.py b/mpmath/libmp/gammazeta.py
index 8e3ca4a..47db9b1 100644
--- a/mpmath/libmp/gammazeta.py
+++ b/mpmath/libmp/gammazeta.py
@@ -16,29 +16,26 @@
 import math
 import sys
 
-from .backend import MPZ, MPZ_ONE, MPZ_THREE, MPZ_ZERO, gmpy
+from .backend import MPZ, MPZ_ONE, MPZ_THREE, MPZ_ZERO
 from .libelefun import (constant_memo, cos_sin_fixed, def_mpf_constant,
                         exp_fixed, ln2_fixed, ln_sqrt2pi_fixed, log_int_fixed,
-                        mpf_cos_pi, mpf_cos_sin, mpf_cos_sin_pi, mpf_cosh,
-                        mpf_cosh_sinh, mpf_exp, mpf_ln2, mpf_ln_sqrt2pi,
-                        mpf_log, mpf_pi, mpf_pow, mpf_sin_pi, mpf_sqrtpi,
-                        pi_fixed, sqrtpi_fixed)
-from .libintmath import ifac, ifac2, list_primes, moebius
+                        mpf_cos_sin_pi, mpf_exp, mpf_ln2, mpf_log, mpf_pi,
+                        mpf_pow, mpf_sin_pi, mpf_sqrtpi, pi_fixed,
+                        sqrtpi_fixed)
+from .libintmath import ifac, ifac2, isqrt_fast, list_primes, lshift, moebius
 from .libmpc import (mpc_abs, mpc_add, mpc_add_mpf, mpc_cos_pi, mpc_div,
                      mpc_div_mpf, mpc_exp, mpc_half, mpc_log, mpc_mpf_div,
                      mpc_mul, mpc_mul_int, mpc_mul_mpf, mpc_neg, mpc_one,
                      mpc_pos, mpc_pow, mpc_pow_int, mpc_reciprocal, mpc_shift,
                      mpc_sin_pi, mpc_square, mpc_sub, mpc_sub_mpf, mpc_two,
                      mpc_zero)
-from .libmpf import (ComplexResult, fhalf, finf, fnan, fninf, fnone, fone,
-                     from_int, from_man_exp, from_rational, ftwo, fzero,
-                     isqrt_fast, lshift, mpf_abs, mpf_add, mpf_div, mpf_floor,
-                     mpf_gt, mpf_le, mpf_lt, mpf_mul, mpf_mul_int, mpf_neg,
-                     mpf_perturb, mpf_pos, mpf_pow_int, mpf_rdiv_int,
-                     mpf_shift, mpf_sign, mpf_sqrt, mpf_sub, negative_rnd,
-                     reciprocal_rnd, round_ceiling, round_down, round_fast,
-                     round_floor, round_nearest, round_up, sqrt_fixed,
-                     to_fixed, to_float, to_int)
+from .libmpf import (ComplexResult, fhalf, finf, fnan, fninf, fone, from_int,
+                     from_man_exp, from_rational, ftwo, fzero, mpf_abs,
+                     mpf_add, mpf_div, mpf_floor, mpf_gt, mpf_le, mpf_lt,
+                     mpf_mul, mpf_mul_int, mpf_neg, mpf_perturb, mpf_pos,
+                     mpf_pow_int, mpf_rdiv_int, mpf_shift, mpf_sign, mpf_sub,
+                     negative_rnd, round_fast, round_nearest, to_fixed,
+                     to_float, to_int)
 
 
 # Catalan's constant is computed using Lupas's rapidly convergent series
@@ -380,7 +377,7 @@ def bernoulli_size(n):
 
 BERNOULLI_PREC_CUTOFF = bernoulli_size(MAX_BERNOULLI_CACHE)
 
-def mpf_bernoulli(n, prec, rnd=None, plus=False):
+def mpf_bernoulli(n, prec, rnd=round_fast, plus=False):
     """Computation of Bernoulli numbers (numerically)"""
     if n < 2:
         if n < 0:
@@ -398,7 +395,7 @@ def mpf_bernoulli(n, prec, rnd=None, plus=False):
     # convert the fraction back to an mpf value at the original precision
     if prec > BERNOULLI_PREC_CUTOFF and prec > bernoulli_size(n)*1.1 + 1000:
         p, q = bernfrac(n)
-        return from_rational(p, q, prec, rnd or round_floor)
+        return from_rational(p, q, prec, rnd)
     if n > MAX_BERNOULLI_CACHE:
         return mpf_bernoulli_huge(n, prec, rnd)
     wp = prec + 30
@@ -408,8 +405,6 @@ def mpf_bernoulli(n, prec, rnd=None, plus=False):
     if cached:
         numbers, state = cached
         if n in numbers:
-            if not rnd:
-                return numbers[n]
             return mpf_pos(numbers[n], prec, rnd)
         m, bin, bin1 = state
         if n - m > 10:
@@ -455,7 +450,7 @@ def mpf_bernoulli(n, prec, rnd=None, plus=False):
         state[:] = [m, bin, bin1]
     return numbers[n]
 
-def mpf_bernoulli_huge(n, prec, rnd=None):
+def mpf_bernoulli_huge(n, prec, rnd=round_fast):
     wp = prec + 10
     piprec = wp + int(math.log(n,2))
     v = mpf_gamma_int(n+1, wp)
@@ -464,7 +459,7 @@ def mpf_bernoulli_huge(n, prec, rnd=None):
     v = mpf_shift(v, 1-n)
     if not n & 3:
         v = mpf_neg(v)
-    return mpf_pos(v, prec, rnd or round_fast)
+    return mpf_pos(v, prec, rnd)
 
 def bernfrac(n, plus=False):
     r"""
@@ -1683,7 +1678,7 @@ def complex_stirling_series(x, y, prec):
     return sre, sim
 
 
-def mpf_gamma(x, prec, rnd='d', type=0):
+def mpf_gamma(x, prec, rnd=round_fast, type=0):
     """
     This function implements multipurpose evaluation of the gamma
     function, G(x), as well as the following versions of the same:
@@ -1889,7 +1884,7 @@ def mpf_gamma(x, prec, rnd='d', type=0):
             return mpf_pos(w, prec, rnd)
 
 
-def mpc_gamma(z, prec, rnd='d', type=0):
+def mpc_gamma(z, prec, rnd=round_fast, type=0):
     a, b = z
     asign, aman, aexp, abc = a
     bsign, bman, bexp, bbc = b
@@ -2123,25 +2118,25 @@ def mpc_gamma(z, prec, rnd='d', type=0):
         if type == 3:
             return mpc_pos(y, prec, rnd)
 
-def mpf_factorial(x, prec, rnd='d'):
+def mpf_factorial(x, prec, rnd=round_fast):
     return mpf_gamma(x, prec, rnd, 1)
 
-def mpc_factorial(x, prec, rnd='d'):
+def mpc_factorial(x, prec, rnd=round_fast):
     return mpc_gamma(x, prec, rnd, 1)
 
-def mpf_rgamma(x, prec, rnd='d'):
+def mpf_rgamma(x, prec, rnd=round_fast):
     return mpf_gamma(x, prec, rnd, 2)
 
-def mpc_rgamma(x, prec, rnd='d'):
+def mpc_rgamma(x, prec, rnd=round_fast):
     return mpc_gamma(x, prec, rnd, 2)
 
-def mpf_loggamma(x, prec, rnd='d'):
+def mpf_loggamma(x, prec, rnd=round_fast):
     sign, man, exp, bc = x
     if sign:
         raise ComplexResult
     return mpf_gamma(x, prec, rnd, 3)
 
-def mpc_loggamma(z, prec, rnd='d'):
+def mpc_loggamma(z, prec, rnd=round_fast):
     a, b = z
     asign, aman, aexp, abc = a
     bsign, bman, bexp, bbc = b
diff --git a/mpmath/libmp/libelefun.py b/mpmath/libmp/libelefun.py
index dc193aa..6ce43d9 100644
--- a/mpmath/libmp/libelefun.py
+++ b/mpmath/libmp/libelefun.py
@@ -10,19 +10,17 @@
 """
 
 import math
-from bisect import bisect
 
 from .backend import BACKEND, MPZ, MPZ_FIVE, MPZ_ONE, MPZ_TWO, MPZ_ZERO
-from .libintmath import ifib
-from .libmpf import (ComplexResult, bctable, fhalf, finf, fnan, fninf, fnone,
-                     fone, from_float, from_int, from_man_exp, from_rational,
-                     fzero, giant_steps, isqrt_fast, lshift, mpf_abs, mpf_add,
-                     mpf_cmp, mpf_div, mpf_mul, mpf_mul_int, mpf_neg,
+from .libintmath import (giant_steps, ifib, isqrt_fast, lshift, rshift,
+                         sqrt_fixed)
+from .libmpf import (ComplexResult, bctable, finf, fnan, fninf, fnone, fone,
+                     from_int, from_man_exp, from_rational, fzero, mpf_abs,
+                     mpf_add, mpf_cmp, mpf_div, mpf_mul, mpf_mul_int, mpf_neg,
                      mpf_perturb, mpf_pos, mpf_pow_int, mpf_rdiv_int,
                      mpf_shift, mpf_sign, mpf_sqrt, mpf_sub, negative_rnd,
-                     normalize, reciprocal_rnd, round_ceiling, round_down,
-                     round_fast, round_floor, round_nearest, round_up, rshift,
-                     sqrt_fixed, to_fixed, to_float, to_int)
+                     normalize, reciprocal_rnd, round_ceiling, round_fast,
+                     round_up, to_fixed, to_int)
 
 
 #-------------------------------------------------------------------------------
diff --git a/mpmath/libmp/libhyper.py b/mpmath/libmp/libhyper.py
index 837705d..019c090 100644
--- a/mpmath/libmp/libhyper.py
+++ b/mpmath/libmp/libhyper.py
@@ -6,24 +6,22 @@
 """
 
 import math
-import operator
 
 from .backend import MPZ_ONE, MPZ_ZERO
 from .gammazeta import euler_fixed, mpf_euler, mpf_gamma_int
-from .libelefun import (agm_fixed, mpf_cos, mpf_cos_sin, mpf_exp, mpf_log,
-                        mpf_pi, mpf_sin, mpf_sqrt, pi_fixed)
-from .libintmath import ifac
+from .libelefun import (agm_fixed, mpf_cos_sin, mpf_exp, mpf_log, mpf_pi,
+                        mpf_sin, mpf_sqrt, pi_fixed)
+from .libintmath import ifac, sqrt_fixed
 from .libmpc import (complex_int_pow, mpc_abs, mpc_add, mpc_add_mpf, mpc_div,
                      mpc_exp, mpc_is_infnan, mpc_log, mpc_mpf_div, mpc_mul,
-                     mpc_mul_mpf, mpc_neg, mpc_one, mpc_pos, mpc_shift,
-                     mpc_sqrt, mpc_square, mpc_sub, mpc_sub_mpf, mpc_zero)
+                     mpc_neg, mpc_one, mpc_pos, mpc_shift, mpc_sqrt, mpc_sub,
+                     mpc_zero)
 from .libmpf import (ComplexResult, finf, fnan, fninf, fnone, fone, from_int,
                      from_man_exp, from_rational, ftwo, fzero, mpf_abs,
-                     mpf_add, mpf_cmp, mpf_div, mpf_gt, mpf_le, mpf_lt,
-                     mpf_min_max, mpf_mul, mpf_neg, mpf_perturb, mpf_pos,
-                     mpf_pow_int, mpf_rdiv_int, mpf_shift, mpf_sign, mpf_sqrt,
-                     mpf_sub, negative_rnd, round_fast, round_nearest,
-                     sqrt_fixed, to_fixed, to_int, to_rational)
+                     mpf_add, mpf_div, mpf_le, mpf_lt, mpf_min_max, mpf_mul,
+                     mpf_neg, mpf_perturb, mpf_pos, mpf_pow_int, mpf_shift,
+                     mpf_sign, mpf_sqrt, mpf_sub, negative_rnd, round_fast,
+                     to_fixed, to_int)
 
 
 class NoConvergence(Exception):
diff --git a/mpmath/libmp/libintmath.py b/mpmath/libmp/libintmath.py
index 25d5bdb..c61bce5 100644
--- a/mpmath/libmp/libintmath.py
+++ b/mpmath/libmp/libintmath.py
@@ -8,7 +8,6 @@
 
 import math
 import sys
-from bisect import bisect
 from functools import lru_cache
 
 from .backend import BACKEND, MPZ, MPZ_ONE, MPZ_ZERO, gmpy
diff --git a/mpmath/libmp/libmpc.py b/mpmath/libmp/libmpc.py
index cfa7df7..32eb87d 100644
--- a/mpmath/libmp/libmpc.py
+++ b/mpmath/libmp/libmpc.py
@@ -4,21 +4,20 @@
 
 import sys
 
-from .backend import MPZ, MPZ_ONE, MPZ_TWO, MPZ_ZERO
+from .backend import MPZ
 from .libelefun import (mpf_acos, mpf_acosh, mpf_asin, mpf_atan, mpf_atan2,
                         mpf_cos, mpf_cos_pi, mpf_cos_sin, mpf_cos_sin_pi,
                         mpf_cosh, mpf_cosh_sinh, mpf_exp, mpf_fibonacci,
                         mpf_log, mpf_log_hypot, mpf_nthroot, mpf_phi, mpf_pi,
                         mpf_pow_int, mpf_sin, mpf_sin_pi, mpf_sinh, mpf_tan,
                         mpf_tanh)
-from .libmpf import (ComplexResult, bctable, fhalf, finf, fnan, fninf, fnone,
-                     fone, from_float, from_int, from_man_exp, ftwo, fzero,
-                     giant_steps, lshift, mpf_abs, mpf_add, mpf_ceil, mpf_div,
-                     mpf_floor, mpf_frac, mpf_hash, mpf_hypot, mpf_mul,
-                     mpf_mul_int, mpf_neg, mpf_nint, mpf_pos, mpf_rdiv_int,
-                     mpf_shift, mpf_sign, mpf_sqrt, mpf_sub, negative_rnd,
-                     normalize, reciprocal_rnd, round_ceiling, round_down,
-                     round_fast, round_floor, round_nearest, round_up, rshift,
+from .libintmath import giant_steps, lshift, rshift
+from .libmpf import (ComplexResult, fhalf, finf, fnan, fninf, fnone, fone,
+                     from_float, from_int, from_man_exp, ftwo, fzero, mpf_abs,
+                     mpf_add, mpf_ceil, mpf_div, mpf_floor, mpf_frac, mpf_hash,
+                     mpf_hypot, mpf_mul, mpf_mul_int, mpf_neg, mpf_nint,
+                     mpf_pos, mpf_rdiv_int, mpf_shift, mpf_sqrt, mpf_sub,
+                     normalize, reciprocal_rnd, round_fast, round_floor,
                      to_fixed, to_float, to_int, to_str)
 
 
@@ -93,7 +92,7 @@ def mpc_pos(z, prec, rnd=round_fast):
     a, b = z
     return mpf_pos(a, prec, rnd), mpf_pos(b, prec, rnd)
 
-def mpc_neg(z, prec=None, rnd=round_fast):
+def mpc_neg(z, prec=0, rnd=round_fast):
     a, b = z
     return mpf_neg(a, prec, rnd), mpf_neg(b, prec, rnd)
 
@@ -781,10 +780,10 @@ def mpc_fibonacci(z, prec, rnd=round_fast):
     u = mpc_div_mpf(u, b, prec, rnd)
     return u
 
-def mpf_expj(x, prec, rnd='f'):
+def mpf_expj(x, prec, rnd=round_floor):
     raise ComplexResult
 
-def mpc_expj(z, prec, rnd='f'):
+def mpc_expj(z, prec, rnd=round_floor):
     re, im = z
     if im == fzero:
         return mpf_cos_sin(re, prec, rnd)
@@ -796,10 +795,10 @@ def mpc_expj(z, prec, rnd='f'):
     im = mpf_mul(ey, s, prec, rnd)
     return re, im
 
-def mpf_expjpi(x, prec, rnd='f'):
+def mpf_expjpi(x, prec, rnd=round_floor):
     raise ComplexResult
 
-def mpc_expjpi(z, prec, rnd='f'):
+def mpc_expjpi(z, prec, rnd=round_floor):
     re, im = z
     if im == fzero:
         return mpf_cos_sin_pi(re, prec, rnd)
diff --git a/mpmath/libmp/libmpf.py b/mpmath/libmp/libmpf.py
index 7ab9cd3..04a12a6 100644
--- a/mpmath/libmp/libmpf.py
+++ b/mpmath/libmp/libmpf.py
@@ -4,18 +4,15 @@
 
 import math
 import sys
-from bisect import bisect
 
 
 # Importing random is slow
 #from random import getrandbits
 getrandbits = None
 
-from .backend import (BACKEND, HASH_BITS, HASH_MODULUS, MPZ, MPZ_FIVE, MPZ_ONE,
-                      MPZ_TWO, MPZ_ZERO, gmpy)
-from .libintmath import (bctable, bin_to_radix, giant_steps, isqrt, isqrt_fast,
-                         lshift, numeral, rshift, sqrt_fixed, sqrtrem,
-                         stddigits, trailing, trailtable)
+from .backend import BACKEND, MPZ, MPZ_FIVE, MPZ_ONE, MPZ_ZERO, gmpy
+from .libintmath import (bctable, bin_to_radix, isqrt, numeral, sqrtrem,
+                         stddigits, trailtable)
 
 
 class ComplexResult(ValueError):
@@ -67,7 +64,7 @@ def repr_dps(n):
 finf = (0, MPZ_ZERO, -456, -2)
 fninf = (1, MPZ_ZERO, -789, -3)
 
-math_float_inf = 1e1000
+math_float_inf = math.inf
 
 
 #----------------------------------------------------------------------------#
@@ -194,7 +191,7 @@ def normalize(sign, man, exp, bc, prec, rnd):
 #                            Conversion functions                            #
 #----------------------------------------------------------------------------#
 
-def from_man_exp(man, exp, prec=None, rnd=round_fast):
+def from_man_exp(man, exp, prec=0, rnd=round_fast):
     """Create raw mpf from (man, exp) pair. The mantissa may be signed.
     If no precision is specified, the mantissa is stored exactly."""
     man = MPZ(man)
@@ -245,7 +242,7 @@ def to_man_exp(s):
         raise ValueError("mantissa and exponent are undefined for %s" % man)
     return man, exp
 
-def to_int(s, rnd=None):
+def to_int(s, rnd=round_fast):
     """Convert a raw mpf to the nearest int. Rounding is done down by
     default (same as int(float) in Python), but can be changed. If the
     input is inf/nan, an exception is raised."""
@@ -257,7 +254,7 @@ def to_int(s, rnd=None):
             return (-man) << exp
         return man << exp
     # Make default rounding fast
-    if not rnd:
+    if rnd == round_fast:
         if sign:
             return -(man >> (-exp))
         else:
@@ -331,18 +328,16 @@ def from_npfloat(x, prec=113, rnd=round_fast):
     import numpy as np
     if np.isfinite(x):
         m, e = np.frexp(x)
-        return from_man_exp(int(np.ldexp(m, 113)), e-113, prec, rnd)
-    if np.isposinf(x): return finf
-    if np.isneginf(x): return fninf
+        return from_man_exp(int(np.ldexp(m, 113)), int(e)-113, prec, rnd)
     return fnan
 
-def from_Decimal(x, prec=None, rnd=round_fast):
+def from_Decimal(x, prec=0, rnd=round_fast):
     """Create a raw mpf from a Decimal, rounding if necessary.
     If prec is not specified, use the equivalent bit precision
     of the number of significant digits in x."""
     if x.is_nan(): return fnan
     if x.is_infinite(): return fninf if x.is_signed() else finf
-    if prec is None:
+    if not prec:
         prec = int(len(x.as_tuple()[1])*3.3219280948873626)
     return from_str(str(x), prec, rnd)
 
@@ -440,20 +435,27 @@ def mpf_eq(s, t):
     return s == t
 
 def mpf_hash(s):
-    # Duplicate the new hash algorithm introduces in Python 3.2.
+    # Duplicate the new hash algorithm, introduced in Python 3.2.
     ssign, sman, sexp, sbc = s
 
     # Handle special numbers
     if not sman:
-        if s == fnan: return sys.hash_info.nan
+        if s == fnan:
+            if sys.version_info >= (3, 10):
+                return object.__hash__(s)
+            else:
+                return sys.hash_info.nan
         if s == finf: return sys.hash_info.inf
         if s == fninf: return -sys.hash_info.inf
-    h = sman % HASH_MODULUS
+
+    hash_modulus = sys.hash_info.modulus
+    hash_bits = 31 if sys.hash_info.width == 32 else 61
+    h = sman % hash_modulus
     if sexp >= 0:
-        sexp = sexp % HASH_BITS
+        sexp = sexp % hash_bits
     else:
-        sexp = HASH_BITS - 1 - ((-1 - sexp) % HASH_BITS)
-    h = (h << sexp) % HASH_MODULUS
+        sexp = hash_bits - 1 - ((-1 - sexp) % hash_bits)
+    h = (h << sexp) % hash_modulus
     if ssign: h = -h
     if h == -1: h = -2
     return int(h)
@@ -547,7 +549,7 @@ def mpf_pos(s, prec=0, rnd=round_fast):
         return normalize(sign, man, exp, bc, prec, rnd)
     return s
 
-def mpf_neg(s, prec=None, rnd=round_fast):
+def mpf_neg(s, prec=0, rnd=round_fast):
     """Negate a raw mpf (return -s), rounding the result to the
     specified precision. The prec argument can be omitted to do the
     operation exactly."""
@@ -561,7 +563,7 @@ def mpf_neg(s, prec=None, rnd=round_fast):
         return (1-sign, man, exp, bc)
     return normalize(1-sign, man, exp, bc, prec, rnd)
 
-def mpf_abs(s, prec=None, rnd=round_fast):
+def mpf_abs(s, prec=0, rnd=round_fast):
     """Return abs(s) of the raw mpf s, rounded to the specified
     precision. The prec argument can be omitted to generate an
     exact result."""
@@ -1234,7 +1236,8 @@ def str_to_man_exp(x, base=10):
         sys.set_int_max_str_digits(int_max_str_digits)
     return x, exp
 
-special_str = {'inf':finf, '+inf':finf, '-inf':fninf, 'nan':fnan}
+special_str = {'inf':finf, '+inf':finf, '-inf':fninf, 'nan':fnan,
+               'oo':finf, '+oo':finf, '-oo':fninf}
 
 def from_str(x, prec=0, rnd=round_fast, base=0):
     """Create a raw mpf from a string x in a given base, rounding in the
diff --git a/mpmath/libmp/libmpi.py b/mpmath/libmp/libmpi.py
index 7c7772a..393d02e 100644
--- a/mpmath/libmp/libmpi.py
+++ b/mpmath/libmp/libmpi.py
@@ -5,14 +5,12 @@
 from .gammazeta import mpc_loggamma, mpf_gamma, mpf_loggamma, mpf_rgamma
 from .libelefun import (mod_pi2, mpf_atan, mpf_atan2, mpf_cos_sin, mpf_exp,
                         mpf_log, mpf_pi, mpf_sqrt)
-from .libmpf import (MPZ_ONE, ComplexResult, dps_to_prec, fhalf, finf, fnan,
-                     fninf, fnone, fone, from_float, from_int, from_man_exp,
-                     from_str, fzero, mpf_abs, mpf_add, mpf_cmp, mpf_div,
-                     mpf_eq, mpf_floor, mpf_ge, mpf_gt, mpf_le, mpf_lt,
-                     mpf_min_max, mpf_mul, mpf_mul_int, mpf_neg, mpf_pos,
-                     mpf_pow_int, mpf_shift, mpf_sign, mpf_sub, prec_to_dps,
-                     repr_dps, round_ceiling, round_down, round_floor,
-                     round_nearest, round_up, to_int, to_str)
+from .libmpf import (MPZ_ONE, dps_to_prec, fhalf, finf, fnan, fninf, fnone,
+                     fone, from_float, from_int, from_man_exp, from_str, fzero,
+                     mpf_abs, mpf_add, mpf_div, mpf_ge, mpf_gt, mpf_le, mpf_lt,
+                     mpf_min_max, mpf_mul, mpf_neg, mpf_pos, mpf_pow_int,
+                     mpf_shift, mpf_sign, mpf_sub, prec_to_dps, round_ceiling,
+                     round_floor, round_nearest, round_up, to_int, to_str)
 
 
 def mpi_str(s, prec):
diff --git a/pyproject.toml b/pyproject.toml
index 081b032..c280b2b 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -54,6 +54,8 @@ testpaths = ['mpmath', 'docs']
 doctest_optionflags = ['IGNORE_EXCEPTION_DETAIL', 'ELLIPSIS']
 addopts = "--durations=20 --doctest-modules --doctest-glob='*.rst'"
 norecursedirs = ['docs/plots', 'demo', '.eggs', '.git']
+filterwarnings = ['error::DeprecationWarning']
+xfail_strict = true
 [tool.coverage.run]
 branch = true
 omit = ['mpmath/tests/*']


Test patch:
diff --git a/mpmath/tests/test_basic_ops.py b/mpmath/tests/test_basic_ops.py
index 5f15fef..c5675cd 100644
--- a/mpmath/tests/test_basic_ops.py
+++ b/mpmath/tests/test_basic_ops.py
@@ -155,10 +155,19 @@ def _mpf_(self):
     assert mpf(mympf()) == mpf(3.5)
     assert mympf() - mpf(0.5) == mpf(3.0)
     assert mpf(decimal.Decimal('1.5')) == mpf('1.5')
+    assert mpf(decimal.Decimal('+inf')) == +inf
+    assert mpf(decimal.Decimal('-inf')) == -inf
+    assert isnan(mpf(decimal.Decimal('nan')))
+    assert mpf(decimal.Decimal(1).exp(), dps=5) == mpf('2.7182807922363281', dps=5)
+    assert mpf(decimal.Decimal(1).exp(), prec=0) == mpf('2.718281828459045235360287471', prec=93)
     assert mpf('0x1.4ace478p+33') == mpf(11100000000.0)
     assert mpf('0x1.4ace478p+33', base=0) == mpf(11100000000.0)
     assert mpf('1.4ace478p+33', base=16) == mpf(11100000000.0)
 
+    assert mpf(float('+inf')) == +inf
+    assert mpf(float('-inf')) == -inf
+    assert isnan(mpf(float('nan')))
+
 def test_mpc_init():
     class mympc:
         @property
@@ -171,6 +180,8 @@ def _mpc_(self):
 def test_mpf_props():
     a = mpf(0.5)
     assert a.man_exp == (1, -1)
+    pytest.raises(ValueError, lambda: inf.man_exp)
+    pytest.raises(ValueError, lambda: nan.man_exp)
     assert a.man == 1
     assert a.exp == -1
     assert a.bc == 1
@@ -203,6 +214,7 @@ def test_hash():
     assert hash(mpc(2,3)) == hash(2+3j)
     # Check that this doesn't fail
     assert hash(inf)
+    hash(nan)
     # Check that overflow doesn't assign equal hashes to large numbers
     assert hash(mpf('1e1000')) != hash('1e10000')
     assert hash(mpc(100,'1e1000')) != hash(mpc(200,'1e1000'))
diff --git a/mpmath/tests/test_convert.py b/mpmath/tests/test_convert.py
index 6c09398..0d71596 100644
--- a/mpmath/tests/test_convert.py
+++ b/mpmath/tests/test_convert.py
@@ -5,7 +5,7 @@
 
 import pytest
 
-from mpmath import iv, mp, mpc, mpf, mpi, mpmathify, sqrt
+from mpmath import inf, isnan, iv, mp, mpc, mpf, mpi, mpmathify, sqrt
 from mpmath.libmp import (fhalf, from_float, from_rational, from_str,
                           round_ceiling, round_floor, round_nearest,
                           to_rational, to_str)
@@ -224,6 +224,12 @@ def test_mpmathify():
     assert mpmathify('(1.0+1.0j)') == mpc(1, 1)
     assert mpmathify('(1.2e-10 - 3.4e5j)') == mpc('1.2e-10', '-3.4e5')
     assert mpmathify('1j') == mpc(1j)
+    assert mpmathify('oo') == mpf('inf')
+    assert mpmathify('+oo') == mpf('inf')
+    assert mpmathify('-oo') == mpf('-inf')
+    assert mpmathify('2+3*I') == mpc(2, 3)
+    assert mpmathify('2+3I') == mpc(2, 3)
+    assert mpmathify('2/3 + 4/5j') == mpc(2/3, 4/5)
 
 def test_issue548():
     try:
@@ -249,6 +255,12 @@ def test_compatibility():
         try: diff = np.abs(type(np.sqrt(x))(sqrt(x)) - np.sqrt(x))
         except: continue
         assert diff < 2.0**-53
+    assert mpf(np.float64('inf')) == inf
+    assert isnan(mp.npconvert(np.float64('nan')))
+    mp.prec = 113
+    assert (mp.npconvert(np.float128('0.841470984807896506652502321630298954')) ==
+            mpf('0.841470984807896506664590813295845351'))
+    mp.prec = 53
     # issues 382 and 539
     assert mp.sqrt(np.int64(1)) == mpf('1.0')
     assert mpf(np.int64(1)) == mpf('1.0')