r"""
Base class for old-style parent objects
CLASS HIERARCHY:
SageObject
Parent
ParentWithBase
ParentWithGens
TESTS:
This came up in some subtle bug once.
::
sage: gp(2) + gap(3)
5
"""
cimport sage_object
import operator
from parent import Set_PythonType, Set_PythonType_class
from coerce import py_scalar_parent
include '../ext/python_object.pxi'
include '../ext/python_bool.pxi'
include '../ext/stdsage.pxi'
cdef inline check_old_coerce(Parent p):
if p._element_constructor is not None:
raise RuntimeError, "%s still using old coercion framework" % p
cdef class Parent(parent.Parent):
"""
Parents are the SAGE/mathematical analogues of container objects
in computer science.
"""
def __init__(self, coerce_from=[], actions=[], embeddings=[], category=None):
self.init_coerce(False)
self._coerce_from_list = list(coerce_from)
self._coerce_from_hash = {}
self._action_list = list(actions)
self._action_hash = {}
cdef parent.Parent other
for mor in embeddings:
other = mor.domain()
print "embedding", self, " --> ", other
print mor
other.init_coerce()
other._coerce_from_list.append(mor)
self._set_element_constructor()
self._has_coerce_map_from = {}
if category is not None:
self._init_category_(category)
cdef int init_coerce(self, bint warn=False) except -1:
parent.Parent.init_coerce(self, warn)
cpdef coerce_map_from_c(self, S):
"""
EXAMPLES:
Check to make sure that we handle coerce maps from Python
native types correctly::
sage: QQ['q,t'].coerce_map_from(int)
Composite map:
From: Set of Python objects of type 'int'
To: Multivariate Polynomial Ring in q, t over Rational Field
Defn: Native morphism:
From: Set of Python objects of type 'int'
To: Rational Field
then
Polynomial base injection morphism:
From: Rational Field
To: Multivariate Polynomial Ring in q, t over Rational Field
"""
check_old_coerce(self)
if S is self:
from sage.categories.homset import Hom
return Hom(self, self).identity()
elif S == self:
from sage.categories.homset import Hom
from sage.categories.morphism import CallMorphism
return CallMorphism(Hom(S, self))
elif isinstance(S, Set_PythonType_class):
return self.coerce_map_from_c(S._type)
if self._coerce_from_hash is None:
self.init_coerce()
cdef object ret
try:
ret = PyObject_GetItem(self._coerce_from_hash,S)
return ret
except KeyError:
pass
if HAS_DICTIONARY(self):
mor = self.coerce_map_from_impl(S)
else:
mor = self.coerce_map_from_c_impl(S)
import sage.categories.morphism
import sage.categories.map
if mor is True:
mor = sage.categories.morphism.CallMorphism(S, self)
elif mor is False:
mor = None
elif mor is not None and not isinstance(mor, sage.categories.map.Map):
raise TypeError, "coerce_map_from_impl must return a boolean, None, or an explicit Map"
if mor is None and isinstance(S, type):
sage_type = py_scalar_parent(S)
mor = self.coerce_map_from_c(sage_type)
if mor is not None:
mor = mor * sage_type.coerce_map_from(S)
if mor is not None:
self._coerce_from_hash[S] = mor
return mor
def coerce_map_from_impl(self, S):
check_old_coerce(self)
return self.coerce_map_from_c_impl(S)
cdef coerce_map_from_c_impl(self, S):
check_old_coerce(self)
import sage.categories.morphism
from sage.categories.map import Map
from sage.categories.homset import Hom
cdef parent.Parent R
for mor in self._coerce_from_list:
if PY_TYPE_CHECK(mor, Map):
R = mor.domain()
else:
R = mor
mor = sage.categories.morphism.CallMorphism(Hom(R, self))
i = self._coerce_from_list.index(R)
self._coerce_from_list[i] = mor
if R is S:
return mor
else:
connecting = R.coerce_map_from(S)
if connecting is not None:
return mor * connecting
if self.has_coerce_map_from_c(S):
if isinstance(S, type):
S = Set_PythonType(S)
return sage.categories.morphism.CallMorphism(Hom(S, self))
else:
return None
cpdef get_action_c(self, S, op, bint self_on_left):
check_old_coerce(self)
try:
if self._action_hash is None:
self.init_coerce()
return self._action_hash[S, op, self_on_left]
except KeyError:
pass
if HAS_DICTIONARY(self):
action = self.get_action_impl(S, op, self_on_left)
else:
action = self.get_action_c_impl(S, op, self_on_left)
if action is not None:
from sage.categories.action import Action
if not isinstance(action, Action):
raise TypeError, "get_action_impl must return None or an Action"
self._action_hash[S, op, self_on_left] = action
return action
def get_action_impl(self, S, op, self_on_left):
check_old_coerce(self)
return self.get_action_c_impl(S, op, self_on_left)
cdef get_action_c_impl(self, S, op, bint self_on_left):
check_old_coerce(self)
return self.discover_action(S, op, self_on_left)
def _coerce_(self, x):
if self._element_constructor is not None:
return self.coerce(x)
check_old_coerce(self)
return self._coerce_c(x)
cpdef _coerce_c(self, x):
if self._element_constructor is not None:
return self.coerce(x)
check_old_coerce(self)
try:
P = x.parent()
if P is self:
return x
except AttributeError, msg:
pass
if HAS_DICTIONARY(self):
return self._coerce_impl(x)
else:
return self._coerce_c_impl(x)
cdef _coerce_c_impl(self, x):
"""
Canonically coerce x in assuming that the parent of x is not
equal to self.
"""
check_old_coerce(self)
raise TypeError
def _coerce_impl(self, x):
"""
Canonically coerce x in assuming that the parent of x is not
equal to self.
"""
check_old_coerce(self)
return self._coerce_c_impl(x)
def _coerce_try(self, x, v):
"""
Given a list v of rings, try to coerce x canonically into each
one in turn. Return the __call__ coercion of the result into
self of the first canonical coercion that succeeds. Raise a
TypeError if none of them succeed.
INPUT:
x -- Python object
v -- parent object or list (iterator) of parent objects
"""
check_old_coerce(self)
if not isinstance(v, list):
v = [v]
for R in v:
try:
y = R._coerce_(x)
return self(y)
except (TypeError, AttributeError), msg:
pass
raise TypeError, "no canonical coercion of element into self"
def _coerce_self(self, x):
check_old_coerce(self)
return self._coerce_self_c(x)
cdef _coerce_self_c(self, x):
"""
Try to canonically coerce x into self.
Return result on success or raise TypeError on failure.
"""
check_old_coerce(self)
try:
P = x.parent()
if P is self:
return x
elif P == self:
return self(x)
except AttributeError:
pass
raise TypeError, "no canonical coercion to self defined"
cpdef has_coerce_map_from_c(self, S):
"""
Return True if there is a natural map from S to self.
Otherwise, return False.
"""
check_old_coerce(self)
if self == S:
return True
if self._has_coerce_map_from is None:
self._has_coerce_map_from = {}
else:
try:
return self._has_coerce_map_from[S]
except KeyError:
pass
if HAS_DICTIONARY(self):
x = self.has_coerce_map_from_impl(S)
else:
x = self.has_coerce_map_from_c_impl(S)
self._has_coerce_map_from[S] = x
return x
def has_coerce_map_from_impl(self, S):
check_old_coerce(self)
return self.has_coerce_map_from_c_impl(S)
cdef has_coerce_map_from_c_impl(self, S):
check_old_coerce(self)
if not PY_TYPE_CHECK(S, parent.Parent):
return False
try:
self._coerce_c((<parent.Parent>S).an_element())
except TypeError:
return False
except NotImplementedError, msg:
raise NotImplementedError, "%s\nAlso, please make sure you have implemented has_coerce_map_from_impl or has_coerce_map_from_c_impl (or better _an_element_c_impl or _an_element_impl if possible) for %s"%(msg,self)
return True
def _an_element_impl(self):
check_old_coerce(self)
return self._an_element_c_impl()
cdef _an_element_c_impl(self):
"""
Returns an element of self. Want it in sufficient generality
that poorly-written functions won't work when they're not
supposed to. This is cached so doesn't have to be super fast.
"""
check_old_coerce(self)
try:
return self.gen(0)
except:
pass
try:
return self.gen()
except:
pass
from sage.rings.infinity import infinity
for x in ['_an_element_', 'pi', 1.2, 2, 1, 0, infinity]:
try:
return self(x)
except (TypeError, NameError, NotImplementedError, AttributeError, ValueError):
pass
raise NotImplementedError, "please implement _an_element_c_impl or _an_element_impl for %s"%self
def _an_element(self):
check_old_coerce(self)
return self._an_element_c()
cpdef _an_element_c(self):
check_old_coerce(self)
if not self.__an_element is None:
return self.__an_element
if HAS_DICTIONARY(self):
self.__an_element = self._an_element_impl()
else:
self.__an_element = self._an_element_c_impl()
return self.__an_element
list = parent.Parent._list_from_iterator_cached
"""
TESTS::
sage: V = VectorSpace(GF(2,'a'),2)
sage: V.list()
[(0, 0), (1, 0), (0, 1), (1, 1)]
sage: MatrixSpace(GF(3), 1, 1).list()
[[0], [1], [2]]
sage: DirichletGroup(3).list()
[Dirichlet character modulo 3 of conductor 1 mapping 2 |--> 1,
Dirichlet character modulo 3 of conductor 3 mapping 2 |--> -1]
sage: K = GF(7^6,'a')
sage: K.list()[:10] # long time
[0, 1, 2, 3, 4, 5, 6, a, a + 1, a + 2]
sage: K.<a> = GF(4)
sage: K.list()
[0, a, a + 1, 1]
"""
cdef _richcmp(left, right, int op):
"""
Compare left and right.
"""
check_old_coerce(left)
cdef int r
if not PY_TYPE_CHECK(right, parent.Parent) or not PY_TYPE_CHECK(left, parent.Parent):
if (<PyObject*>left) < (<PyObject*>right):
r = -1
elif (<PyObject*>left) > (<PyObject*>right):
r = 1
else:
r = 0
else:
if HAS_DICTIONARY(left):
r = left.__cmp__(right)
else:
r = left._cmp_c_impl(right)
if op == 0:
return PyBool_FromLong(r < 0)
elif op == 2:
return PyBool_FromLong(r == 0)
elif op == 4:
return PyBool_FromLong(r > 0)
elif op == 1:
return PyBool_FromLong(r <= 0)
elif op == 3:
return PyBool_FromLong(r != 0)
elif op == 5:
return PyBool_FromLong(r >= 0)
cdef int _cmp_c_impl(left, parent.Parent right) except -2:
check_old_coerce(left)
pass
if (<PyObject*>left) < (<PyObject*>right):
return -1
elif (<PyObject*>left) > (<PyObject*>right):
return 1
return 0
cpdef _coerce_map_from_(self, S):
if self._element_constructor is None:
return self.coerce_map_from_c(S)
else:
return parent.Parent._coerce_map_from_(self, S)
cpdef _get_action_(self, other, op, bint self_on_left):
if self._element_constructor is None:
return self.get_action_c(other, op, self_on_left)
else:
return parent.Parent._get_action_(self, other, op, self_on_left)
def _an_element_(self):
if self._element_constructor is None:
return self._an_element_c()
else:
return parent.Parent._an_element_(self)
cpdef _generic_convert_map(self, S):
if self._element_constructor is None:
if hasattr(self, '_element_constructor_'):
assert callable(self._element_constructor_)
self._element_constructor = self._element_constructor_
else:
from sage.categories.morphism import CallMorphism
from sage.categories.homset import Hom
if PY_TYPE_CHECK(S, type):
S = Set_PythonType(S)
return CallMorphism(Hom(S, self))
return parent.Parent._generic_convert_map(self, S)