Spaces:
Sleeping
Sleeping
File size: 9,743 Bytes
6a86ad5 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 |
import os
from ctypes import c_long, sizeof
from functools import reduce
from typing import Tuple as tTuple, Type
from warnings import warn
from sympy.external import import_module
from .pythonmpq import PythonMPQ
from .ntheory import (
bit_scan1 as python_bit_scan1,
bit_scan0 as python_bit_scan0,
remove as python_remove,
factorial as python_factorial,
sqrt as python_sqrt,
sqrtrem as python_sqrtrem,
gcd as python_gcd,
lcm as python_lcm,
gcdext as python_gcdext,
is_square as python_is_square,
invert as python_invert,
legendre as python_legendre,
jacobi as python_jacobi,
kronecker as python_kronecker,
iroot as python_iroot,
is_fermat_prp as python_is_fermat_prp,
is_euler_prp as python_is_euler_prp,
is_strong_prp as python_is_strong_prp,
is_fibonacci_prp as python_is_fibonacci_prp,
is_lucas_prp as python_is_lucas_prp,
is_selfridge_prp as python_is_selfridge_prp,
is_strong_lucas_prp as python_is_strong_lucas_prp,
is_strong_selfridge_prp as python_is_strong_selfridge_prp,
is_bpsw_prp as python_is_bpsw_prp,
is_strong_bpsw_prp as python_is_strong_bpsw_prp,
)
__all__ = [
# GROUND_TYPES is either 'gmpy' or 'python' depending on which is used. If
# gmpy is installed then it will be used unless the environment variable
# SYMPY_GROUND_TYPES is set to something other than 'auto', 'gmpy', or
# 'gmpy2'.
'GROUND_TYPES',
# If HAS_GMPY is 0, no supported version of gmpy is available. Otherwise,
# HAS_GMPY will be 2 for gmpy2 if GROUND_TYPES is 'gmpy'. It used to be
# possible for HAS_GMPY to be 1 for gmpy but gmpy is no longer supported.
'HAS_GMPY',
# SYMPY_INTS is a tuple containing the base types for valid integer types.
# This is either (int,) or (int, type(mpz(0))) depending on GROUND_TYPES.
'SYMPY_INTS',
# MPQ is either gmpy.mpq or the Python equivalent from
# sympy.external.pythonmpq
'MPQ',
# MPZ is either gmpy.mpz or int.
'MPZ',
'bit_scan1',
'bit_scan0',
'remove',
'factorial',
'sqrt',
'is_square',
'sqrtrem',
'gcd',
'lcm',
'gcdext',
'invert',
'legendre',
'jacobi',
'kronecker',
'iroot',
'is_fermat_prp',
'is_euler_prp',
'is_strong_prp',
'is_fibonacci_prp',
'is_lucas_prp',
'is_selfridge_prp',
'is_strong_lucas_prp',
'is_strong_selfridge_prp',
'is_bpsw_prp',
'is_strong_bpsw_prp',
]
#
# Tested python-flint version. Future versions might work but we will only use
# them if explicitly requested by SYMPY_GROUND_TYPES=flint.
#
_PYTHON_FLINT_VERSION_NEEDED = ["0.6", "0.7", "0.8", "0.9"]
def _flint_version_okay(flint_version):
major, minor = flint_version.split('.')[:2]
flint_ver = f'{major}.{minor}'
return flint_ver in _PYTHON_FLINT_VERSION_NEEDED
#
# We will only use gmpy2 >= 2.0.0
#
_GMPY2_MIN_VERSION = '2.0.0'
def _get_flint(sympy_ground_types):
if sympy_ground_types not in ('auto', 'flint'):
return None
try:
import flint
# Earlier versions of python-flint may not have __version__.
from flint import __version__ as _flint_version
except ImportError:
if sympy_ground_types == 'flint':
warn("SYMPY_GROUND_TYPES was set to flint but python-flint is not "
"installed. Falling back to other ground types.")
return None
if _flint_version_okay(_flint_version):
return flint
elif sympy_ground_types == 'auto':
return None
else:
warn(f"Using python-flint {_flint_version} because SYMPY_GROUND_TYPES "
f"is set to flint but this version of SymPy is only tested "
f"with python-flint versions {_PYTHON_FLINT_VERSION_NEEDED}.")
return flint
def _get_gmpy2(sympy_ground_types):
if sympy_ground_types not in ('auto', 'gmpy', 'gmpy2'):
return None
gmpy = import_module('gmpy2', min_module_version=_GMPY2_MIN_VERSION,
module_version_attr='version', module_version_attr_call_args=())
if sympy_ground_types != 'auto' and gmpy is None:
warn("gmpy2 library is not installed, switching to 'python' ground types")
return gmpy
#
# SYMPY_GROUND_TYPES can be flint, gmpy, gmpy2, python or auto (default)
#
_SYMPY_GROUND_TYPES = os.environ.get('SYMPY_GROUND_TYPES', 'auto').lower()
_flint = None
_gmpy = None
#
# First handle auto-detection of flint/gmpy2. We will prefer flint if available
# or otherwise gmpy2 if available and then lastly the python types.
#
if _SYMPY_GROUND_TYPES in ('auto', 'flint'):
_flint = _get_flint(_SYMPY_GROUND_TYPES)
if _flint is not None:
_SYMPY_GROUND_TYPES = 'flint'
else:
_SYMPY_GROUND_TYPES = 'auto'
if _SYMPY_GROUND_TYPES in ('auto', 'gmpy', 'gmpy2'):
_gmpy = _get_gmpy2(_SYMPY_GROUND_TYPES)
if _gmpy is not None:
_SYMPY_GROUND_TYPES = 'gmpy'
else:
_SYMPY_GROUND_TYPES = 'python'
if _SYMPY_GROUND_TYPES not in ('flint', 'gmpy', 'python'):
warn("SYMPY_GROUND_TYPES environment variable unrecognised. "
"Should be 'auto', 'flint', 'gmpy', 'gmpy2' or 'python'.")
_SYMPY_GROUND_TYPES = 'python'
#
# At this point _SYMPY_GROUND_TYPES is either flint, gmpy or python. The blocks
# below define the values exported by this module in each case.
#
#
# In gmpy2 and flint, there are functions that take a long (or unsigned long)
# argument. That is, it is not possible to input a value larger than that.
#
LONG_MAX = (1 << (8*sizeof(c_long) - 1)) - 1
#
# Type checkers are confused by what SYMPY_INTS is. There may be a better type
# hint for this like Type[Integral] or something.
#
SYMPY_INTS: tTuple[Type, ...]
if _SYMPY_GROUND_TYPES == 'gmpy':
assert _gmpy is not None
flint = None
gmpy = _gmpy
HAS_GMPY = 2
GROUND_TYPES = 'gmpy'
SYMPY_INTS = (int, type(gmpy.mpz(0)))
MPZ = gmpy.mpz
MPQ = gmpy.mpq
bit_scan1 = gmpy.bit_scan1
bit_scan0 = gmpy.bit_scan0
remove = gmpy.remove
factorial = gmpy.fac
sqrt = gmpy.isqrt
is_square = gmpy.is_square
sqrtrem = gmpy.isqrt_rem
gcd = gmpy.gcd
lcm = gmpy.lcm
gcdext = gmpy.gcdext
invert = gmpy.invert
legendre = gmpy.legendre
jacobi = gmpy.jacobi
kronecker = gmpy.kronecker
def iroot(x, n):
# In the latest gmpy2, the threshold for n is ULONG_MAX,
# but adjust to the older one.
if n <= LONG_MAX:
return gmpy.iroot(x, n)
return python_iroot(x, n)
is_fermat_prp = gmpy.is_fermat_prp
is_euler_prp = gmpy.is_euler_prp
is_strong_prp = gmpy.is_strong_prp
is_fibonacci_prp = gmpy.is_fibonacci_prp
is_lucas_prp = gmpy.is_lucas_prp
is_selfridge_prp = gmpy.is_selfridge_prp
is_strong_lucas_prp = gmpy.is_strong_lucas_prp
is_strong_selfridge_prp = gmpy.is_strong_selfridge_prp
is_bpsw_prp = gmpy.is_bpsw_prp
is_strong_bpsw_prp = gmpy.is_strong_bpsw_prp
elif _SYMPY_GROUND_TYPES == 'flint':
assert _flint is not None
flint = _flint
gmpy = None
HAS_GMPY = 0
GROUND_TYPES = 'flint'
SYMPY_INTS = (int, flint.fmpz) # type: ignore
MPZ = flint.fmpz # type: ignore
MPQ = flint.fmpq # type: ignore
bit_scan1 = python_bit_scan1
bit_scan0 = python_bit_scan0
remove = python_remove
factorial = python_factorial
def sqrt(x):
return flint.fmpz(x).isqrt()
def is_square(x):
if x < 0:
return False
return flint.fmpz(x).sqrtrem()[1] == 0
def sqrtrem(x):
return flint.fmpz(x).sqrtrem()
def gcd(*args):
return reduce(flint.fmpz.gcd, args, flint.fmpz(0))
def lcm(*args):
return reduce(flint.fmpz.lcm, args, flint.fmpz(1))
gcdext = python_gcdext
invert = python_invert
legendre = python_legendre
def jacobi(x, y):
if y <= 0 or not y % 2:
raise ValueError("y should be an odd positive integer")
return flint.fmpz(x).jacobi(y)
kronecker = python_kronecker
def iroot(x, n):
if n <= LONG_MAX:
y = flint.fmpz(x).root(n)
return y, y**n == x
return python_iroot(x, n)
is_fermat_prp = python_is_fermat_prp
is_euler_prp = python_is_euler_prp
is_strong_prp = python_is_strong_prp
is_fibonacci_prp = python_is_fibonacci_prp
is_lucas_prp = python_is_lucas_prp
is_selfridge_prp = python_is_selfridge_prp
is_strong_lucas_prp = python_is_strong_lucas_prp
is_strong_selfridge_prp = python_is_strong_selfridge_prp
is_bpsw_prp = python_is_bpsw_prp
is_strong_bpsw_prp = python_is_strong_bpsw_prp
elif _SYMPY_GROUND_TYPES == 'python':
flint = None
gmpy = None
HAS_GMPY = 0
GROUND_TYPES = 'python'
SYMPY_INTS = (int,)
MPZ = int
MPQ = PythonMPQ
bit_scan1 = python_bit_scan1
bit_scan0 = python_bit_scan0
remove = python_remove
factorial = python_factorial
sqrt = python_sqrt
is_square = python_is_square
sqrtrem = python_sqrtrem
gcd = python_gcd
lcm = python_lcm
gcdext = python_gcdext
invert = python_invert
legendre = python_legendre
jacobi = python_jacobi
kronecker = python_kronecker
iroot = python_iroot
is_fermat_prp = python_is_fermat_prp
is_euler_prp = python_is_euler_prp
is_strong_prp = python_is_strong_prp
is_fibonacci_prp = python_is_fibonacci_prp
is_lucas_prp = python_is_lucas_prp
is_selfridge_prp = python_is_selfridge_prp
is_strong_lucas_prp = python_is_strong_lucas_prp
is_strong_selfridge_prp = python_is_strong_selfridge_prp
is_bpsw_prp = python_is_bpsw_prp
is_strong_bpsw_prp = python_is_strong_bpsw_prp
else:
assert False
|