# HG changeset patch
# User Simon King
# Date 1361268866 3600
# Node ID 935c3845daaa8c8f2ddc9b2273be08c711184fb8
# Parent efe3c1085e05c5ae339d6e1fb9dcf58a4b52aea3
#14054: Separate CachedRepresentation from UniqueRepresentation.
diff git a/doc/en/reference/misc.rst b/doc/en/reference/misc.rst
 a/doc/en/reference/misc.rst
+++ b/doc/en/reference/misc.rst
@@ 46,6 +46,7 @@
sage/misc/nested_class
sage/misc/nested_class_test
sage/misc/classcall_metaclass
+ sage/misc/fast_methods
sage/misc/sage_unittest
sage/misc/randstate
sage/misc/cython
diff git a/module_list.py b/module_list.py
 a/module_list.py
+++ b/module_list.py
@@ 1197,6 +1197,9 @@
Extension('sage.misc.classcall_metaclass',
sources = ['sage/misc/classcall_metaclass.pyx']),
+ Extension('sage.misc.fast_methods',
+ sources = ['sage/misc/fast_methods.pyx']),
+
Extension('sage.misc.binary_tree',
sources = ['sage/misc/binary_tree.pyx']),
diff git a/sage/algebras/iwahori_hecke_algebra.py b/sage/algebras/iwahori_hecke_algebra.py
 a/sage/algebras/iwahori_hecke_algebra.py
+++ b/sage/algebras/iwahori_hecke_algebra.py
@@ 14,8 +14,9 @@
from sage.combinat.family import Family
from sage.combinat.free_module import CombinatorialFreeModule, CombinatorialFreeModuleElement
from sage.misc.cachefunc import cached_method
+from sage.structure.unique_representation import UniqueRepresentation
class IwahoriHeckeAlgebraT(CombinatorialFreeModule):
+class IwahoriHeckeAlgebraT(UniqueRepresentation, CombinatorialFreeModule):
r"""
INPUT:
diff git a/sage/categories/category_singleton.pxd b/sage/categories/category_singleton.pxd
 a/sage/categories/category_singleton.pxd
+++ b/sage/categories/category_singleton.pxd
@@ 1,5 +1,2 @@
cdef class FastHashable_class:
 cdef Py_ssize_t _hash

cdef class Category_contains_method_by_parent_class:
cdef type _parent_class_of_category
diff git a/sage/categories/category_singleton.pyx b/sage/categories/category_singleton.pyx
 a/sage/categories/category_singleton.pyx
+++ b/sage/categories/category_singleton.pyx
@@ 82,33 +82,7 @@
except AttributeError:
return False
cdef class FastHashable_class:
 """
 A class that has a fast hash method, returning a preassigned value.

 NOTE:

 This is for internal use only. The class has a cdef attribute ``_hash``,
 that needs to be assigned.

 TESTS::

 sage: issubclass(Rings, sage.categories.category_singleton.FastHashable_class)
 True

 """
 def __hash__(self):
 """
 TESTS::

 sage: hash(Rings()) == id(Rings) # indirect doctest
 True

 """
 return self._hash


class Category_singleton(FastHashable_class,Category):
+class Category_singleton(Category):
"""
A base class for implementing singleton category
@@ 274,29 +248,14 @@
sage: MySubStuff()
Category of my stuff
"""
 cdef FastHashable_class obj
if cls.__mro__[1] is not Category_singleton:
# Actually this type error is invisible. But it makes sure that
# the __classcall__ for Category_singleton is not overridden
# when someone is calling it.
raise TypeError, "%s is not a direct subclass of %s"%(cls,Category_singleton)
obj = UniqueRepresentation.__classcall__(cls)
 obj._hash = cls
return ConstantFunction(obj)
# @lazy_class_attribute
# def __hash__(cls):
# """
# The hash of the unique instance of a category singleton is the address of its class.
#
# EXAMPLES::
#
# sage: hash(Rings()) == hash(Rings) # indirect doctest
# True
#
# """
# return ConstantFunction(id(cls))

@lazy_class_attribute
def __contains__(cls):
"""
diff git a/sage/categories/examples/algebras_with_basis.py b/sage/categories/examples/algebras_with_basis.py
 a/sage/categories/examples/algebras_with_basis.py
+++ b/sage/categories/examples/algebras_with_basis.py
@@ 13,8 +13,9 @@
from sage.categories.all import AlgebrasWithBasis
from sage.combinat.free_module import CombinatorialFreeModule
from sage.combinat.words.words import Words
+from sage.structure.unique_representation import UniqueRepresentation
class FreeAlgebra(CombinatorialFreeModule):
+class FreeAlgebra(UniqueRepresentation, CombinatorialFreeModule):
r"""
An example of an algebra with basis: the free algebra
diff git a/sage/categories/primer.py b/sage/categories/primer.py
 a/sage/categories/primer.py
