Skip to content

Commit

Permalink
Trac #18358: a cython function that produces long given python input
Browse files Browse the repository at this point in the history
I met twice the same problem which is the nedd for a standalone function
that given a python objects return an associated long (if it makes
sense).

The following is not safe
{{{
cdef long f(x):
    return x
}}}
since `f(2/3)` would return `0` (see e.g. #18278). Moreover, one could
expect something that would be very fast for both Python int and Sage
integers (see #18346).

URL: http://trac.sagemath.org/18358
Reported by: vdelecroix
Ticket author(s): Vincent Delecroix
Reviewer(s): Jeroen Demeyer
  • Loading branch information
Release Manager authored and vbraun committed May 14, 2015
2 parents c688c15 + 6b46fee commit 01432cf
Show file tree
Hide file tree
Showing 10 changed files with 93 additions and 29 deletions.
9 changes: 5 additions & 4 deletions src/sage/graphs/base/c_graph.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ method :meth:`realloc <sage.graphs.base.c_graph.CGraph.realloc>`.
include "sage/data_structures/bitset.pxi"

from sage.rings.integer cimport Integer
from sage.misc.long cimport pyobject_to_long

cdef class CGraph:
"""
Expand Down Expand Up @@ -1206,16 +1207,16 @@ cdef class CGraphBackend(GenericGraphBackend):
cdef dict vertex_ints = self.vertex_ints
cdef dict vertex_labels = self.vertex_labels
cdef CGraph G = self._cg
cdef int u_int
cdef long u_long
if u in vertex_ints:
return vertex_ints[u]
try:
u_int = u
u_long = pyobject_to_long(u)
except Exception:
return -1
if u_int < 0 or u_int >= G.active_vertices.size or u_int in vertex_labels or u_int != u:
if u_long < 0 or u_long >= G.active_vertices.size or u_long in vertex_labels:
return -1
return u_int
return u_long

cdef vertex_label(self, int u_int):
"""
Expand Down
9 changes: 8 additions & 1 deletion src/sage/libs/flint/fmpz_poly.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ AUTHORS:

include 'sage/ext/stdsage.pxi'

from sage.misc.long cimport pyobject_to_long

from cpython.sequence cimport *

from sage.structure.sage_object cimport SageObject
Expand Down Expand Up @@ -247,8 +249,13 @@ cdef class Fmpz_poly(SageObject):
1 1427247692705959881058285969449495136382746624
sage: 2^150
1427247692705959881058285969449495136382746624
sage: f**(3/2)
Traceback (most recent call last):
...
TypeError: rational is not an integer
"""
cdef long nn = n
cdef long nn = pyobject_to_long(n)
if not isinstance(self, Fmpz_poly):
raise TypeError
cdef Fmpz_poly res = <Fmpz_poly>Fmpz_poly.__new__(Fmpz_poly)
Expand Down
3 changes: 1 addition & 2 deletions src/sage/libs/ntl/ntl_GF2X.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -676,9 +676,8 @@ cdef class ntl_GF2X:
sage: int(e)
1
"""
cdef long l = 0
if GF2X_deg(self.x) != 0:
raise ValueError, "cannot convert non-constant polynomial to integer"
raise ValueError("cannot convert non-constant polynomial to integer")
else:
return GF2_conv_to_long(GF2X_coeff(self.x,0))

Expand Down
51 changes: 51 additions & 0 deletions src/sage/misc/long.pxd
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
r"""
Fast conversion macro to long
"""

from libc.limits cimport LONG_MIN

from cpython.int cimport PyInt_AS_LONG
from cpython.long cimport PyLong_AsLong
from cpython.number cimport PyNumber_Index

from sage.libs.gmp.mpz cimport mpz_fits_slong_p, mpz_get_si

from sage.rings.integer cimport Integer

cdef inline long pyobject_to_long(x) except? LONG_MIN:
r"""
Given a Python object ``x`` cast it quickly to a C long.
A ``TypeError`` is raised if the input can not be converted to an integer or
an ``OverflowError`` is raised if it does not fit into a C long.
TESTS:
We test indirectly that ``Integer.__pow__`` works::
sage: a = 10
sage: a^10
10000000000
sage: a^(10r)
10000000000
sage: a^(10l)
10000000000
sage: a^(10/1)
10000000000
sage: a^(2**258)
Traceback (most recent call last):
...
RuntimeError: exponent must be at most 2147483647 # 32-bit
RuntimeError: exponent must be at most 9223372036854775807 # 64-bit
"""
if isinstance(x, int):
return PyInt_AS_LONG(x)
elif type(x) is Integer:
if mpz_fits_slong_p((<Integer>x).value):
return mpz_get_si((<Integer>x).value)
else:
raise OverflowError("Sage Integer too large to convert to C long")
elif isinstance(x, long):
return PyLong_AsLong(x)

return PyNumber_Index(x)
7 changes: 5 additions & 2 deletions src/sage/rings/integer.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ import operator
import sys

from sage.misc.superseded import deprecated_function_alias
from sage.misc.long cimport pyobject_to_long

include "sage/ext/interrupt.pxi" # ctrl-c interrupt block support
include "sage/ext/stdsage.pxi"
Expand Down Expand Up @@ -1892,7 +1893,9 @@ cdef class Integer(sage.structure.element.EuclideanDomainElement):
sage: 2^t
Traceback (most recent call last):
...
TypeError: non-integral exponents not supported
TypeError:
'sage.rings.polynomial.polynomial_rational_flint.Polynomial_rational_flint'
object cannot be interpreted as an index
sage: int(3)^-3
1/27
sage: type(int(3)^2)
Expand All @@ -1915,7 +1918,7 @@ cdef class Integer(sage.structure.element.EuclideanDomainElement):
cdef long nn

try:
nn = PyNumber_Index(n)
nn = pyobject_to_long(n)
except TypeError:
s = parent_c(n)(self)
return s**n
Expand Down
15 changes: 10 additions & 5 deletions src/sage/rings/polynomial/pbori.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ import operator
from sage.misc.cachefunc import cached_method

from sage.misc.randstate import current_randstate
from sage.misc.long cimport pyobject_to_long
import sage.misc.weak_dict
from sage.rings.integer import Integer
from sage.rings.finite_rings.constructor import FiniteField as GF
Expand Down Expand Up @@ -6131,13 +6132,17 @@ cdef class BooleanPolynomialVector:
sage: v[3]
Traceback (most recent call last):
...
IndexError
IndexError: index out of range
sage: v['a']
Traceback (most recent call last):
...
TypeError: 'str' object cannot be interpreted as an index
"""
cdef long i = int(ind)
while i < 0:
cdef long i = pyobject_to_long(ind)
if i < 0:
i += self._vec.size()
if i >= self._vec.size():
raise IndexError
if i < 0 or i >= self._vec.size():
raise IndexError("index out of range")
return new_BP_from_PBPoly(self._parent, self._vec.get(i))


Expand Down
3 changes: 2 additions & 1 deletion src/sage/rings/polynomial/polynomial_element.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -1672,7 +1672,7 @@ cdef class Polynomial(CommutativeAlgebraElement):
"""
EXAMPLES::
sage: R.<x> = QQ[]
sage: x = polygen(QQ['u']['v'])
sage: f = x - 1
sage: f._pow(3)
x^3 - 3*x^2 + 3*x - 1
Expand All @@ -1687,6 +1687,7 @@ cdef class Polynomial(CommutativeAlgebraElement):
TESTS::
sage: x = polygen(QQ['u']['v'])
sage: x^(1/2)
Traceback (most recent call last):
...
Expand Down
8 changes: 4 additions & 4 deletions src/sage/rings/polynomial/polynomial_integer_dense_flint.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ include "sage/ext/stdsage.pxi"
include "sage/ext/interrupt.pxi"
include "sage/libs/ntl/decl.pxi"

from sage.misc.long cimport pyobject_to_long

from sage.rings.polynomial.polynomial_element cimport Polynomial
from sage.structure.element cimport ModuleElement, RingElement
from sage.structure.element import coerce_binop
Expand All @@ -55,8 +57,6 @@ from sage.libs.flint.fmpz_poly cimport fmpz_poly_reverse, fmpz_poly_revert_serie
from sage.libs.flint.ntl_interface cimport fmpz_set_ZZ, fmpz_poly_set_ZZX, fmpz_poly_get_ZZX
from sage.libs.ntl.ntl_ZZX_decl cimport *

from cpython.number cimport PyNumber_Index

cdef extern from "limits.h":
long LONG_MAX

Expand Down Expand Up @@ -902,10 +902,10 @@ cdef class Polynomial_integer_dense_flint(Polynomial):
sage: x^(2^100)
Traceback (most recent call last):
...
OverflowError: Python int too large to convert to C long
OverflowError: Sage Integer too large to convert to C long
"""
cdef Polynomial_integer_dense_flint res = self._new()
cdef long nn = PyNumber_Index(exp)
cdef long nn = pyobject_to_long(exp)

if self.is_zero():
if exp == 0:
Expand Down
14 changes: 5 additions & 9 deletions src/sage/rings/polynomial/polynomial_rational_flint.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ AUTHOR:
include "sage/ext/stdsage.pxi"
include "sage/ext/interrupt.pxi"

from sage.misc.long cimport pyobject_to_long

from sage.libs.gmp.mpz cimport *
from sage.libs.gmp.mpq cimport *
from sage.libs.flint.fmpz cimport *
Expand All @@ -42,8 +44,6 @@ from sage.structure.parent cimport Parent
from sage.structure.element cimport Element, ModuleElement, RingElement
from sage.structure.element import coerce_binop

from cpython.number cimport PyNumber_AsSsize_t

cdef inline bint _do_sig(fmpq_poly_t op):
"""
Returns 1 when signal handling should be carried out for an operation
Expand Down Expand Up @@ -1081,11 +1081,11 @@ cdef class Polynomial_rational_flint(Polynomial):
sage: (1 + t)^(2/3)
Traceback (most recent call last):
...
TypeError: non-integral exponents not supported
TypeError: rational is not an integer
sage: (1 + t)^(2^63)
Traceback (most recent call last):
...
OverflowError: cannot fit 'sage.rings.integer.Integer' into an index-sized integer
OverflowError: Sage Integer too large to convert to C long
FLINT memory errors do not crash Sage (:trac:`17629`)::
Expand All @@ -1094,13 +1094,9 @@ cdef class Polynomial_rational_flint(Polynomial):
...
RuntimeError: FLINT exception
"""
cdef Py_ssize_t n
cdef Polynomial_rational_flint res

try:
n = PyNumber_AsSsize_t(exp, OverflowError)
except TypeError:
raise TypeError("non-integral exponents not supported")
cdef long n = pyobject_to_long(exp)

if n < 0:
if fmpq_poly_is_zero(self.__poly):
Expand Down
3 changes: 2 additions & 1 deletion src/sage/rings/rational.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import sys
import operator

from sage.misc.mathml import mathml
from sage.misc.long cimport pyobject_to_long

import sage.misc.misc as misc
import sage.rings.rational_field
Expand Down Expand Up @@ -2337,7 +2338,7 @@ cdef class Rational(sage.structure.element.FieldElement):
cdef long nn

try:
nn = PyNumber_Index(n)
nn = pyobject_to_long(n)
except TypeError:
if isinstance(n, Rational):
# Perhaps it can be done exactly
Expand Down

0 comments on commit 01432cf

Please sign in to comment.