+++ b/sage/categories/primer.py
@@ 427,6 +427,8 @@
[,
,
,
+ ,
+ ,
,
,
,
diff git a/sage/categories/sets_cat.py b/sage/categories/sets_cat.py
 a/sage/categories/sets_cat.py
+++ b/sage/categories/sets_cat.py
@@ 88,6 +88,8 @@
+
+
diff git a/sage/combinat/free_module.py b/sage/combinat/free_module.py
 a/sage/combinat/free_module.py
+++ b/sage/combinat/free_module.py
@@ 9,7 +9,7 @@
# Distributed under the terms of the GNU General Public License (GPL)
# http://www.gnu.org/licenses/
#*****************************************************************************
from sage.structure.unique_representation import UniqueRepresentation
+from sage.structure.unique_representation import CachedRepresentation
from sage.structure.element import Element
from sage.structure.parent import Parent
from sage.structure.sage_object import have_same_parent
@@ 262,7 +262,7 @@
sage: F1.zero() == 0
True
sage: F1.zero() == F2.zero()
 False
+ True
sage: F1.an_element() == None
False
@@ 870,7 +870,7 @@
else:
return q
class CombinatorialFreeModule(UniqueRepresentation, Module):
+class CombinatorialFreeModule(CachedRepresentation, Module):
r"""
Class for free modules with a named basis
@@ 1000,22 +1000,26 @@
sage: F1 is F
True
 The constructed free module depends on the order of the basis and
 on the other parameters, like the prefix::
+ The identity of the constructed free module depends on the order of the
+ basis and on the other parameters, like the prefix. However, the resulting
+ modules evaluate equal (thus, violate the unique parent assumption)::
sage: F1 = CombinatorialFreeModule(QQ, (1,2,3,4))
sage: F1 is F
True
sage: F1 = CombinatorialFreeModule(QQ, [4,3,2,1])
sage: F1 == F
 False
+ True
sage: F2 = CombinatorialFreeModule(QQ, [1,2,3,4], prefix='F')
sage: F2 == F
 False
+ True
Because of this, if you create a free module with certain
parameters and then modify its prefix or other print options, this
 affects all modules which were defined using the same parameters::
+ affects all modules which were defined using the same parameters.
+ Note, however, that :class:`CombinatorialFreeModule` has just a
+ :class:`~sage.structure.unique_representation.CachedRepresentation`,
+ but no :class:`~sage.structure.unique_representation.UniqueRepresentation`::
sage: F2.print_options(prefix='x')
sage: F2.prefix()
@@ 1027,7 +1031,7 @@
'x'
sage: F4 = CombinatorialFreeModule(QQ, [1,2,3,4], prefix='F', bracket=True)
sage: F4 == F2 # F4 was NOT defined just like F2
 False
+ True
sage: F4.prefix()
'F'
@@ 1221,7 +1225,7 @@
# self._repr_option_bracket. Future users might consider
# using 'bracket' instead of _repr_option_bracket.
 # ignore the optional 'key' since it only affects UniqueRepresentation
+ # ignore the optional 'key' since it only affects CachedRepresentation
kwds.pop('key', None)
self.print_options(**kwds)
@@ 2328,6 +2332,10 @@
sage: T.print_options(tensor_symbol= ' @ ') # note the spaces
sage: T # indirect doctest
F @ G
+
+ To avoid a side\effect on another doctest, we revert the change::
+
+ sage: T.print_options(tensor_symbol= ' # ')
"""
from sage.categories.tensor import tensor
if hasattr(self, "_print_options"):
@@ 2412,7 +2420,6 @@
sage: f = F.monomial(1) + 2 * F.monomial(2)
sage: g = 2*G.monomial(3) + G.monomial(4)
sage: h = H.monomial(5) + H.monomial(6)

sage: FG = tensor([F, G ])
sage: phi_fg = FG.tensor_constructor((F, G))
sage: phi_fg(f,g)
diff git a/sage/combinat/ncsf_qsym/ncsf.py b/sage/combinat/ncsf_qsym/ncsf.py
 a/sage/combinat/ncsf_qsym/ncsf.py
+++ b/sage/combinat/ncsf_qsym/ncsf.py
@@ 943,7 +943,7 @@
from sage.categories.all import tensor
return tensor([self.one(), x]) + tensor([x, self.one()])
 class Ribbon(CombinatorialFreeModule, BindableClass):
+ class Ribbon(UniqueRepresentation, CombinatorialFreeModule, BindableClass):
r"""
The Hopf algebra of noncommutative symmetric functions in the Ribbon basis.
@@ 1089,7 +1089,7 @@
R = ribbon = Ribbon
 class Complete(CombinatorialFreeModule, BindableClass):
+ class Complete(UniqueRepresentation, CombinatorialFreeModule, BindableClass):
"""
The Hopf algebra of noncommutative symmetric functions in the Complete basis.
@@ 1256,7 +1256,7 @@
S = complete = Complete
 class Elementary(CombinatorialFreeModule, BindableClass):
+ class Elementary(UniqueRepresentation, CombinatorialFreeModule, BindableClass):
r"""
The Hopf algebra of noncommutative symmetric functions in the Elementary basis.
@@ 1346,7 +1346,7 @@
L = elementary = Elementary
 class Psi(CombinatorialFreeModule, BindableClass):
+ class Psi(UniqueRepresentation, CombinatorialFreeModule, BindableClass):
r"""
The Hopf algebra of noncommutative symmetric functions in the Psi basis.
@@ 1485,7 +1485,7 @@
return complete.sum_of_terms((J, minus_one**(len(J)len(I))*coeff_lp(J,I))
for J in I.finer())
 class Phi(CombinatorialFreeModule, BindableClass):
+ class Phi(UniqueRepresentation, CombinatorialFreeModule, BindableClass):
r"""
The Hopf algebra of noncommutative symmetric functions in the Phi basis.
@@ 1593,7 +1593,7 @@
return complete.sum_of_terms((J, minus_one**(len(J)len(I)) * prod(I) / coeff_ell(J,I))
for J in I.finer())
 class Monomial(CombinatorialFreeModule, BindableClass):
+ class Monomial(UniqueRepresentation, CombinatorialFreeModule, BindableClass):
def __init__(self, NCSF):
r"""
Basis from 'Noncommutative Analogs of Monomial Symmetric
@@ 1700,7 +1700,7 @@
nM = monomial = Monomial
 class Immaculate(CombinatorialFreeModule, BindableClass):
+ class Immaculate(UniqueRepresentation, CombinatorialFreeModule, BindableClass):
def __init__(self, NCSF):
r"""
The immaculate basis of the noncommutative symmetric functions. This basis first
diff git a/sage/combinat/root_system/dynkin_diagram.py b/sage/combinat/root_system/dynkin_diagram.py
 a/sage/combinat/root_system/dynkin_diagram.py
+++ b/sage/combinat/root_system/dynkin_diagram.py
@@ 171,9 +171,9 @@
"""
EXAMPLES::
 sage: hash(CartanType(['A',3]).dynkin_diagram()) # indirect doctest
 286820813001824631 # 64bit
 2127980169 # 32bit
+ sage: d = CartanType(['A',3]).dynkin_diagram()
+ sage: hash(d) == hash((d.cartan_type(), tuple(d.vertices()), tuple(d.edge_iterator(d.vertices()))))
+ True
"""
# Should assert for immutability!
diff git a/sage/combinat/root_system/weyl_characters.py b/sage/combinat/root_system/weyl_characters.py
 a/sage/combinat/root_system/weyl_characters.py
+++ b/sage/combinat/root_system/weyl_characters.py
@@ 18,10 +18,11 @@
from sage.misc.flatten import flatten
from sage.misc.lazy_attribute import lazy_attribute
from sage.misc.functional import is_even, is_odd
+from sage.structure.unique_representation import UniqueRepresentation
from sage.modules.free_module_element import vector
from sage.rings.all import ZZ, QQ
class WeylCharacterRing(CombinatorialFreeModule):
+class WeylCharacterRing(UniqueRepresentation, CombinatorialFreeModule):
"""
A class for rings of Weyl characters.
@@ 2516,7 +2517,7 @@
else:
return lambda x : tuple(M*vector(x))
class WeightRing(CombinatorialFreeModule):
+class WeightRing(UniqueRepresentation,CombinatorialFreeModule):
"""
The WeightRing is the group algebra over a weight lattice.
diff git a/sage/combinat/sf/sfa.py b/sage/combinat/sf/sfa.py
 a/sage/combinat/sf/sfa.py
+++ b/sage/combinat/sf/sfa.py
@@ 203,6 +203,7 @@
from sage.combinat.partition import Partitions
import sage.libs.symmetrica.all as symmetrica # used in eval()
from sage.combinat.free_module import CombinatorialFreeModule
+from sage.structure.unique_representation import UniqueRepresentation
from sage.matrix.constructor import matrix
from sage.misc.misc import repr_lincomb, prod, uniq
from functools import partial
@@ 762,7 +763,7 @@
"""
return self.coefficient([])
class SymmetricFunctionAlgebra_generic(CombinatorialFreeModule):
+class SymmetricFunctionAlgebra_generic(UniqueRepresentation, CombinatorialFreeModule):
r"""
.. TODO:: most of the methods in this class are generic (manipulations of morphisms, ...) and should be generalized (or removed)
diff git a/sage/combinat/symmetric_group_algebra.py b/sage/combinat/symmetric_group_algebra.py
 a/sage/combinat/symmetric_group_algebra.py
+++ b/sage/combinat/symmetric_group_algebra.py
@@ 15,6 +15,7 @@
import partition
from tableau import Tableau, StandardTableaux_size, StandardTableaux_shape, StandardTableaux
from sage.interfaces.all import gap
+from sage.structure.unique_representation import UniqueRepresentation
from sage.rings.all import factorial, QQ, PolynomialRing
from sage.matrix.all import matrix
from sage.modules.all import vector
@@ 91,14 +92,14 @@
"""
return SymmetricGroupAlgebra_n(R,n)
class SymmetricGroupAlgebra_n(CombinatorialFreeModule):
+class SymmetricGroupAlgebra_n(UniqueRepresentation, CombinatorialFreeModule):
def __init__(self, R, n):
"""
TESTS::
sage: QS3 = SymmetricGroupAlgebra(QQ, 3)
 sage: QS3 == loads(dumps(QS3))
+ sage: QS3 is loads(dumps(QS3))
True
"""
self.n = n
@@ 747,7 +748,7 @@
return HeckeAlgebraSymmetricGroup_t(R, n, q)
class HeckeAlgebraSymmetricGroup_generic(CombinatorialAlgebra):
+class HeckeAlgebraSymmetricGroup_generic(UniqueRepresentation, CombinatorialAlgebra):
def __init__(self, R, n, q=None):
"""
TESTS::
diff git a/sage/ext/python_rich_object.pxi b/sage/ext/python_rich_object.pxi
 a/sage/ext/python_rich_object.pxi
+++ b/sage/ext/python_rich_object.pxi
@@ 31,6 +31,8 @@
newfunc tp_new
freefunc tp_free
destructor tp_dealloc
+ hashfunc tp_hash
+ richcmpfunc tp_richcompare
#sizeof(Object)
Py_ssize_t tp_basicsize
diff git a/sage/graphs/isgci.py b/sage/graphs/isgci.py
 a/sage/graphs/isgci.py
+++ b/sage/graphs/isgci.py
@@ 328,7 +328,7 @@
"""
from sage.structure.sage_object import SageObject
from sage.structure.unique_representation import UniqueRepresentation
+from sage.structure.unique_representation import CachedRepresentation, UniqueRepresentation
from sage.misc.unknown import Unknown
from sage.misc.misc import SAGE_SHARE
@@ 341,7 +341,7 @@
_XML_FILE = "isgci_sage.xml"
class GraphClass(SageObject, UniqueRepresentation):
+class GraphClass(SageObject, CachedRepresentation):
r"""
An instance of this class represents a Graph Class, matching some entry in
the ISGCI database.
diff git a/sage/groups/perm_gps/permgroup_named.py b/sage/groups/perm_gps/permgroup_named.py
 a/sage/groups/perm_gps/permgroup_named.py
+++ b/sage/groups/perm_gps/permgroup_named.py
@@ 89,11 +89,12 @@
from sage.rings.arith import factor
from sage.groups.abelian_gps.abelian_group import AbelianGroup
from sage.misc.functional import is_even
from sage.misc.cachefunc import cached_method
+from sage.misc.cachefunc import cached_method, weak_cached_function
+from sage.misc.classcall_metaclass import typecall
from sage.misc.superseded import deprecated_function_alias
from sage.groups.perm_gps.permgroup import PermutationGroup_generic
from sage.groups.perm_gps.permgroup_element import PermutationGroupElement
from sage.structure.unique_representation import UniqueRepresentation
+from sage.structure.unique_representation import CachedRepresentation
from sage.structure.parent import Parent
from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets
from sage.sets.finite_enumerated_set import FiniteEnumeratedSet
@@ 103,8 +104,19 @@
from sage.sets.family import Family
from sage.sets.primes import Primes
class PermutationGroup_unique(UniqueRepresentation, PermutationGroup_generic):
 @staticmethod
+class PermutationGroup_unique(CachedRepresentation, PermutationGroup_generic):
+ """
+ .. TODO::
+
+ Fix the broken hash.
+ ::
+
+ sage: G = SymmetricGroup(6)
+ sage: G3 = G.subgroup([G((1,2,3,4,5,6)),G((1,2))])
+ sage: hash(G) == hash(G3) # todo: Should be True!
+ False
+ """
+ @weak_cached_function
def __classcall__(cls, *args, **kwds):
"""
This makes sure that domain is a FiniteEnumeratedSet before it gets passed
@@ 124,15 +136,16 @@
def __eq__(self, other):
"""
 Overrides the default equality testing provided by
 UniqueRepresentation by forcing a call to :meth:.`__cmp__`.

EXAMPLES::
sage: G = SymmetricGroup(6)
sage: G3 = G.subgroup([G((1,2,3,4,5,6)),G((1,2))])
sage: G == G3
True
+
+ .. WARNING::
+
+ The hash currently is broken for this comparison.
"""
return self.__cmp__(other) == 0
@@ 1631,7 +1644,7 @@
"""
return isinstance(G,TransitiveGroup)
class TransitiveGroupsOfDegree(UniqueRepresentation, Parent):
+class TransitiveGroupsOfDegree(CachedRepresentation, Parent):
"""
The set of all transitive groups of a given (small) degree up to isomorphisms.
@@ 2028,7 +2041,7 @@
"""
return isinstance(G,PrimitiveGroup)
class PrimitiveGroupsOfDegree(UniqueRepresentation, Parent):
+class PrimitiveGroupsOfDegree(CachedRepresentation, Parent):
"""
The set of all primitive groups of a given degree up to isomorphisms.
@@ 2666,10 +2679,3 @@
"""
return "The Suzuki group over %s"%self.base_ring()







diff git a/sage/misc/fast_methods.pxd b/sage/misc/fast_methods.pxd
new file mode 100644
 /dev/null
+++ b/sage/misc/fast_methods.pxd
@@ 0,0 +1,2 @@
+cdef class FastHashable_class:
+ cdef long _hash
diff git a/sage/misc/fast_methods.pyx b/sage/misc/fast_methods.pyx
new file mode 100644
 /dev/null
+++ b/sage/misc/fast_methods.pyx
@@ 0,0 +1,258 @@
+"""
+Fast methods via Cython
+
+This module provides extension classes with useful methods of cython speed,
+that python classes can inherit.
+
+.. NOTE::
+
+ In its original version, this module provides a cython base class
+ :class:`FastHashable_class` with assignable hash, and a cython base
+ class :class:`WithRichCmpById` implementing unique instance behaviour.
+
+AUTHOR:
+
+ Simon King (201302)
+
+"""
+
+#******************************************************************************
+# Copyright (C) 2013 Simon A. King
+#
+# Distributed under the terms of the GNU General Public License (GPL)
+#
+# This code is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# The full text of the GPL is available at:
+#
+# http://www.gnu.org/licenses/
+#******************************************************************************
+
+include "../ext/python_rich_object.pxi"
+include "../ext/python_bool.pxi"
+include "../ext/python_ref.pxi"
+
+cdef int SIZEOF_VOID_P_SHIFT = 8*sizeof(void *)  4
+
+cdef class WithRichCmpById:
+ """
+ Provide hash and comparison based on identity.
+
+ .. NOTE::
+
+ This class provides the unique representation behaviour of
+ :class:`~sage.structure.unique_representation.UniqueRepresentation`,
+ together with :class:`~sage.structure.unique_representation.CachedRepresentation`.
+
+ EXAMPLES:
+
+ Any instance of :class:`~sage.structure.unique_representation.UniqueRepresentation`
+ inherits from :class:`WithRichCmpById`.
+ ::
+
+ sage: class MyParent(Parent):
+ ... def __init__(self, x):
+ ... self.x = x
+ ... def __cmp__(self,other):
+ ... return cmp(self.x^2,other.x^2)
+ ... def __hash__(self):
+ ... return hash(self.x)
+ sage: class MyUniqueParent(UniqueRepresentation, MyParent): pass
+ sage: issubclass(MyUniqueParent, sage.misc.fast_methods.WithRichCmpById)
+ True
+
+ Inheriting from :class:`WithRichCmpById` provides unique representation
+ behaviour. In particular, the comparison inherited from ``MyParent``
+ is overloaded::
+
+ sage: a = MyUniqueParent(1)
+ sage: b = MyUniqueParent(2)
+ sage: c = MyUniqueParent(1)
+ sage: a is c
+ True
+ sage: d = MyUniqueParent(1)
+ sage: a == d
+ False
+
+ Note, however, that Python distinguishes between "comparison by cmp"
+ and "comparison by binary relations"::
+
+ sage: cmp(a,d)
+ 0
+
+ The comparison inherited from ``MyParent`` will be used in those cases
+ in which identity does not give sufficient information to find the relation::
+
+ sage: a < b
+ True
+ sage: b > d
+ True
+
+ The hash inherited from ``MyParent`` is replaced by a hash that coincides
+ with :class:`object`'s hash::
+
+ sage: hash(a) == hash(a.x)
+ False
+ sage: hash(a) == object.__hash__(a)
+ True
+
+ .. WARNING::
+
+ It is possible to inherit from
+ :class:`~sage.structure.unique_representation.UniqueRepresentation`
+ and then overload comparison in a way that destroys the unique
+ representation property. We strongly recommend against it! You should
+ use :class:`~sage.structure.unique_representation.CachedRepresentation`
+ instead.
+
+ ::
+
+ sage: class MyNonUniqueParent(MyUniqueParent):
+ ... def __eq__(self, other):
+ ... return self.x^2 == other.x^2
+ sage: a = MyNonUniqueParent(1)
+ sage: d = MyNonUniqueParent(1)
+ sage: a is MyNonUniqueParent(1)
+ True
+ sage: a == d
+ True
+ sage: a is d
+ False
+
+ """
+ def __hash__(self):
+ """
+ The hash provided by this class coincides with that of ````.
+
+ TESTS::
+
+ sage: class MyParent(Parent):
+ ... def __init__(self, x):
+ ... self.x = x
+ ... def __cmp__(self,other):
+ ... return cmp(self.x^2,other.x^2)
+ ... def __hash__(self):
+ ... return hash(self.x)
+ sage: class MyUniqueParent(UniqueRepresentation, MyParent): pass
+ sage: issubclass(MyUniqueParent, sage.misc.fast_methods.WithRichCmpById)
+ True
+ sage: a = MyUniqueParent(1)
+ sage: hash(a) == hash(a.x)
+ False
+ sage: hash(a) == object.__hash__(a)
+ True
+
+ """
+ # This is the default hash function in Python's object.c:
+ cdef long x
+ cdef size_t y = self
+ y = (y >> 4)  (y << SIZEOF_VOID_P_SHIFT)
+ x = y
+ if x==1:
+ x = 2
+ return x
+
+ def __richcmp__(self, other, int m):
+ """
+ Rich comparison provided by this class is by identity.
+
+ TESTS::
+
+ sage: class MyParent(Parent):
+ ... def __init__(self, x):
+ ... self.x = x
+ ... def __cmp__(self,other):
+ ... return cmp(self.x^2,other.x^2)
+ ... def __hash__(self):
+ ... return hash(self.x)
+ sage: class MyUniqueParent(UniqueRepresentation, MyParent): pass
+ sage: issubclass(MyUniqueParent, sage.misc.fast_methods.WithRichCmpById)
+ True
+ sage: a = MyUniqueParent(1)
+ sage: b = MyUniqueParent(1)
+
+ Comparison with ``cmp`` is still using what is inherited
+ from ``MyParent``::
+
+ sage: cmp(a,b)
+ 0
+
+ However, rich comparison takes into account identity::
+
+ sage: a == b
+ False
+
+ In cases in which rich comparison by identity gives no final answer,
+ the comparison inherited from ``MyParent`` is consulted again::
+
+ sage: a <= b and b >= a
+ True
+
+ """
+ cdef object out
+ if self is other:
+ if m==0: # <
+ return False
+ elif m==1: # <=
+ return True
+ elif m==2: # ==
+ return True
+ elif m==3: # !=
+ return False
+ elif m==4: # >
+ return False
+ else: # >=
+ return True
+ else:
+ if m==2:
+ return False
+ elif m==3:
+ return True
+ else:
+ return NotImplemented
+
+cdef class FastHashable_class:
+ """
+ A class that has a fast hash method, returning a preassigned value.
+
+ NOTE:
+
+ This is for internal use only. The class has a cdef attribute ``_hash``,
+ that needs to be assigned (for example, by calling the init method, or by
+ a direct assignement using cython). This is slower than using
+ :func:`provide_hash_by_id`, but has the advantage that the hash can be
+ prescribed, by assigning a cdef attribute ``_hash``.
+
+ TESTS::
+
+ sage: from sage.misc.fast_methods import FastHashable_class
+ sage: H = FastHashable_class(123)
+ sage: hash(H)
+ 123
+
+ """
+ def __init__(self, h):
+ """
+ TESTS::
+
+ sage: from sage.misc.fast_methods import FastHashable_class
+ sage: H = FastHashable_class(123)
+ sage: hash(H) # indirect doctest
+ 123
+
+ """
+ self._hash = h
+ def __hash__(self):
+ """
+ TESTS::
+
+ sage: from sage.misc.fast_methods import FastHashable_class
+ sage: H = FastHashable_class(123)
+ sage: hash(H) # indirect doctest
+ 123
+
+ """
+ return self._hash
diff git a/sage/misc/unknown.py b/sage/misc/unknown.py
 a/sage/misc/unknown.py
+++ b/sage/misc/unknown.py
@@ 153,26 +153,4 @@
else:
raise ValueError, "Unable to compare %s with %s"%(self, other)
 def __eq__(self, other):
 """
 TESTS::

 sage: Unknown == Unknown
 True
 sage: Unknown == None
 False
 """
 return self is other

 def __ne__(self, other):
 """
 TESTS::

 sage: Unknown != Unknown
 False
 sage: Unknown != None
 True
 """
 return self is not other

Unknown = UnknownClass()
diff git a/sage/structure/unique_representation.py b/sage/structure/unique_representation.py
 a/sage/structure/unique_representation.py
+++ b/sage/structure/unique_representation.py
@@ 6,9 +6,16 @@
.. SEEALSO::
:class:`sage.structure.factory.UniqueFactory`
+
+AUTHORS:
+
+ Nicolas M. Thiery (2008): Original version
+ Simon A. King (201302): Separate cached and unique representation.
+
"""
#*****************************************************************************
# Copyright (C) 2008 Nicolas M. Thiery
+# Copyright (C) 2013 Simon A. King
#
# Distributed under the terms of the GNU General Public License (GPL)
#
@@ 24,83 +31,84 @@
from sage.misc.cachefunc import weak_cached_function
from sage.misc.classcall_metaclass import ClasscallMetaclass, typecall
+from sage.misc.fast_methods import WithRichCmpById
class UniqueRepresentation:
+class CachedRepresentation:
"""
 Classes derived from UniqueRepresentation inherit a unique
 representation behavior for their instances.
+ Classes derived from CachedRepresentation inherit a weak cache for their
+ instances.
+
+ .. NOTE::
+
+ If this class is used as a base class, then instances are (weakly)
+ cached, according to the arguments used to create the instance.
+ Pickling is provided, of course by using the cache.
+
+ .. NOTE::
+
+ Using this class, one can have arbitrary hash and comparison.
+ Hence, *unique* representation behaviour is *not* provided.
+
+ .. SEEALSO::
+
+ :class:`UniqueRepresentation`
EXAMPLES:
 The short story: to construct a class whose instances have a
 unique representation behavior one just has to do::
+ Providing a class with a weak cache for the instances is easy: Just
+ inherit from :class:`CachedRepresentation`::
 sage: class MyClass(UniqueRepresentation):
+ sage: from sage.structure.unique_representation import CachedRepresentation
+ sage: class MyClass(CachedRepresentation):
... # all the rest as usual
... pass
 Everything below is for the curious or for advanced usage.
+ .. rubric:: What is cached representation?
 .. rubric:: What is unique representation?
+ Instances of a class have a *cached representation behavior* when
+ several instances constructed with the same arguments share the
+ same memory representation. For example, calling twice
+ ::
 Instances of a class have a *unique representation behavior* when
 several instances constructed with the same arguments share the
 same memory representation. For example, calling twice::
+ sage: G = SymmetricGroup(6)
+ sage: H = SymmetricGroup(6)
 sage: f = GF(7)
 sage: g = GF(7)

 to create the finite field of order 7 actually gives back the same
+ to create the symmetric group on six elements gives back the same
object::
 sage: f == g
+ sage: G is H
True
 sage: f is g
+
+ However, symmetric groups do not show the *unique* representation behaviour,
+ since they are equal to groups created in a totally different way::
+
+ sage: G3 = G.subgroup([G((1,2,3,4,5,6)),G((1,2))])
+ sage: G is G3
+ False
+ sage: type(G) == type(G3)
+ False
+ sage: G == G3
True
This is a standard design pattern. Besides saving memory, it
allows for sharing cached data (say representation theoretical
 information about a group) as well as for further optimizations
 (fast hashing, equality testing). This behaviour is typically
 desirable for parents and categories. It can also be useful for
 intensive computations where one wants to cache all the operations
 on a small set of elements (say the multiplication table of a
 small group), and access this cache as quickly as possible.

 The :class:`UniqueRepresentation` and
 :class:`~sage.structure.factory.UniqueFactory` classes
 provide two alternative implementations of this design pattern. Both
 implementations have their own merits. :class:`UniqueRepresentation` is
 very easy to use: a class just needs to derive from it, or make sure some
 of its super classes does. Also, it groups together the
 class and the factory in a single gadget; in the example above, one would
 want to do::

 sage: isinstance(f, GF) # todo: not implemented
 True

 but this does not work, because GF is only the factory.

 On the other hand the :class:`UniqueRepresentation` class is more
 intrusive, as it imposes a behavior (and a metaclass) to all the
 subclasses. Its implementation is also more technical, which leads
 to some subtleties.

 EXAMPLES:
+ information about a group).
We start with a simple class whose constructor takes a single
value as argument (TODO: find a more meaningful example)::
 sage: class MyClass(UniqueRepresentation):
+ sage: class MyClass(CachedRepresentation):
... def __init__(self, value):
... self.value = value
 ...
+ ... def __cmp__(self, other):
+ ... c = cmp(type(self),type(other))
+ ... if c: return c
+ ... return cmp(self.value, other.value)
 Two coexisting instances of MyClass created with the same
 argument data are guaranteed to share the same identity. Since
 trac ticket #12215, this is only the case if there is some
 strong reference to the returned instance, since otherwise
 it may be garbage collected::
+ Two coexisting instances of ``MyClass`` created with the same argument data
+ are guaranteed to share the same identity. Since :trac:`12215`, this is
+ only the case if there is some strong reference to the returned instance,
+ since otherwise it may be garbage collected::
sage: x = MyClass(1)
sage: y = MyClass(1)
@@ 120,25 +128,8 @@
sage: x.value, y.value
(1, 1)
 Unless overridden by the derived class, equality testing is
 implemented by comparing identities, which is as fast as it can get::

 sage: x == y
 True
 sage: z = MyClass(2)
 sage: x == z, x is z
 (False, False)

 Similarly, the identity is used as hash function, which is also as
 fast as it can get. However this means that the hash function may
 change in between Sage sessions, or even within the same Sage
 session (see below). Subclasses should overload :meth:`__hash__ `
 if this could be a problem.


 The arguments can consist of any combination of positional or
 keyword arguments, as taken by a usual
 :meth:`__init__ `
+ The arguments can consist of any combination of positional or keyword
+ arguments, as taken by a usual :meth:`__init__ `
function. However, all values passed in should be hashable::
sage: MyClass(value = [1,2,3])
@@ 153,7 +144,7 @@
how to achieve this; it takes as argument any iterable, and
canonicalizes it into a tuple (which is hashable!)::
 sage: class MyClass2(UniqueRepresentation):
+ sage: class MyClass2(CachedRepresentation):
... @staticmethod
... def __classcall__(cls, iterable):
... t = tuple(iterable)
@@ 174,7 +165,7 @@
values for some of its parameters. Alas, the obvious
implementation does not work::
 sage: class MyClass3(UniqueRepresentation):
+ sage: class MyClass3(CachedRepresentation):
... def __init__(self, value = 3):
... self.value = value
...
@@ 194,18 +185,17 @@
sage: MyClass3(3) is MyClass3()
True
 A bit of explanation is in order. First, the call
 ``MyClass2([1,2,3])`` triggers a call to
 ``MyClass2.__classcall__(MyClass2, [1,2,3])``. This is an extension of
 the standard Python behavior, needed by :class:`UniqueRepresentation`,
 and implemented by the
+ A bit of explanation is in order. First, the call ``MyClass2([1,2,3])``
+ triggers a call to ``MyClass2.__classcall__(MyClass2, [1,2,3])``. This is
+ an extension of the standard Python behavior, needed by
+ :class:`CachedRepresentation`, and implemented by the
:class:`~sage.misc.classcall_metaclass.ClasscallMetaclass`. Then,
``MyClass2.__classcall__`` does the desired transformations on the
 arguments. Finally, it uses ``super`` to call the default
 implementation of ``__classcall__`` provided by
 :class:`UniqueRepresentation`. This one in turn handles the caching
 and, if needed, constructs and initializes a new object in the
 class using :meth:`__new__` and :meth:`__init__` as usual.
+ arguments. Finally, it uses ``super`` to call the default implementation
+ of ``__classcall__`` provided by :class:`CachedRepresentation`. This one
+ in turn handles the caching and, if needed, constructs and initializes a
+ new object in the class using :meth:`__new__` and
+ :meth:`__init__` as usual.
Constraints:
@@ 213,18 +203,18 @@
 the preprocessing on the arguments should be
idempotent. Namely, If ``MyClass2.__classcall__`` calls
 ``UniqueRepresentation.__classcall__()``, then
 it should accept as its own input, and pass it
 down unmodified to :meth:`UniqueRepresentation.__classcall__`.
+ ``CachedRepresentation.__classcall__()``, then
+ it should accept ```` as its own input, and pass it
+ down unmodified to :meth:`CachedRepresentation.__classcall__`.
 ``MyClass2.__classcall__`` should return the result of
 :meth:`UniqueRepresentation.__classcall__` without modifying it.
+ :meth:`CachedRepresentation.__classcall__` without modifying it.
 Other than that ``MyClass2.__classcall__`` may play any tricks,
 like acting as a Factory and returning object from other classes.
+ Other than that ``MyClass2.__classcall__`` may play any tricks, like
+ acting as a Factory and returning objects from other classes.
 .. rubric:: Unique representation and mutability
+ .. rubric:: Cached representation and mutability
 :class:`UniqueRepresentation` is primarily intended for implementing
+ :class:`CachedRepresentation` is primarily intended for implementing
objects which are (at least semantically) immutable. This is in
particular assumed by the default implementations of ``copy`` and
``deepcopy``::
@@ 235,43 +225,46 @@
sage: deepcopy(x) is x
True
 Using :class:`UniqueRepresentation` on mutable objects may lead to
 subtle behavior::
+ However, in contrast to :class:`UniqueRepresentation`, using
+ :class:`CachedRepresentation` allows for a comparison that is not by
+ identity::
sage: t = MyClass(3)
sage: z = MyClass(2)
sage: t.value = 2
 Now x and z have the same data structure, but are not considered
 as equal::
+ Now x and z are nonidentical, but equal::
sage: t.value == z.value
True
sage: t == z
+ True
+ sage: t is z
False
 .. rubric:: More on unique representation and identity
+ .. rubric:: More on cached representation and identity
 :class:`UniqueRepresentation` is implemented by mean of a cache. This
 cache uses weak references so that, when all other references to,
 say, ``MyClass(1)`` have been deleted, the instance is actually
 deleted from memory. A later call to ``MyClass(1)`` reconstructs the
 instance from scratch, *most likely with a different id*.
+ :class:`CachedRepresentation` is implemented by means of a cache. This
+ cache uses weak references. Hence, when all other references to, say,
+ ``MyClass(1)`` have been deleted, the instance is actually deleted from
+ memory. A later call to ``MyClass(1)`` reconstructs the instance from
+ scratch, *most likely with a different id*.
 TODO: add an example illustrating this behavior
+ .. TODO::
+ add an example illustrating this behavior
 .. rubric:: Unique representation and pickling
 The default Python pickling implementation (by reconstructing an
 object from its class and dictionary, see "The pickle protocol" in
 the Python Library Reference) does not preserves unique
 representation, as Python has no chance to know whether and where
 the same object already exists.
+ .. rubric:: Cached representation and pickling
 :class:`UniqueRepresentation` tries to ensure appropriate pickling by
 implementing a :meth:`__reduce__ ` method returning the arguments
 passed to the constructor::
+ The default Python pickling implementation (by reconstructing an object
+ from its class and dictionary, see "The pickle protocol" in the Python
+ Library Reference) does not preserves cached representation, as Python has
+ no chance to know whether and where the same object already exists.
+
+ :class:`CachedRepresentation` tries to ensure appropriate pickling by
+ implementing a :meth:`__reduce__ ` method returning the
+ arguments passed to the constructor::
sage: import __main__ # Fake MyClass being defined in a python module
sage: __main__.MyClass = MyClass
@@ 279,7 +272,7 @@
sage: loads(dumps(x)) is x
True
 :class:`UniqueRepresentation` uses the :meth:`__reduce__
+ :class:`CachedRepresentation` uses the :meth:`__reduce__
` pickle protocol rather than :meth:`__getnewargs__
` because the later does not handle keyword
arguments::
@@ 292,8 +285,8 @@
.. warning::
 the default implementation of :meth:`__reduce__ `
 in :class:`UniqueRepresentation` requires to store the constructor's
+ The default implementation of :meth:`__reduce__ `
+ in :class:`CachedRepresentation` requires to store the constructor's
arguments in the instance dictionary upon construction::
sage: x.__dict__
@@ 318,9 +311,9 @@
sage: x.__dict__
{'value': 1}
 .. rubric:: Migrating classes to ``UniqueRepresentation`` and unpickling
+ .. rubric:: Migrating classes to ``CachedRepresentation`` and unpickling
 We check that, when migrating a class to :class:`UniqueRepresentation`,
+ We check that, when migrating a class to :class:`CachedRepresentation`,
older pickle can still be reasonably unpickled. Let us create a
(new style) class, and pickle one of its instances::
@@ 337,7 +330,8 @@
sage: y.value
1
 Now, we upgrade the class to derive from :class:`UniqueRepresentation`::
+ Now, we upgrade the class to derive from :class:`UniqueRepresentation`,
+ which inherits from :class:`CachedRepresentation`::
sage: class MyClass4(UniqueRepresentation, object):
... def __init__(self, value):
@@ 392,18 +386,16 @@
sage: y.value # todo: not implemented
1


.. rubric:: Rationale for the current implementation
 :class:`UniqueRepresentation` and derived classes use the
+ :class:`CachedRepresentation` and derived classes use the
:class:`~sage.misc.classcall_metaclass.ClasscallMetaclass`
of the standard Python type. The following example explains why.
We define a variant of ``MyClass`` where the calls to
:meth:`__init__` are traced::
 sage: class MyClass(UniqueRepresentation):
+ sage: class MyClass(CachedRepresentation):
... def __init__(self, value):
... print "initializing object"
... self.value = value
@@ 431,20 +423,7 @@
unprocessed arguments will be passed down to
:meth:`__init__`.
 .. rubric:: Mixing super types and super classes

 TESTS:

 For the record, this test did fail with previous implementation
 attempts::

 sage: class bla(UniqueRepresentation, SageObject):
 ... pass
 ...
 sage: b = bla()

"""

__metaclass__ = ClasscallMetaclass
_included_private_doc_ = ["__classcall__"]
@@ 454,115 +433,35 @@
"""
Constructs a new object of this class or reuse an existing one.
 See also :class:`UniqueRepresentation` for a discussion.
+ See also :class:`CachedRepresentation` and
+ :class:`UniqueRepresentation` for a discussion.
EXAMPLES::
sage: x = UniqueRepresentation()
sage: y = UniqueRepresentation()
 sage: x is y
+ sage: x is y # indirect doctest
True
+
"""
instance = typecall(cls, *args, **options)
assert isinstance( instance, cls )
 if instance.__class__.__reduce__ == UniqueRepresentation.__reduce__:
+ if instance.__class__.__reduce__ == CachedRepresentation.__reduce__:
instance._reduction = (cls, args, options)
return instance
 # Should be cythoned
 def __eq__(self, other):
 """
 Test if ``self`` and ``other` are equal by comparing their
 identity.

 See also :class:`UniqueRepresentation` for a discussion.

 EXAMPLES::

 sage: x = UniqueRepresentation()
 sage: y = UniqueRepresentation()
 sage: x == y
 True
 sage: x is y
 True
 sage: x == 3
 False

 TESTS::

 sage: class bla(UniqueRepresentation, SageObject):
 ... def __init__(self, i): pass
 sage: b1 = bla(1); b2 = bla(2)
 sage: b1 == b1
 True
 sage: b1 == b2
 False
 """
 return self is other

 # Should be cythoned
 def __ne__(self, other):
 """
 Test if ``self`` and ``other` are different by comparing their
 identity.

 See also :class:`UniqueRepresentation` for a discussion.

 EXAMPLES::

 sage: x = UniqueRepresentation()
 sage: y = UniqueRepresentation()
 sage: x != y
 False

 TESTS::

 sage: class bla(UniqueRepresentation, SageObject):
 ... def __init__(self, i): pass
 sage: b1 = bla(1); b2 = bla(2)
 sage: b1 != b1
 False
 sage: b1 != b2
 True
 """
 return self is not other

 # Should be cythoned
 def __hash__(self):
 """
 Returns the hash value of ``self``.

 See also :class:`UniqueRepresentation` for a discussion.

 EXAMPLES::

 sage: x = UniqueRepresentation()
 sage: y = UniqueRepresentation()
 sage: hash(x) # random
 74153040
 sage: type(hash(x))

 sage: hash(x) == hash(y)
 True
 sage: class bla(UniqueRepresentation, SageObject): pass
 sage: x = bla(); hx = hash(x)
 sage: x.rename("toto")
 sage: hx == hash(x)
 True
 """
 return id(self)

def __reduce__(self):
"""
Returns the argument that have been passed to :meth:`__new__`
to construct this object, as per the pickle protocol.
 See also :class:`UniqueRepresentation` for a discussion.
+ See also :class:`CachedRepresentation` and
+ :class:`UniqueRepresentation` for a discussion.
EXAMPLES::
sage: x = UniqueRepresentation()
 sage: x.__reduce__()
+ sage: x.__reduce__() # indirect doctest
(, (, (), {}))
"""
return (unreduce, self._reduction)
@@ 576,7 +475,7 @@
EXAMPLES::
sage: x = UniqueRepresentation()
 sage: x is copy(x)
+ sage: x is copy(x) # indirect doctest
True
"""
return self
@@ 591,7 +490,7 @@
sage: from copy import deepcopy
sage: x = UniqueRepresentation()
 sage: x is deepcopy(x)
+ sage: x is deepcopy(x) # indirect doctest
True
"""
return self
@@ 603,6 +502,163 @@
sage: sage.structure.unique_representation.unreduce(Integer, (1,), {})
1
 Todo: should reuse something preexisting ...
+ .. TODO::
+
+ should reuse something preexisting ...
+
"""
return cls(*args, **keywords)
+
+
+class UniqueRepresentation(CachedRepresentation, WithRichCmpById):
+ """
+ Classes derived from UniqueRepresentation inherit a unique
+ representation behavior for their instances.
+
+ EXAMPLES:
+
+ The short story: to construct a class whose instances have a
+ unique representation behavior one just has to do::
+
+ sage: class MyClass(UniqueRepresentation):
+ ... # all the rest as usual
+ ... pass
+
+ Everything below is for the curious or for advanced usage.
+
+ .. rubric:: What is unique representation?
+
+ Instances of a class have a *unique representation behavior* when
+ instances evaluate equal if and only if they are identical (i.e., share
+ the same memory representation), if and only if they were created using
+ equal arguments. For example, calling twice::
+
+ sage: f = GF(7)
+ sage: g = GF(7)
+
+ to create the finite field of order 7 actually gives back the same
+ object::
+
+ sage: f == g
+ True
+ sage: f is g
+ True
+
+ This is a standard design pattern. It allows for sharing cached data (say
+ representation theoretical information about a group) as well as for very
+ fast hashing and equality testing. This behaviour is typically desirable
+ for parents and categories. It can also be useful for intensive
+ computations where one wants to cache all the operations on a small set of
+ elements (say the multiplication table of a small group), and access this
+ cache as quickly as possible.
+
+ The :class:`UniqueRepresentation` and
+ :class:`~sage.structure.factory.UniqueFactory` classes provide two
+ alternative implementations of this design pattern. Both implementations
+ have their own merits. :class:`UniqueRepresentation` is very easy to use:
+ a class just needs to derive from it, or make sure some of its super
+ classes does. Also, it groups together the class and the factory in a
+ single gadget; in the example above, one would want to do::
+
+ sage: isinstance(f, GF) # todo: not implemented
+ True
+
+ but this does not work, because ``GF`` is only the factory.
+
+ On the other hand the :class:`UniqueRepresentation` class is more
+ intrusive, as it imposes a behavior (and a metaclass) to all the
+ subclasses. In particular, the unique representation behaviour is imposed
+ on *all* subclasses (unless the ``__classcall__`` method is overloaded and
+ not called in the subclass, which is not recommended). Its implementation
+ is also more technical, which leads to some subtleties.
+
+ EXAMPLES:
+
+ We start with a simple class whose constructor takes a single
+ value as argument (TODO: find a more meaningful example)::
+
+ sage: class MyClass(UniqueRepresentation):
+ ... def __init__(self, value):
+ ... self.value = value
+ ... def __cmp__(self, other):
+ ... c = cmp(type(self),type(other))
+ ... if c: return c
+ ... print "custom cmp"
+ ... return cmp(self.value, other.value)
+ ...
+
+ Two coexisting instances of MyClass created with the same argument data
+ are guaranteed to share the same identity. Since :trac:`12215`, this is
+ only the case if there is some strong reference to the returned instance,
+ since otherwise it may be garbage collected::
+
+ sage: x = MyClass(1)
+ sage: y = MyClass(1)
+ sage: x is y # There is a strong reference
+ True
+ sage: z = MyClass(2)
+ sage: x is z
+ False
+
+ In particular, modifying any one of them modifies the other
+ (reference effect)::
+
+ sage: x.value = 3
+ sage: x.value, y.value
+ (3, 3)
+ sage: y.value = 1
+ sage: x.value, y.value
+ (1, 1)
+
+ Rich comparison by identity is used when possible (hence, for ``==``, for
+ ``!=``, and for identical arguments in the case of ``<``, ``<=``, ``>=``
+ and ``>``), which is as fast as it can get. Only if identity is not enough
+ to decide the answer of a comparison, the custom comparison is called::
+
+ sage: x == y
+ True
+ sage: z = MyClass(2)
+ sage: x == z, x is z
+ (False, False)
+ sage: x <= x
+ True
+ sage: x != z
+ True
+ sage: x <= z
+ custom cmp
+ True
+ sage: x > z
+ custom cmp
+ False
+
+ A hash function equivalent to :meth:`object.__hash__` is used, which is
+ compatible with comparison by identity. However this means that the hash
+ function may change in between Sage sessions, or even within the same Sage
+ session.
+ ::
+
+ sage: hash(x) == object.__hash__(x)
+ True
+
+ .. WARNING::
+
+ It is possible to inherit from
+ :class:`~sage.structure.unique_representation.UniqueRepresentation`
+ and then overload comparison in a way that destroys the unique
+ representation property. We strongly recommend against it! You should
+ use :class:`~sage.structure.unique_representation.CachedRepresentation`
+ instead.
+
+ .. rubric:: Mixing super types and super classes
+
+ TESTS:
+
+ For the record, this test did fail with previous implementation
+ attempts::
+
+ sage: class bla(UniqueRepresentation, SageObject):
+ ... pass
+ ...
+ sage: b = bla()
+
+ """
diff git a/sage/tests/cython.pyx b/sage/tests/cython.pyx
 a/sage/tests/cython.pyx
+++ b/sage/tests/cython.pyx
@@ 11,7 +11,7 @@
# http://www.gnu.org/licenses/
#*****************************************************************************
from sage.categories.category_singleton cimport FastHashable_class
+from sage.misc.fast_methods cimport FastHashable_class
cdef class ClassWithLargeHash(FastHashable_class):
"""
This class tests against a bug with :class:`FastHashable_class`