passagemath-objects 10.6.45__cp313-cp313-musllinux_1_2_x86_64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of passagemath-objects might be problematic. Click here for more details.

Files changed (280) hide show
  1. passagemath_objects/__init__.py +3 -0
  2. passagemath_objects-10.6.45.dist-info/METADATA +115 -0
  3. passagemath_objects-10.6.45.dist-info/RECORD +280 -0
  4. passagemath_objects-10.6.45.dist-info/WHEEL +5 -0
  5. passagemath_objects-10.6.45.dist-info/top_level.txt +3 -0
  6. passagemath_objects.libs/libgmp-0e7fc84e.so.10.5.0 +0 -0
  7. sage/all__sagemath_objects.py +37 -0
  8. sage/arith/all__sagemath_objects.py +5 -0
  9. sage/arith/long.pxd +411 -0
  10. sage/arith/numerical_approx.cpython-313-x86_64-linux-musl.so +0 -0
  11. sage/arith/numerical_approx.pxd +35 -0
  12. sage/arith/numerical_approx.pyx +75 -0
  13. sage/arith/power.cpython-313-x86_64-linux-musl.so +0 -0
  14. sage/arith/power.pxd +31 -0
  15. sage/arith/power.pyx +127 -0
  16. sage/categories/action.cpython-313-x86_64-linux-musl.so +0 -0
  17. sage/categories/action.pxd +29 -0
  18. sage/categories/action.pyx +641 -0
  19. sage/categories/algebra_functor.py +745 -0
  20. sage/categories/all__sagemath_objects.py +33 -0
  21. sage/categories/basic.py +62 -0
  22. sage/categories/cartesian_product.py +295 -0
  23. sage/categories/category.py +3401 -0
  24. sage/categories/category_cy_helper.cpython-313-x86_64-linux-musl.so +0 -0
  25. sage/categories/category_cy_helper.pxd +8 -0
  26. sage/categories/category_cy_helper.pyx +322 -0
  27. sage/categories/category_singleton.cpython-313-x86_64-linux-musl.so +0 -0
  28. sage/categories/category_singleton.pxd +3 -0
  29. sage/categories/category_singleton.pyx +342 -0
  30. sage/categories/category_types.py +637 -0
  31. sage/categories/category_with_axiom.py +2876 -0
  32. sage/categories/covariant_functorial_construction.py +703 -0
  33. sage/categories/facade_sets.py +228 -0
  34. sage/categories/functor.cpython-313-x86_64-linux-musl.so +0 -0
  35. sage/categories/functor.pxd +7 -0
  36. sage/categories/functor.pyx +691 -0
  37. sage/categories/homset.py +1338 -0
  38. sage/categories/homsets.py +364 -0
  39. sage/categories/isomorphic_objects.py +73 -0
  40. sage/categories/map.cpython-313-x86_64-linux-musl.so +0 -0
  41. sage/categories/map.pxd +34 -0
  42. sage/categories/map.pyx +2106 -0
  43. sage/categories/morphism.cpython-313-x86_64-linux-musl.so +0 -0
  44. sage/categories/morphism.pxd +14 -0
  45. sage/categories/morphism.pyx +895 -0
  46. sage/categories/objects.py +167 -0
  47. sage/categories/primer.py +1696 -0
  48. sage/categories/pushout.py +4834 -0
  49. sage/categories/quotients.py +64 -0
  50. sage/categories/realizations.py +200 -0
  51. sage/categories/sets_cat.py +3290 -0
  52. sage/categories/sets_with_partial_maps.py +52 -0
  53. sage/categories/subobjects.py +64 -0
  54. sage/categories/subquotients.py +21 -0
  55. sage/categories/with_realizations.py +311 -0
  56. sage/cpython/__init__.py +19 -0
  57. sage/cpython/_py2_random.py +619 -0
  58. sage/cpython/all.py +3 -0
  59. sage/cpython/atexit.cpython-313-x86_64-linux-musl.so +0 -0
  60. sage/cpython/atexit.pyx +269 -0
  61. sage/cpython/builtin_types.cpython-313-x86_64-linux-musl.so +0 -0
  62. sage/cpython/builtin_types.pyx +7 -0
  63. sage/cpython/cython_metaclass.cpython-313-x86_64-linux-musl.so +0 -0
  64. sage/cpython/cython_metaclass.h +117 -0
  65. sage/cpython/cython_metaclass.pxd +3 -0
  66. sage/cpython/cython_metaclass.pyx +130 -0
  67. sage/cpython/debug.cpython-313-x86_64-linux-musl.so +0 -0
  68. sage/cpython/debug.pyx +302 -0
  69. sage/cpython/dict_del_by_value.cpython-313-x86_64-linux-musl.so +0 -0
  70. sage/cpython/dict_del_by_value.pxd +9 -0
  71. sage/cpython/dict_del_by_value.pyx +191 -0
  72. sage/cpython/dict_internal.h +245 -0
  73. sage/cpython/getattr.cpython-313-x86_64-linux-musl.so +0 -0
  74. sage/cpython/getattr.pxd +9 -0
  75. sage/cpython/getattr.pyx +439 -0
  76. sage/cpython/pycore_long.h +97 -0
  77. sage/cpython/pycore_long.pxd +10 -0
  78. sage/cpython/python_debug.h +44 -0
  79. sage/cpython/python_debug.pxd +47 -0
  80. sage/cpython/pyx_visit.h +13 -0
  81. sage/cpython/string.cpython-313-x86_64-linux-musl.so +0 -0
  82. sage/cpython/string.pxd +76 -0
  83. sage/cpython/string.pyx +34 -0
  84. sage/cpython/string_impl.h +60 -0
  85. sage/cpython/type.cpython-313-x86_64-linux-musl.so +0 -0
  86. sage/cpython/type.pxd +2 -0
  87. sage/cpython/type.pyx +40 -0
  88. sage/cpython/wrapperdescr.pxd +67 -0
  89. sage/ext/all__sagemath_objects.py +3 -0
  90. sage/ext/ccobject.h +64 -0
  91. sage/ext/cplusplus.pxd +17 -0
  92. sage/ext/mod_int.h +30 -0
  93. sage/ext/mod_int.pxd +24 -0
  94. sage/ext/stdsage.pxd +39 -0
  95. sage/groups/all__sagemath_objects.py +1 -0
  96. sage/groups/group.cpython-313-x86_64-linux-musl.so +0 -0
  97. sage/groups/group.pxd +14 -0
  98. sage/groups/group.pyx +322 -0
  99. sage/groups/old.cpython-313-x86_64-linux-musl.so +0 -0
  100. sage/groups/old.pxd +14 -0
  101. sage/groups/old.pyx +219 -0
  102. sage/libs/all__sagemath_objects.py +3 -0
  103. sage/libs/gmp/__init__.py +1 -0
  104. sage/libs/gmp/all.pxd +6 -0
  105. sage/libs/gmp/binop.pxd +23 -0
  106. sage/libs/gmp/misc.pxd +8 -0
  107. sage/libs/gmp/mpf.pxd +88 -0
  108. sage/libs/gmp/mpn.pxd +57 -0
  109. sage/libs/gmp/mpq.pxd +57 -0
  110. sage/libs/gmp/mpz.pxd +202 -0
  111. sage/libs/gmp/pylong.cpython-313-x86_64-linux-musl.so +0 -0
  112. sage/libs/gmp/pylong.pxd +12 -0
  113. sage/libs/gmp/pylong.pyx +150 -0
  114. sage/libs/gmp/random.pxd +25 -0
  115. sage/libs/gmp/randomize.pxd +59 -0
  116. sage/libs/gmp/types.pxd +53 -0
  117. sage/libs/gmpxx.pxd +19 -0
  118. sage/misc/abstract_method.py +276 -0
  119. sage/misc/all__sagemath_objects.py +43 -0
  120. sage/misc/bindable_class.py +253 -0
  121. sage/misc/c3_controlled.cpython-313-x86_64-linux-musl.so +0 -0
  122. sage/misc/c3_controlled.pxd +2 -0
  123. sage/misc/c3_controlled.pyx +1402 -0
  124. sage/misc/cachefunc.cpython-313-x86_64-linux-musl.so +0 -0
  125. sage/misc/cachefunc.pxd +43 -0
  126. sage/misc/cachefunc.pyx +3781 -0
  127. sage/misc/call.py +188 -0
  128. sage/misc/classcall_metaclass.cpython-313-x86_64-linux-musl.so +0 -0
  129. sage/misc/classcall_metaclass.pxd +14 -0
  130. sage/misc/classcall_metaclass.pyx +599 -0
  131. sage/misc/constant_function.cpython-313-x86_64-linux-musl.so +0 -0
  132. sage/misc/constant_function.pyx +130 -0
  133. sage/misc/decorators.py +747 -0
  134. sage/misc/fast_methods.cpython-313-x86_64-linux-musl.so +0 -0
  135. sage/misc/fast_methods.pxd +20 -0
  136. sage/misc/fast_methods.pyx +351 -0
  137. sage/misc/flatten.py +90 -0
  138. sage/misc/fpickle.cpython-313-x86_64-linux-musl.so +0 -0
  139. sage/misc/fpickle.pyx +177 -0
  140. sage/misc/function_mangling.cpython-313-x86_64-linux-musl.so +0 -0
  141. sage/misc/function_mangling.pxd +11 -0
  142. sage/misc/function_mangling.pyx +308 -0
  143. sage/misc/inherit_comparison.cpython-313-x86_64-linux-musl.so +0 -0
  144. sage/misc/inherit_comparison.pxd +5 -0
  145. sage/misc/inherit_comparison.pyx +105 -0
  146. sage/misc/instancedoc.cpython-313-x86_64-linux-musl.so +0 -0
  147. sage/misc/instancedoc.pyx +331 -0
  148. sage/misc/lazy_attribute.cpython-313-x86_64-linux-musl.so +0 -0
  149. sage/misc/lazy_attribute.pyx +607 -0
  150. sage/misc/lazy_format.py +135 -0
  151. sage/misc/lazy_import.cpython-313-x86_64-linux-musl.so +0 -0
  152. sage/misc/lazy_import.pyx +1299 -0
  153. sage/misc/lazy_import_cache.py +36 -0
  154. sage/misc/lazy_list.cpython-313-x86_64-linux-musl.so +0 -0
  155. sage/misc/lazy_list.pxd +19 -0
  156. sage/misc/lazy_list.pyx +1187 -0
  157. sage/misc/lazy_string.cpython-313-x86_64-linux-musl.so +0 -0
  158. sage/misc/lazy_string.pxd +7 -0
  159. sage/misc/lazy_string.pyx +546 -0
  160. sage/misc/misc.py +1066 -0
  161. sage/misc/misc_c.cpython-313-x86_64-linux-musl.so +0 -0
  162. sage/misc/misc_c.pxd +3 -0
  163. sage/misc/misc_c.pyx +766 -0
  164. sage/misc/namespace_package.py +37 -0
  165. sage/misc/nested_class.cpython-313-x86_64-linux-musl.so +0 -0
  166. sage/misc/nested_class.pxd +3 -0
  167. sage/misc/nested_class.pyx +394 -0
  168. sage/misc/persist.cpython-313-x86_64-linux-musl.so +0 -0
  169. sage/misc/persist.pyx +1251 -0
  170. sage/misc/prandom.py +418 -0
  171. sage/misc/randstate.cpython-313-x86_64-linux-musl.so +0 -0
  172. sage/misc/randstate.pxd +30 -0
  173. sage/misc/randstate.pyx +1059 -0
  174. sage/misc/repr.py +203 -0
  175. sage/misc/reset.cpython-313-x86_64-linux-musl.so +0 -0
  176. sage/misc/reset.pyx +196 -0
  177. sage/misc/sage_ostools.cpython-313-x86_64-linux-musl.so +0 -0
  178. sage/misc/sage_ostools.pyx +323 -0
  179. sage/misc/sage_timeit.py +275 -0
  180. sage/misc/sage_timeit_class.cpython-313-x86_64-linux-musl.so +0 -0
  181. sage/misc/sage_timeit_class.pyx +120 -0
  182. sage/misc/sage_unittest.py +637 -0
  183. sage/misc/sageinspect.py +2768 -0
  184. sage/misc/session.cpython-313-x86_64-linux-musl.so +0 -0
  185. sage/misc/session.pyx +392 -0
  186. sage/misc/superseded.py +557 -0
  187. sage/misc/test_nested_class.py +228 -0
  188. sage/misc/timing.py +264 -0
  189. sage/misc/unknown.py +222 -0
  190. sage/misc/verbose.py +253 -0
  191. sage/misc/weak_dict.cpython-313-x86_64-linux-musl.so +0 -0
  192. sage/misc/weak_dict.pxd +15 -0
  193. sage/misc/weak_dict.pyx +1231 -0
  194. sage/modules/all__sagemath_objects.py +1 -0
  195. sage/modules/module.cpython-313-x86_64-linux-musl.so +0 -0
  196. sage/modules/module.pxd +5 -0
  197. sage/modules/module.pyx +329 -0
  198. sage/rings/all__sagemath_objects.py +3 -0
  199. sage/rings/integer_fake.h +22 -0
  200. sage/rings/integer_fake.pxd +55 -0
  201. sage/sets/all__sagemath_objects.py +3 -0
  202. sage/sets/pythonclass.cpython-313-x86_64-linux-musl.so +0 -0
  203. sage/sets/pythonclass.pxd +9 -0
  204. sage/sets/pythonclass.pyx +247 -0
  205. sage/structure/__init__.py +4 -0
  206. sage/structure/all.py +30 -0
  207. sage/structure/category_object.cpython-313-x86_64-linux-musl.so +0 -0
  208. sage/structure/category_object.pxd +28 -0
  209. sage/structure/category_object.pyx +1087 -0
  210. sage/structure/coerce.cpython-313-x86_64-linux-musl.so +0 -0
  211. sage/structure/coerce.pxd +44 -0
  212. sage/structure/coerce.pyx +2107 -0
  213. sage/structure/coerce_actions.cpython-313-x86_64-linux-musl.so +0 -0
  214. sage/structure/coerce_actions.pxd +27 -0
  215. sage/structure/coerce_actions.pyx +988 -0
  216. sage/structure/coerce_dict.cpython-313-x86_64-linux-musl.so +0 -0
  217. sage/structure/coerce_dict.pxd +51 -0
  218. sage/structure/coerce_dict.pyx +1557 -0
  219. sage/structure/coerce_exceptions.py +23 -0
  220. sage/structure/coerce_maps.cpython-313-x86_64-linux-musl.so +0 -0
  221. sage/structure/coerce_maps.pxd +28 -0
  222. sage/structure/coerce_maps.pyx +718 -0
  223. sage/structure/debug_options.cpython-313-x86_64-linux-musl.so +0 -0
  224. sage/structure/debug_options.pxd +6 -0
  225. sage/structure/debug_options.pyx +54 -0
  226. sage/structure/dynamic_class.py +541 -0
  227. sage/structure/element.cpython-313-x86_64-linux-musl.so +0 -0
  228. sage/structure/element.pxd +272 -0
  229. sage/structure/element.pyx +4772 -0
  230. sage/structure/element_wrapper.cpython-313-x86_64-linux-musl.so +0 -0
  231. sage/structure/element_wrapper.pxd +12 -0
  232. sage/structure/element_wrapper.pyx +582 -0
  233. sage/structure/factorization.py +1422 -0
  234. sage/structure/factorization_integer.py +105 -0
  235. sage/structure/factory.cpython-313-x86_64-linux-musl.so +0 -0
  236. sage/structure/factory.pyx +786 -0
  237. sage/structure/formal_sum.py +489 -0
  238. sage/structure/gens_py.py +73 -0
  239. sage/structure/global_options.py +1743 -0
  240. sage/structure/indexed_generators.py +863 -0
  241. sage/structure/list_clone.cpython-313-x86_64-linux-musl.so +0 -0
  242. sage/structure/list_clone.pxd +65 -0
  243. sage/structure/list_clone.pyx +1867 -0
  244. sage/structure/list_clone_demo.cpython-313-x86_64-linux-musl.so +0 -0
  245. sage/structure/list_clone_demo.pyx +248 -0
  246. sage/structure/list_clone_timings.py +179 -0
  247. sage/structure/list_clone_timings_cy.cpython-313-x86_64-linux-musl.so +0 -0
  248. sage/structure/list_clone_timings_cy.pyx +86 -0
  249. sage/structure/mutability.cpython-313-x86_64-linux-musl.so +0 -0
  250. sage/structure/mutability.pxd +21 -0
  251. sage/structure/mutability.pyx +348 -0
  252. sage/structure/nonexact.py +69 -0
  253. sage/structure/parent.cpython-313-x86_64-linux-musl.so +0 -0
  254. sage/structure/parent.pxd +112 -0
  255. sage/structure/parent.pyx +3093 -0
  256. sage/structure/parent_base.cpython-313-x86_64-linux-musl.so +0 -0
  257. sage/structure/parent_base.pxd +13 -0
  258. sage/structure/parent_base.pyx +44 -0
  259. sage/structure/parent_gens.cpython-313-x86_64-linux-musl.so +0 -0
  260. sage/structure/parent_gens.pxd +22 -0
  261. sage/structure/parent_gens.pyx +377 -0
  262. sage/structure/parent_old.cpython-313-x86_64-linux-musl.so +0 -0
  263. sage/structure/parent_old.pxd +25 -0
  264. sage/structure/parent_old.pyx +294 -0
  265. sage/structure/proof/__init__.py +1 -0
  266. sage/structure/proof/all.py +243 -0
  267. sage/structure/proof/proof.py +300 -0
  268. sage/structure/richcmp.cpython-313-x86_64-linux-musl.so +0 -0
  269. sage/structure/richcmp.pxd +213 -0
  270. sage/structure/richcmp.pyx +495 -0
  271. sage/structure/sage_object.cpython-313-x86_64-linux-musl.so +0 -0
  272. sage/structure/sage_object.pxd +3 -0
  273. sage/structure/sage_object.pyx +988 -0
  274. sage/structure/sage_object_test.py +19 -0
  275. sage/structure/sequence.py +937 -0
  276. sage/structure/set_factories.py +1178 -0
  277. sage/structure/set_factories_example.py +527 -0
  278. sage/structure/support_view.py +179 -0
  279. sage/structure/test_factory.py +56 -0
  280. sage/structure/unique_representation.py +1359 -0
@@ -0,0 +1,1557 @@
1
+ # sage_setup: distribution = sagemath-objects
2
+ """
3
+ Containers for storing coercion data
4
+
5
+ This module provides :class:`TripleDict` and :class:`MonoDict`. These are
6
+ structures similar to :class:`~weakref.WeakKeyDictionary` in Python's weakref
7
+ module, and are optimized for lookup speed. The keys for :class:`TripleDict`
8
+ consist of triples (k1,k2,k3) and are looked up by identity rather than
9
+ equality. The keys are stored by weakrefs if possible. If any one of the
10
+ components k1, k2, k3 gets garbage collected, then the entry is removed from
11
+ the :class:`TripleDict`.
12
+
13
+ Key components that do not allow for weakrefs are stored via a normal
14
+ refcounted reference. That means that any entry stored using a triple
15
+ (k1,k2,k3) so that none of the k1,k2,k3 allows a weak reference behaves
16
+ as an entry in a normal dictionary: Its existence in :class:`TripleDict`
17
+ prevents it from being garbage collected.
18
+
19
+ That container currently is used to store coercion and conversion maps between
20
+ two parents (:issue:`715`) and to store homsets of pairs of objects of a
21
+ category (:issue:`11521`). In both cases, it is essential that the parent
22
+ structures remain garbage collectable, it is essential that the data access is
23
+ faster than with a usual :class:`~weakref.WeakKeyDictionary`, and we enforce
24
+ the "unique parent condition" in Sage (parent structures should be identical
25
+ if they are equal).
26
+
27
+ :class:`MonoDict` behaves similarly, but it takes a single item as a key. It
28
+ is used for caching the parents which allow a coercion map into a fixed other
29
+ parent (:issue:`12313`).
30
+
31
+ By :issue:`14159`, :class:`MonoDict` and :class:`TripleDict` can be optionally
32
+ used with weak references on the values.
33
+
34
+ Note that this kind of dictionary is also used for caching actions and
35
+ coerce maps. In previous versions of Sage, the cache was by strong
36
+ references and resulted in a memory leak in the following example.
37
+ However, this leak was fixed by :issue:`715`, using weak references::
38
+
39
+ sage: # needs sage.combinat sage.modules sage.rings.finite_rings
40
+ sage: K.<t> = GF(2^55)
41
+ sage: for i in range(20):
42
+ ....: a = K.random_element()
43
+ ....: E = EllipticCurve(j=a)
44
+ ....: P = E.random_point()
45
+ ....: Q = 2*P
46
+ sage: L = [Partitions(n) for n in range(200)] # purge strong cache in CachedRepresentation
47
+ sage: import gc
48
+ sage: n = gc.collect()
49
+ sage: from sage.schemes.elliptic_curves.ell_finite_field import EllipticCurve_finite_field
50
+ sage: LE = [x for x in gc.get_objects() if isinstance(x, EllipticCurve_finite_field)]
51
+ sage: len(LE)
52
+ 1
53
+ """
54
+
55
+ #*****************************************************************************
56
+ # Copyright (C) 2007 Robert Bradshaw <robertwb@math.washington.edu>
57
+ # 2012 Simon King <simon.king@uni-jena.de>
58
+ # 2013 Nils Bruin <nbruin@sfu.ca>
59
+ #
60
+ # This program is free software: you can redistribute it and/or modify
61
+ # it under the terms of the GNU General Public License as published by
62
+ # the Free Software Foundation, either version 2 of the License, or
63
+ # (at your option) any later version.
64
+ # http://www.gnu.org/licenses/
65
+ #*****************************************************************************
66
+
67
+ cimport cython
68
+ from cpython.object cimport *
69
+ from cpython.ref cimport Py_XINCREF, Py_XDECREF, Py_CLEAR
70
+ from cpython.tuple cimport PyTuple_New
71
+ from cpython.weakref cimport PyWeakref_GetObject, PyWeakref_GET_OBJECT
72
+ from cysignals.memory cimport check_calloc, sig_free
73
+
74
+ cdef extern from "Python.h":
75
+ void PyTuple_SET_ITEM(object tuple, Py_ssize_t index, PyObject* item)
76
+
77
+ cdef extern from "../cpython/pyx_visit.h":
78
+ void Py_VISIT3(PyObject*, visitproc, void*)
79
+
80
+ cdef type KeyedRef, ref
81
+ from weakref import KeyedRef, ref
82
+
83
+ cdef inline bint is_dead_keyedref(x) noexcept:
84
+ """
85
+ Check whether ``x`` is a ``KeyedRef`` which is dead.
86
+ """
87
+ if type(x) is not KeyedRef:
88
+ return False
89
+ return PyWeakref_GET_OBJECT(x) is <PyObject*>None
90
+
91
+
92
+ # Unique sentinel to indicate a deleted cell
93
+ cdef object dummy = object()
94
+ cdef PyObject* deleted_key = <PyObject*>dummy
95
+
96
+
97
+ cdef inline bint valid(PyObject* obj) noexcept:
98
+ """
99
+ Check whether ``obj`` points to a valid object
100
+ """
101
+ return obj is not NULL and obj is not deleted_key
102
+
103
+
104
+ @cython.freelist(256)
105
+ cdef class ObjectWrapper:
106
+ """
107
+ A simple fast wrapper around a Python object. This is like a
108
+ 1-element tuple except that it does not keep a reference to the
109
+ wrapped object.
110
+ """
111
+ cdef PyObject* obj
112
+
113
+
114
+ cdef inline ObjectWrapper wrap(obj):
115
+ """
116
+ Wrap a given Python object in an :class:`ObjectWrapper`.
117
+ """
118
+ cdef ObjectWrapper w = <ObjectWrapper>(ObjectWrapper.__new__(ObjectWrapper))
119
+ w.obj = <PyObject*>obj
120
+ return w
121
+
122
+
123
+ cdef inline PyObject* unwrap(w) except? NULL:
124
+ """
125
+ Return the object wrapped by an :class:`ObjectWrapper`.
126
+ """
127
+ return (<ObjectWrapper?>w).obj
128
+
129
+
130
+ cdef extract_mono_cell(mono_cell* cell):
131
+ """
132
+ Take the refcounted components from a mono_cell, put them in a
133
+ tuple and return it. The mono_cell itself is marked as "freed".
134
+ The refcounts originally accounting for the presence in the
135
+ mono_cell now account for the presence in the returned tuple,
136
+ which steals those references.
137
+
138
+ The returned result is only used to throw away: an advantage is
139
+ that the containing tuple participates in CPython's trashcan,
140
+ which prevents stack overflow on large dereffing cascades.
141
+
142
+ A slight disadvantage is that this routine needs to allocate a
143
+ tuple (mainly just to be thrown away)
144
+ """
145
+ assert valid(cell.key_id)
146
+ t = PyTuple_New(2)
147
+ PyTuple_SET_ITEM(t, 0, cell.key_weakref)
148
+ PyTuple_SET_ITEM(t, 1, cell.value)
149
+ cell.key_id = deleted_key
150
+ cell.key_weakref = NULL
151
+ cell.value = NULL
152
+ return t
153
+
154
+
155
+ cdef extract_triple_cell(triple_cell* cell):
156
+ # See extract_mono_cell for documentation
157
+ assert valid(cell.key_id1)
158
+ t = PyTuple_New(4)
159
+ PyTuple_SET_ITEM(t, 0, cell.key_weakref1)
160
+ PyTuple_SET_ITEM(t, 1, cell.key_weakref2)
161
+ PyTuple_SET_ITEM(t, 2, cell.key_weakref3)
162
+ PyTuple_SET_ITEM(t, 3, cell.value)
163
+ cell.key_id1 = deleted_key
164
+ cell.key_id2 = NULL
165
+ cell.key_id3 = NULL
166
+ cell.key_weakref1 = NULL
167
+ cell.key_weakref2 = NULL
168
+ cell.key_weakref3 = NULL
169
+ cell.value = NULL
170
+ return t
171
+
172
+
173
+ cdef class MonoDictEraser:
174
+ """
175
+ Erase items from a :class:`MonoDict` when a weak reference becomes
176
+ invalid.
177
+
178
+ This is of internal use only. Instances of this class will be passed as a
179
+ callback function when creating a weak reference.
180
+
181
+ EXAMPLES::
182
+
183
+ sage: from sage.structure.coerce_dict import MonoDict
184
+ sage: class A: pass
185
+ sage: a = A()
186
+ sage: M = MonoDict()
187
+ sage: M[a] = 1
188
+ sage: len(M)
189
+ 1
190
+ sage: del a
191
+ sage: import gc
192
+ sage: n = gc.collect()
193
+ sage: len(M) # indirect doctest
194
+ 0
195
+
196
+ AUTHOR:
197
+
198
+ - Simon King (2012-01)
199
+ - Nils Bruin (2013-11)
200
+ """
201
+ cdef D
202
+
203
+ def __init__(self, D):
204
+ """
205
+ INPUT:
206
+
207
+ - ``D`` -- a :class:`MonoDict`
208
+
209
+ EXAMPLES::
210
+
211
+ sage: k = set([1])
212
+ sage: D = sage.structure.coerce_dict.MonoDict([(k,1)])
213
+ sage: len(D)
214
+ 1
215
+ sage: del k
216
+ sage: len(D) # indirect doctest
217
+ 0
218
+ """
219
+ self.D = ref(D)
220
+
221
+ def __call__(self, r):
222
+ """
223
+ INPUT:
224
+
225
+ - ``r`` -- a weak reference with key
226
+
227
+ For internal use only.
228
+
229
+ EXAMPLES::
230
+
231
+ sage: k = set([1])
232
+ sage: D = sage.structure.coerce_dict.MonoDict([(k,1)])
233
+ sage: len(D)
234
+ 1
235
+ sage: del k
236
+ sage: len(D) # indirect doctest
237
+ 0
238
+ """
239
+ cdef MonoDict md = <MonoDict>PyWeakref_GetObject(self.D)
240
+ if md is None or not md.mask:
241
+ return
242
+ cdef mono_cell* cursor = md.lookup(unwrap(r.key))
243
+ cdef PyObject* r_ = <PyObject*>r
244
+ if valid(cursor.key_id):
245
+ if cursor.key_weakref is r_ or cursor.value is r_:
246
+ L = extract_mono_cell(cursor)
247
+ md.used -= 1
248
+ else:
249
+ raise AssertionError("MonoDictEraser: key match but no weakref match")
250
+
251
+
252
+ cdef class MonoDict:
253
+ """
254
+ This is a hashtable specifically designed for (read) speed in
255
+ the coercion model.
256
+
257
+ It differs from a python WeakKeyDictionary in the following important ways:
258
+
259
+ - Comparison is done using the 'is' rather than '==' operator.
260
+ - Only weak references to the keys are stored if at all possible.
261
+ Keys that do not allow for weak references are stored with a normal
262
+ refcounted reference.
263
+ - The callback of the weak references is safe against recursion, see below.
264
+
265
+ There are special cdef set/get methods for faster access.
266
+ It is bare-bones in the sense that not all dictionary methods are
267
+ implemented.
268
+
269
+ IMPLEMENTATION:
270
+
271
+ It is implemented as a hash table with open addressing, similar to python's
272
+ dict.
273
+
274
+ INPUT:
275
+
276
+ - ``data`` -- (optional) iterable defining initial data, as dict or
277
+ iterable of (key, value) pairs
278
+
279
+ - ``weak_values`` -- boolean (default: ``False``); if it is
280
+ ``True``, weak references to the values in this dictionary will be used,
281
+ when possible
282
+
283
+ EXAMPLES::
284
+
285
+ sage: from sage.structure.coerce_dict import MonoDict
286
+ sage: L = MonoDict()
287
+ sage: a = 'a'; b = 'ab'; c = '-15'
288
+ sage: L[a] = 1
289
+ sage: L[b] = 2
290
+ sage: L[c] = 3
291
+
292
+ The key is expected to be a unique object. Hence, the item stored for ``c``
293
+ cannot be obtained by providing another equal string::
294
+
295
+ sage: L[a]
296
+ 1
297
+ sage: L[b]
298
+ 2
299
+ sage: L[c]
300
+ 3
301
+ sage: L['-15']
302
+ Traceback (most recent call last):
303
+ ...
304
+ KeyError: '-15'
305
+
306
+ Not all features of Python dictionaries are available, but iteration over
307
+ the dictionary items is possible::
308
+
309
+ sage: sorted(L.items())
310
+ [('-15', 3), ('a', 1), ('ab', 2)]
311
+ sage: del L[c]
312
+ sage: sorted(L.items())
313
+ [('a', 1), ('ab', 2)]
314
+ sage: len(L)
315
+ 2
316
+ sage: for i in range(1000):
317
+ ....: L[i] = i
318
+ sage: len(L)
319
+ 1002
320
+ sage: L['a']
321
+ 1
322
+ sage: L['c']
323
+ Traceback (most recent call last):
324
+ ...
325
+ KeyError: 'c'
326
+
327
+ TESTS:
328
+
329
+ Here, we demonstrate the use of weak values::
330
+
331
+ sage: M = MonoDict()
332
+ sage: MW = MonoDict(weak_values=True)
333
+ sage: class Foo: pass
334
+ sage: a = Foo()
335
+ sage: b = Foo()
336
+ sage: k = 1
337
+ sage: M[k] = a
338
+ sage: MW[k] = b
339
+ sage: M[k] is a
340
+ True
341
+ sage: MW[k] is b
342
+ True
343
+ sage: k in M
344
+ True
345
+ sage: k in MW
346
+ True
347
+
348
+ While ``M`` uses a strong reference to ``a``, ``MW`` uses a *weak*
349
+ reference to ``b``, and after deleting ``b``, the corresponding item of
350
+ ``MW`` will be removed during the next garbage collection::
351
+
352
+ sage: import gc
353
+ sage: del a,b
354
+ sage: _ = gc.collect()
355
+ sage: k in M
356
+ True
357
+ sage: k in MW
358
+ False
359
+ sage: len(MW)
360
+ 0
361
+ sage: len(M)
362
+ 1
363
+
364
+ Note that ``MW`` also accepts values that do not allow for weak references::
365
+
366
+ sage: MW[k] = int(5)
367
+ sage: MW[k]
368
+ 5
369
+
370
+ The following demonstrates that :class:`MonoDict` is safer than
371
+ :class:`~weakref.WeakKeyDictionary` against recursions created by nested
372
+ callbacks; compare :issue:`15069` (the mechanism used now is different, though)::
373
+
374
+ sage: M = MonoDict()
375
+ sage: class A: pass
376
+ sage: a = A()
377
+ sage: prev = a
378
+ sage: for i in range(1000):
379
+ ....: newA = A()
380
+ ....: M[prev] = newA
381
+ ....: prev = newA
382
+ sage: len(M)
383
+ 1000
384
+ sage: del a
385
+ sage: len(M)
386
+ 0
387
+
388
+ The corresponding example with a Python :class:`weakref.WeakKeyDictionary`
389
+ would result in a too deep recursion during deletion of the dictionary
390
+ items::
391
+
392
+ sage: import weakref
393
+ sage: M = weakref.WeakKeyDictionary()
394
+ sage: a = A()
395
+ sage: prev = a
396
+ sage: for i in range(1000):
397
+ ....: newA = A()
398
+ ....: M[prev] = newA
399
+ ....: prev = newA
400
+ sage: len(M)
401
+ 1000
402
+
403
+ Check that also in the presence of circular references, :class:`MonoDict`
404
+ gets properly collected::
405
+
406
+ sage: import gc
407
+ sage: def count_type(T):
408
+ ....: return len([c for c in gc.get_objects() if isinstance(c,T)])
409
+ sage: gc.freeze() # so that gc.collect() only deals with our trash
410
+ sage: N = count_type(MonoDict)
411
+ sage: for i in range(100):
412
+ ....: V = [MonoDict({"id":j+100*i}) for j in range(100)]
413
+ ....: n = len(V)
414
+ ....: for i in range(n): V[i][V[(i+1)%n]] = (i+1)%n
415
+ ....: del V
416
+ ....: _ = gc.collect()
417
+ ....: assert count_type(MonoDict) == N
418
+ sage: count_type(MonoDict) == N
419
+ True
420
+ sage: gc.unfreeze()
421
+
422
+ AUTHORS:
423
+
424
+ - Simon King (2012-01)
425
+ - Nils Bruin (2012-08)
426
+ - Simon King (2013-02)
427
+ - Nils Bruin (2013-11)
428
+ """
429
+ cdef mono_cell* lookup(self, PyObject* key) noexcept:
430
+ """
431
+ Return a pointer to where ``key`` should be stored in this
432
+ :class:`MonoDict`.
433
+
434
+ This routine is used for all cases where a (potential) spot for
435
+ a key is looked up. The returned value is a pointer into the dictionary
436
+ store that either contains an entry with the requested key or a free spot
437
+ where an entry for that key should go.
438
+ """
439
+ assert valid(key)
440
+
441
+ cdef size_t mask = self.mask
442
+ cdef mono_cell* table = self.table
443
+ cdef mono_cell* first_deleted = NULL
444
+
445
+ # Use the memory location of the key as starting point for our
446
+ # hash.
447
+ cdef size_t h = <size_t>key
448
+
449
+ # The size of a Python object is at least 2 * sizeof(size_t).
450
+ # Therefore, we don't lose any information by dividing by that.
451
+ # Instead, the lower order bits become more interesting.
452
+ h //= 2 * sizeof(size_t)
453
+
454
+ # Bring some higher-order bits in with this permutation.
455
+ cdef size_t i = (h >> 8) ^ h
456
+
457
+ cdef size_t perturb = h
458
+
459
+ # The probing algorithm is heavily inspired by Python dicts.
460
+ # There is always at least one NULL entry in the store, and the
461
+ # probe sequence eventually covers the entire store (see Theorem
462
+ # below), so the loop below does terminate. Since this loop does
463
+ # not change any refcounts, we know that table will not change
464
+ # during iteration.
465
+
466
+ # Theorem: when iterating the function i -> 5*i + 1, every
467
+ # element of Z/(2^n Z) is reached.
468
+ # Proof: define f(x) = 4*x + 1. Then f(5*i + 1) = 5*f(i).
469
+ # Therefore, the iteration is really a transformation of
470
+ # i -> 5*i on the group of 1 mod 4 elements of (Z/2^(n+2) Z).
471
+ # It is a well known fact that this is a cyclic group generated
472
+ # by 5 (or any element which is 5 mod 8).
473
+ cdef mono_cell* cursor
474
+ while True:
475
+ cursor = &(table[i & mask])
476
+ perturb >>= 5
477
+ if cursor.key_id is key:
478
+ return cursor
479
+ elif cursor.key_id is NULL:
480
+ return first_deleted or cursor
481
+ elif cursor.key_id is deleted_key:
482
+ if first_deleted is NULL:
483
+ first_deleted = cursor
484
+ i = (5*i + 1) + perturb
485
+
486
+ cdef int resize(self) except -1:
487
+ """
488
+ Resize dictionary. That can also mean shrink! Size is always a power of 2.
489
+ """
490
+ cdef mono_cell* old_table = self.table
491
+ cdef size_t old_mask = self.mask
492
+ cdef size_t newsize = 8
493
+ cdef size_t minsize = 2 * self.used
494
+ cdef mono_cell* cursor
495
+ cdef mono_cell* entry
496
+ while newsize < minsize:
497
+ newsize *= 2
498
+ cdef mono_cell* table = <mono_cell*>check_calloc(newsize, sizeof(mono_cell))
499
+
500
+ # We are done with memory activity. We can move the new (empty)
501
+ # table into place:
502
+ self.table = table
503
+ self.mask = newsize - 1
504
+ self.used = 0
505
+ self.fill = 0
506
+
507
+ # We now move all entries over. We are not changing any
508
+ # refcounts here, so this is a very tight loop that doesn't need
509
+ # to worry about tables changing.
510
+ cdef size_t i
511
+ for i in range(old_mask + 1):
512
+ entry = &(old_table[i])
513
+ if valid(entry.key_id):
514
+ cursor = self.lookup(entry.key_id)
515
+ assert cursor.key_id is NULL
516
+ cursor[0] = entry[0]
517
+ self.used += 1
518
+ self.fill += 1
519
+ sig_free(old_table)
520
+
521
+ def __cinit__(self):
522
+ """
523
+ Setup basic data structure.
524
+
525
+ TESTS::
526
+
527
+ sage: from sage.structure.coerce_dict import TripleDict
528
+ sage: TripleDict.__new__(TripleDict)
529
+ <sage.structure.coerce_dict.TripleDict object at ...>
530
+ """
531
+ cdef size_t newsize = 8
532
+ # The order is important here: the object must be in a
533
+ # consistent state even if exceptions are raised.
534
+ self.eraser = MonoDictEraser(self)
535
+ self.table = <mono_cell*>check_calloc(newsize, sizeof(mono_cell))
536
+ self.mask = newsize - 1
537
+ self.used = 0
538
+ self.fill = 0
539
+
540
+ def __init__(self, data=None, *, weak_values=False):
541
+ """
542
+ Create a special dict using singletons for keys.
543
+
544
+ EXAMPLES::
545
+
546
+ sage: from sage.structure.coerce_dict import MonoDict
547
+ sage: L = MonoDict()
548
+ sage: a = 'a'
549
+ sage: L[a] = 1
550
+ sage: L[a]
551
+ 1
552
+ sage: L = MonoDict({a: 1})
553
+ sage: L[a]
554
+ 1
555
+ sage: L = MonoDict([(a, 1)])
556
+ sage: L[a]
557
+ 1
558
+ """
559
+ self.weak_values = weak_values
560
+ if data:
561
+ try:
562
+ data = data.items()
563
+ except AttributeError:
564
+ pass
565
+ for k, v in data:
566
+ self.set(k,v)
567
+
568
+ def __dealloc__(self):
569
+ MonoDict_clear(self)
570
+ sig_free(self.table)
571
+
572
+ def __len__(self):
573
+ """
574
+ The number of items in ``self``.
575
+ EXAMPLES::
576
+
577
+ sage: from sage.structure.coerce_dict import MonoDict
578
+ sage: L = MonoDict()
579
+ sage: a = 'a'; b = 'b'; c = 'c'
580
+ sage: L[a] = 1
581
+ sage: L[a] = -1 # re-assign
582
+ sage: L[b] = 1
583
+ sage: L[c] = None
584
+ sage: len(L)
585
+ 3
586
+ """
587
+ return self.used
588
+
589
+ def __contains__(self, k):
590
+ """
591
+ Test if the dictionary contains a given key.
592
+
593
+ EXAMPLES::
594
+
595
+ sage: from sage.structure.coerce_dict import MonoDict
596
+ sage: L = MonoDict()
597
+ sage: a = 'a'; b = 'ab'; c = 15
598
+ sage: L[a] = 1
599
+ sage: L[b] = 2
600
+ sage: L[c] = 3
601
+ sage: c in L # indirect doctest
602
+ True
603
+
604
+ The keys are compared by identity, not by equality. Hence, we have::
605
+
606
+ sage: c == 15
607
+ True
608
+ sage: 15 in L
609
+ False
610
+ """
611
+ cdef mono_cell* cursor = self.lookup(<PyObject*>k)
612
+ if not valid(cursor.key_id):
613
+ return False
614
+ if not self.weak_values:
615
+ return True
616
+ value = <object>cursor.value
617
+ return not is_dead_keyedref(value)
618
+
619
+ def __getitem__(self, k):
620
+ """
621
+ Get the value corresponding to a key.
622
+
623
+ EXAMPLES::
624
+
625
+ sage: from sage.structure.coerce_dict import MonoDict
626
+ sage: L = MonoDict()
627
+ sage: a = 'a'; b = 'b'; c = 15
628
+ sage: L[a] = 1
629
+ sage: L[b] = 2
630
+ sage: L[c] = 3
631
+ sage: L[c] # indirect doctest
632
+ 3
633
+
634
+ Note that the keys are supposed to be unique::
635
+
636
+ sage: c == 15
637
+ True
638
+ sage: c is 15
639
+ False
640
+ sage: L[15]
641
+ Traceback (most recent call last):
642
+ ...
643
+ KeyError: 15
644
+ """
645
+ return self.get(k)
646
+
647
+ cdef get(self, k):
648
+ cdef mono_cell* cursor = self.lookup(<PyObject*>k)
649
+ if not valid(cursor.key_id):
650
+ raise KeyError(k)
651
+ # We need to check that the value is a live reference.
652
+ # Items with dead references (in the key or value) are deleted
653
+ # from the MonoDict by the MonoDictEraser. However, if we are
654
+ # in the middle of a deallocation, we may see a dead reference
655
+ # for the value. This cannot happen for the key: we are passed a
656
+ # strong reference to the key as argument of this function, so
657
+ # we know that it's alive.
658
+ value = <object>cursor.value
659
+ if type(value) is KeyedRef:
660
+ value = <object>PyWeakref_GET_OBJECT(value)
661
+ if value is None:
662
+ raise KeyError(k)
663
+ return value
664
+
665
+ def __setitem__(self, k, value):
666
+ """
667
+ Set the value corresponding to a key.
668
+
669
+ EXAMPLES::
670
+
671
+ sage: from sage.structure.coerce_dict import MonoDict
672
+ sage: L = MonoDict()
673
+ sage: a = 'a'
674
+ sage: L[a] = -1 # indirect doctest
675
+ sage: L[a]
676
+ -1
677
+ sage: L[a] = 1
678
+ sage: L[a]
679
+ 1
680
+ sage: len(L)
681
+ 1
682
+ """
683
+ self.set(k, value)
684
+
685
+ cdef int set(self, k, value) except -1:
686
+ cdef mono_cell entry
687
+ cdef PyObject* old_value
688
+ cdef bint maybe_resize = False
689
+ entry.key_id = <PyObject*>k
690
+ if self.weak_values:
691
+ wrap_k = wrap(k)
692
+ try:
693
+ value_store = KeyedRef(value, self.eraser, wrap_k)
694
+ entry.value = <PyObject*>value_store
695
+ except TypeError:
696
+ entry.value = <PyObject*>value
697
+ else:
698
+ entry.value = <PyObject*>value
699
+ Py_XINCREF(entry.value)
700
+ cursor = self.lookup(<PyObject*>k)
701
+ if not valid(cursor.key_id):
702
+ self.used += 1
703
+ if cursor.key_id is NULL:
704
+ self.fill += 1
705
+ maybe_resize = True
706
+ if not self.weak_values:
707
+ wrap_k = wrap(k)
708
+ try:
709
+ key_store = KeyedRef(k, self.eraser, wrap_k)
710
+ entry.key_weakref = <PyObject*>key_store
711
+ except TypeError:
712
+ entry.key_weakref = <PyObject*>k
713
+ Py_XINCREF(entry.key_weakref)
714
+
715
+ # We are taking a bit of a gamble here: we're assuming the
716
+ # dictionary has not been resized (otherwise cursor might
717
+ # not be a valid location anymore). The only way in which
718
+ # that could happen is if the allocation activity above
719
+ # forced a GC that triggered code that *adds* entries to
720
+ # this dictionary: the dictionary can only get reshaped if
721
+ # self.fill increases (as happens below). Note that we're
722
+ # holding a strong ref to the dict itself, so that's not
723
+ # liable to disappear. For the truly paranoid: we could
724
+ # detect a change by checking if self.table has changed
725
+ # value.
726
+ cursor[0] = entry
727
+
728
+ if maybe_resize and 3*self.fill > 2*self.mask:
729
+ self.resize()
730
+ else:
731
+ old_value = cursor.value
732
+ cursor.value = entry.value
733
+ Py_XDECREF(old_value)
734
+
735
+ def __delitem__(self, k):
736
+ """
737
+ Delete the value corresponding to a key.
738
+
739
+ EXAMPLES::
740
+
741
+ sage: from sage.structure.coerce_dict import MonoDict
742
+ sage: L = MonoDict()
743
+ sage: a = 15
744
+ sage: L[a] = -1
745
+ sage: len(L)
746
+ 1
747
+
748
+ Note that the keys are unique, hence using a key that is equal but not
749
+ identical to a results in an error::
750
+
751
+ sage: del L[15]
752
+ Traceback (most recent call last):
753
+ ...
754
+ KeyError: 15
755
+ sage: a in L
756
+ True
757
+ sage: del L[a]
758
+ sage: len(L)
759
+ 0
760
+ sage: a in L
761
+ False
762
+ """
763
+ cdef mono_cell* cursor = self.lookup(<PyObject*>k)
764
+ if not valid(cursor.key_id):
765
+ raise KeyError(k)
766
+ L = extract_mono_cell(cursor)
767
+ self.used -= 1
768
+
769
+ def items(self):
770
+ """
771
+ Iterate over the ``(key, value)`` pairs of this :class:`MonoDict`.
772
+
773
+ EXAMPLES::
774
+
775
+ sage: from sage.structure.coerce_dict import MonoDict
776
+ sage: L = MonoDict()
777
+ sage: L[1] = None
778
+ sage: L[2] = True
779
+ sage: L.items()
780
+ <...generator object at ...>
781
+ sage: sorted(L.items())
782
+ [(1, None), (2, True)]
783
+ """
784
+ # Iteration is tricky because the table could change from under
785
+ # us. The following iterates properly if the dictionary does
786
+ # not get resized, which is guaranteed if no NEW entries in the
787
+ # dictionary are introduced. At least we make sure to get our
788
+ # data fresh from "self" every iteration, so that at least we're
789
+ # not reading random memory. If the dictionary changes, it's not
790
+ # guaranteed you get to see any particular entry.
791
+ cdef size_t i = 0
792
+ while i <= self.mask:
793
+ cursor = &(self.table[i])
794
+ i += 1
795
+ if valid(cursor.key_id):
796
+ key = <object>(cursor.key_weakref)
797
+ value = <object>(cursor.value)
798
+ if type(key) is KeyedRef:
799
+ key = <object>PyWeakref_GET_OBJECT(key)
800
+ if key is None:
801
+ print("found defunct key")
802
+ continue
803
+ if type(value) is KeyedRef:
804
+ value = <object>PyWeakref_GET_OBJECT(value)
805
+ if value is None:
806
+ print("found defunct value")
807
+ continue
808
+ yield (key, value)
809
+
810
+ def copy(self):
811
+ """
812
+ Return a copy of this :class:`MonoDict` as Python dict.
813
+
814
+ EXAMPLES::
815
+
816
+ sage: from sage.structure.coerce_dict import MonoDict
817
+ sage: L = MonoDict()
818
+ sage: L[1] = 42
819
+ sage: L.copy()
820
+ {1: 42}
821
+ """
822
+ return dict(self.items())
823
+
824
+ def __reduce__(self):
825
+ """
826
+ Note that we don't expect equality as this class concerns itself with
827
+ object identity rather than object equality.
828
+
829
+ EXAMPLES::
830
+
831
+ sage: from sage.structure.coerce_dict import MonoDict
832
+ sage: L = MonoDict()
833
+ sage: L[1] = True
834
+ sage: loads(dumps(L)) == L
835
+ False
836
+ sage: list(loads(dumps(L)).items())
837
+ [(1, True)]
838
+ """
839
+ return MonoDict, (self.copy(),)
840
+
841
+ # The Cython supplied tp_traverse and tp_clear do not take the
842
+ # dynamically allocated table into account, so we have to supply our
843
+ # own. The only additional link to follow (that Cython does pick up
844
+ # and we have to replicate here) is the "eraser" which in its closure
845
+ # stores a reference back to the dictionary itself (meaning that
846
+ # MonoDicts only disappear on cyclic GC).
847
+ cdef int MonoDict_traverse(MonoDict self, visitproc visit, void* arg) noexcept:
848
+ if not self.mask:
849
+ return 0
850
+ Py_VISIT3(<PyObject*>self.eraser, visit, arg)
851
+ cdef size_t i
852
+ for i in range(self.mask + 1):
853
+ cursor = &self.table[i]
854
+ if valid(cursor.key_id):
855
+ Py_VISIT3(cursor.key_weakref, visit, arg)
856
+ Py_VISIT3(cursor.value, visit, arg)
857
+
858
+
859
+ cdef int MonoDict_clear(MonoDict self) noexcept:
860
+ """
861
+ We clear a monodict by taking first taking away the table before
862
+ dereffing its contents. That shortcuts callbacks, so we deref the
863
+ entries straight here. That means this code does not participate in
864
+ Python's trashcan the way that deletion code based on
865
+ extract_mono_cell does, so there is probably a way this code can be
866
+ used to overflow the C stack. It would have to be a pretty devious
867
+ example, though.
868
+ """
869
+ if not self.mask:
870
+ return 0
871
+ cdef size_t mask = self.mask
872
+ self.mask = 0 # Setting mask to 0 immediately prevents recursion
873
+ self.used = 0
874
+ self.fill = 0
875
+ # Set self.eraser to None safely
876
+ cdef object eraser = self.eraser
877
+ self.eraser = None
878
+ for i in range(mask+1):
879
+ cursor = &(self.table[i])
880
+ if valid(cursor.key_id):
881
+ cursor.key_id = deleted_key
882
+ Py_CLEAR(cursor.key_weakref)
883
+ Py_CLEAR(cursor.value)
884
+
885
+
886
+ (<PyTypeObject*>MonoDict).tp_traverse = <traverseproc>(&MonoDict_traverse)
887
+ (<PyTypeObject*>MonoDict).tp_clear = <inquiry>(&MonoDict_clear)
888
+
889
+
890
+ cdef class TripleDictEraser:
891
+ """
892
+ Erases items from a :class:`TripleDict` when a weak reference becomes
893
+ invalid.
894
+
895
+ This is of internal use only. Instances of this class will be passed as a
896
+ callback function when creating a weak reference.
897
+
898
+ EXAMPLES::
899
+
900
+ sage: from sage.structure.coerce_dict import TripleDict
901
+ sage: class A: pass
902
+ sage: a = A()
903
+ sage: T = TripleDict()
904
+ sage: T[a,ZZ,None] = 1
905
+ sage: T[ZZ,a,1] = 2
906
+ sage: T[a,a,ZZ] = 3
907
+ sage: len(T)
908
+ 3
909
+ sage: del a
910
+ sage: import gc
911
+ sage: n = gc.collect()
912
+ sage: len(T) # indirect doctest
913
+ 0
914
+
915
+ AUTHOR:
916
+
917
+ - Simon King (2012-01)
918
+ - Nils Bruin (2013-11)
919
+ """
920
+ cdef D
921
+
922
+ def __init__(self, D):
923
+ """
924
+ INPUT:
925
+
926
+ - ``D`` -- a :class:`TripleDict`. For internal use only.
927
+
928
+ EXAMPLES::
929
+
930
+ sage: D = sage.structure.coerce_dict.TripleDict()
931
+ sage: k = set([1])
932
+ sage: D[k,1,1] = 1
933
+ sage: len(D)
934
+ 1
935
+ sage: del k
936
+ sage: len(D) # indirect doctest
937
+ 0
938
+ """
939
+ self.D = ref(D)
940
+
941
+ def __call__(self, r):
942
+ """
943
+ INPUT:
944
+
945
+ - ``r`` -- a weak reference with key
946
+
947
+ For internal use only.
948
+
949
+ EXAMPLES::
950
+
951
+ sage: from sage.structure.coerce_dict import TripleDict
952
+ sage: class A: pass
953
+ sage: a = A()
954
+ sage: T = TripleDict()
955
+ sage: T[a,ZZ,None] = 1
956
+ sage: T[ZZ,a,1] = 2
957
+ sage: T[a,a,ZZ] = 3
958
+ sage: len(T)
959
+ 3
960
+ sage: del a
961
+ sage: import gc
962
+ sage: n = gc.collect()
963
+ sage: len(T) # indirect doctest
964
+ 0
965
+ """
966
+ cdef TripleDict td = <TripleDict>PyWeakref_GetObject(self.D)
967
+ if td is None or not td.mask:
968
+ return
969
+ k1, k2, k3 = r.key
970
+ cdef triple_cell* cursor = td.lookup(unwrap(k1), unwrap(k2), unwrap(k3))
971
+ cdef PyObject* r_ = <PyObject*>r
972
+ if valid(cursor.key_id1):
973
+ if (cursor.key_weakref1 is r_ or
974
+ cursor.key_weakref2 is r_ or
975
+ cursor.key_weakref3 is r_ or
976
+ cursor.value is r_):
977
+ L = extract_triple_cell(cursor)
978
+ td.used -= 1
979
+ else:
980
+ raise AssertionError("TripleDictEraser: key match but no weakref match")
981
+
982
+
983
+ cdef class TripleDict:
984
+ """
985
+ This is a hashtable specifically designed for (read) speed in
986
+ the coercion model.
987
+
988
+ It differs from a python dict in the following important ways:
989
+
990
+ - All keys must be sequence of exactly three elements. All sequence
991
+ types (tuple, list, etc.) map to the same item.
992
+
993
+ - Any of the three key components that support weak-refs are stored
994
+ via a weakref. If any of these components gets garbage collected
995
+ then the entire entry is removed. In that sense, this structure
996
+ behaves like a nested :class:`~weakref.WeakKeyDictionary`.
997
+
998
+ - Comparison is done using the 'is' rather than '==' operator.
999
+
1000
+ There are special cdef set/get methods for faster access.
1001
+ It is bare-bones in the sense that not all dictionary methods are
1002
+ implemented.
1003
+
1004
+ INPUT:
1005
+
1006
+ - ``data`` -- (optional) iterable defining initial data, as dict or
1007
+ iterable of (key, value) pairs
1008
+
1009
+ - ``weak_values`` -- boolean (default: ``False``); if it is
1010
+ ``True``, weak references to the values in this dictionary will be used,
1011
+ when possible
1012
+
1013
+ IMPLEMENTATION:
1014
+
1015
+ It is implemented as a hash table with open addressing, similar to python's
1016
+ dict.
1017
+
1018
+ EXAMPLES::
1019
+
1020
+ sage: from sage.structure.coerce_dict import TripleDict
1021
+ sage: L = TripleDict()
1022
+ sage: a = 'a'; b = 'b'; c = 'c'
1023
+ sage: L[a,b,c] = 1
1024
+ sage: L[a,b,c]
1025
+ 1
1026
+ sage: L[c,b,a] = -1
1027
+ sage: sorted(L.items())
1028
+ [(('a', 'b', 'c'), 1), (('c', 'b', 'a'), -1)]
1029
+ sage: del L[a,b,c]
1030
+ sage: list(L.items())
1031
+ [(('c', 'b', 'a'), -1)]
1032
+ sage: len(L)
1033
+ 1
1034
+ sage: for i in range(1000):
1035
+ ....: L[i,i,i] = i
1036
+ sage: len(L)
1037
+ 1001
1038
+ sage: L = TripleDict(L)
1039
+ sage: L[c,b,a]
1040
+ -1
1041
+ sage: L[a,b,c]
1042
+ Traceback (most recent call last):
1043
+ ...
1044
+ KeyError: ('a', 'b', 'c')
1045
+ sage: L[a]
1046
+ Traceback (most recent call last):
1047
+ ...
1048
+ KeyError: 'a'
1049
+ sage: L[a] = 1
1050
+ Traceback (most recent call last):
1051
+ ...
1052
+ KeyError: 'a'
1053
+
1054
+ TESTS:
1055
+
1056
+ Here, we demonstrate the use of weak values::
1057
+
1058
+ sage: class Foo: pass
1059
+ sage: T = TripleDict()
1060
+ sage: TW = TripleDict(weak_values=True)
1061
+ sage: a = Foo()
1062
+ sage: b = Foo()
1063
+ sage: k = 1
1064
+ sage: T[a,k,k]=1
1065
+ sage: T[k,a,k]=2
1066
+ sage: T[k,k,a]=3
1067
+ sage: T[k,k,k]=a
1068
+ sage: TW[b,k,k]=1
1069
+ sage: TW[k,b,k]=2
1070
+ sage: TW[k,k,b]=3
1071
+ sage: TW[k,k,k]=b
1072
+ sage: len(T)
1073
+ 4
1074
+ sage: len(TW)
1075
+ 4
1076
+ sage: (k,k,k) in T
1077
+ True
1078
+ sage: (k,k,k) in TW
1079
+ True
1080
+ sage: T[k,k,k] is a
1081
+ True
1082
+ sage: TW[k,k,k] is b
1083
+ True
1084
+
1085
+ Now, ``T`` holds a strong reference to ``a``, namely in ``T[k,k,k]``. Hence,
1086
+ when we delete ``a``, *all* items of ``T`` survive::
1087
+
1088
+ sage: import gc
1089
+ sage: del a
1090
+ sage: _ = gc.collect()
1091
+ sage: len(T)
1092
+ 4
1093
+
1094
+ Only when we remove the strong reference, the items become collectable::
1095
+
1096
+ sage: del T[k,k,k]
1097
+ sage: _ = gc.collect()
1098
+ sage: len(T)
1099
+ 0
1100
+
1101
+ The situation is different for ``TW``, since it only holds *weak*
1102
+ references to ``a``. Therefore, all items become collectable after
1103
+ deleting ``a``::
1104
+
1105
+ sage: del b
1106
+ sage: _ = gc.collect()
1107
+ sage: len(TW)
1108
+ 0
1109
+
1110
+ AUTHORS:
1111
+
1112
+ - Robert Bradshaw, 2007-08
1113
+
1114
+ - Simon King, 2012-01
1115
+
1116
+ - Nils Bruin, 2012-08
1117
+
1118
+ - Simon King, 2013-02
1119
+
1120
+ - Nils Bruin, 2013-11
1121
+ """
1122
+ cdef triple_cell* lookup(self, PyObject* key1, PyObject* key2, PyObject* key3) noexcept:
1123
+ """
1124
+ Return a pointer to where ``(key1, key2, key3)`` should be
1125
+ stored in this :class:`MonoDict`.
1126
+
1127
+ This routine is used for all cases where a (potential) spot for
1128
+ a key is looked up. The returned value is a pointer into the dictionary
1129
+ store that either contains an entry with the requested key or a free spot
1130
+ where an entry for that key should go.
1131
+ """
1132
+ cdef size_t mask = self.mask
1133
+ cdef triple_cell* table = self.table
1134
+ cdef triple_cell* first_deleted = NULL
1135
+
1136
+ # A random linear combination of the memory locations of the keys
1137
+ cdef size_t C2 = 0x7de83cbb
1138
+ cdef size_t C3 = 0x32354bf3
1139
+ cdef size_t h = (<size_t>key1) + C2*(<size_t>key2) + C3*(<size_t>key3)
1140
+
1141
+ # See MonoDict.lookup() for comments about the algorithm
1142
+ h //= 2 * sizeof(size_t)
1143
+
1144
+ cdef size_t i = (h >> 8) ^ h
1145
+ cdef size_t perturb = h
1146
+
1147
+ cdef triple_cell* cursor
1148
+ while True:
1149
+ cursor = &(table[i & mask])
1150
+ perturb >>= 5
1151
+ if cursor.key_id1 is key1:
1152
+ if cursor.key_id2 is key2 and cursor.key_id3 is key3:
1153
+ return cursor
1154
+ elif cursor.key_id1 is NULL:
1155
+ return first_deleted or cursor
1156
+ elif cursor.key_id1 is deleted_key:
1157
+ if first_deleted is NULL:
1158
+ first_deleted = cursor
1159
+ i = (5*i + 1) + perturb
1160
+
1161
+ cdef int resize(self) except -1:
1162
+ cdef triple_cell* old_table = self.table
1163
+ cdef size_t old_mask = self.mask
1164
+ cdef size_t newsize = 8
1165
+ cdef size_t minsize = 2 * self.used
1166
+ cdef triple_cell* cursor
1167
+ cdef triple_cell* entry
1168
+ while newsize < minsize:
1169
+ newsize *= 2
1170
+ cdef triple_cell* table = <triple_cell*>check_calloc(newsize, sizeof(triple_cell))
1171
+ self.table = table
1172
+ self.mask = newsize - 1
1173
+ self.used = 0
1174
+ self.fill = 0
1175
+ cdef size_t i
1176
+ for i in range(old_mask + 1):
1177
+ entry = &(old_table[i])
1178
+ if valid(entry.key_id1):
1179
+ cursor = self.lookup(entry.key_id1, entry.key_id2, entry.key_id3)
1180
+ assert cursor.key_id1 is NULL
1181
+ cursor[0] = entry[0]
1182
+ self.used +=1
1183
+ self.fill +=1
1184
+ sig_free(old_table)
1185
+
1186
+ def __cinit__(self):
1187
+ """
1188
+ Setup basic data structure.
1189
+
1190
+ TESTS::
1191
+
1192
+ sage: from sage.structure.coerce_dict import MonoDict
1193
+ sage: MonoDict.__new__(MonoDict)
1194
+ <sage.structure.coerce_dict.MonoDict object at ...>
1195
+ """
1196
+ cdef size_t newsize = 8
1197
+ # The order is important here: the object must be in a
1198
+ # consistent state even if exceptions are raised.
1199
+ self.eraser = TripleDictEraser(self)
1200
+ self.table = <triple_cell*>check_calloc(newsize, sizeof(triple_cell))
1201
+ self.mask = newsize - 1
1202
+ self.used = 0
1203
+ self.fill = 0
1204
+
1205
+ def __init__(self, data=None, *, weak_values=False):
1206
+ """
1207
+ Create a special dict using triples for keys.
1208
+
1209
+ EXAMPLES::
1210
+
1211
+ sage: from sage.structure.coerce_dict import TripleDict
1212
+ sage: L = TripleDict()
1213
+ sage: a = 'a'; b = 'b'; c = 'c'
1214
+ sage: L[a,b,c] = 1
1215
+ sage: L[a,b,c]
1216
+ 1
1217
+ sage: key = ("x", "y", "z")
1218
+ sage: L = TripleDict([(key, 42)])
1219
+ sage: L[key]
1220
+ 42
1221
+ sage: L = TripleDict({key: 42})
1222
+ sage: L[key]
1223
+ 42
1224
+ """
1225
+ self.weak_values = weak_values
1226
+ if data:
1227
+ try:
1228
+ data = data.items()
1229
+ except AttributeError:
1230
+ pass
1231
+ for k, v in data:
1232
+ self[k] = v
1233
+
1234
+ def __dealloc__(self):
1235
+ TripleDict_clear(self)
1236
+ sig_free(self.table)
1237
+
1238
+ def __len__(self):
1239
+ """
1240
+ The number of items in ``self``.
1241
+
1242
+ EXAMPLES::
1243
+
1244
+ sage: from sage.structure.coerce_dict import TripleDict
1245
+ sage: L = TripleDict()
1246
+ sage: a = 'a'; b = 'b'; c = 'c'
1247
+ sage: L[a,b,c] = 1
1248
+ sage: L[a,b,c] = -1 # re-assign
1249
+ sage: L[a,c,b] = 1
1250
+ sage: L[a,a,None] = None
1251
+ sage: len(L)
1252
+ 3
1253
+ """
1254
+ return self.used
1255
+
1256
+ def __contains__(self, k):
1257
+ """
1258
+ Test if the dictionary contains a given key.
1259
+
1260
+ EXAMPLES::
1261
+
1262
+ sage: from sage.structure.coerce_dict import TripleDict
1263
+ sage: L = TripleDict()
1264
+ sage: a = 'a'; b = 'ab'; c = 15
1265
+ sage: L[a,b,c] = 123
1266
+ sage: (a,b,c) in L # indirect doctest
1267
+ True
1268
+
1269
+ The keys are compared by identity, not by equality. Hence, we have::
1270
+
1271
+ sage: c == 15
1272
+ True
1273
+ sage: (a, b, 15) in L
1274
+ False
1275
+
1276
+ TESTS::
1277
+
1278
+ sage: a in L
1279
+ Traceback (most recent call last):
1280
+ ...
1281
+ KeyError: 'a'
1282
+ sage: (a, b) in L
1283
+ Traceback (most recent call last):
1284
+ ...
1285
+ KeyError: ('a', 'ab')
1286
+ """
1287
+ try:
1288
+ k1, k2, k3 = k
1289
+ except (TypeError, ValueError):
1290
+ raise KeyError(k)
1291
+ cdef triple_cell* cursor = self.lookup(<PyObject*>k1, <PyObject*>k2, <PyObject*>k3)
1292
+ if not valid(cursor.key_id1):
1293
+ return False
1294
+ if not self.weak_values:
1295
+ return True
1296
+ value = <object>cursor.value
1297
+ return not is_dead_keyedref(value)
1298
+
1299
+ def __getitem__(self, k):
1300
+ """
1301
+ Get the value corresponding to a key.
1302
+
1303
+ EXAMPLES::
1304
+
1305
+ sage: from sage.structure.coerce_dict import TripleDict
1306
+ sage: L = TripleDict()
1307
+ sage: a = 'a'; b = 'b'; c = 'c'
1308
+ sage: L[a,b,c] = 1
1309
+ sage: L[a,b,c]
1310
+ 1
1311
+ """
1312
+ try:
1313
+ k1, k2, k3 = k
1314
+ except (TypeError, ValueError):
1315
+ raise KeyError(k)
1316
+ return self.get(k1, k2, k3)
1317
+
1318
+ cdef get(self, k1, k2, k3):
1319
+ cdef triple_cell* cursor = self.lookup(<PyObject*>k1, <PyObject*>k2, <PyObject*>k3)
1320
+ if not valid(cursor.key_id1):
1321
+ raise KeyError((k1, k2, k3))
1322
+ value = <object>cursor.value
1323
+ if type(value) is KeyedRef:
1324
+ value = <object>PyWeakref_GET_OBJECT(value)
1325
+ if value is None:
1326
+ raise KeyError((k1, k2, k3))
1327
+ return value
1328
+
1329
+ def __setitem__(self, k, value):
1330
+ """
1331
+ Set the value corresponding to a key.
1332
+
1333
+ EXAMPLES::
1334
+
1335
+ sage: from sage.structure.coerce_dict import TripleDict
1336
+ sage: L = TripleDict()
1337
+ sage: a = 'a'; b = 'b'; c = 'c'
1338
+ sage: L[a,b,c] = -1
1339
+ sage: L[a,b,c]
1340
+ -1
1341
+ """
1342
+ try:
1343
+ k1, k2, k3 = k
1344
+ except (TypeError, ValueError):
1345
+ raise KeyError(k)
1346
+ self.set(k1, k2, k3, value)
1347
+
1348
+ cdef int set(self, k1, k2, k3, value) except -1:
1349
+ cdef triple_cell entry
1350
+ cdef PyObject* old_value
1351
+ cdef bint maybe_resize = False
1352
+ entry.key_id1 = <PyObject*>k1
1353
+ entry.key_id2 = <PyObject*>k2
1354
+ entry.key_id3 = <PyObject*>k3
1355
+ if self.weak_values:
1356
+ wrap_k = (wrap(k1), wrap(k2), wrap(k3))
1357
+ try:
1358
+ value_store = KeyedRef(value, self.eraser, wrap_k)
1359
+ entry.value = <PyObject*>value_store
1360
+ except TypeError:
1361
+ entry.value = <PyObject*>value
1362
+ else:
1363
+ entry.value = <PyObject*>value
1364
+ Py_XINCREF(entry.value)
1365
+ cursor = self.lookup(<PyObject*>k1, <PyObject*>k2, <PyObject*>k3)
1366
+ if not valid(cursor.key_id1):
1367
+ self.used += 1
1368
+ if cursor.key_id1 is NULL:
1369
+ self.fill += 1
1370
+ maybe_resize = True
1371
+ if not self.weak_values:
1372
+ wrap_k = (wrap(k1), wrap(k2), wrap(k3))
1373
+ try:
1374
+ key_store = KeyedRef(k1, self.eraser, wrap_k)
1375
+ entry.key_weakref1 = <PyObject*>key_store
1376
+ except TypeError:
1377
+ entry.key_weakref1 = <PyObject*>k1
1378
+ Py_XINCREF(entry.key_weakref1)
1379
+ try:
1380
+ key_store = KeyedRef(k2, self.eraser, wrap_k)
1381
+ entry.key_weakref2 = <PyObject*>key_store
1382
+ except TypeError:
1383
+ entry.key_weakref2 = <PyObject*>k2
1384
+ Py_XINCREF(entry.key_weakref2)
1385
+ try:
1386
+ key_store = KeyedRef(k3, self.eraser, wrap_k)
1387
+ entry.key_weakref3 = <PyObject*>key_store
1388
+ except TypeError:
1389
+ entry.key_weakref3 = <PyObject*>k3
1390
+ Py_XINCREF(entry.key_weakref3)
1391
+
1392
+ # We are taking a bit of a gamble here: we're assuming the
1393
+ # dictionary has not been resized (otherwise cursor might
1394
+ # not be a valid location anymore). The only way in which
1395
+ # that could happen is if the allocation activity above
1396
+ # forced a GC that triggered code that *adds* entries to
1397
+ # this dictionary: the dictionary can only get reshaped if
1398
+ # self.fill increases (as happens below). Note that we're
1399
+ # holding a strong ref to the dict itself, so that's not
1400
+ # liable to disappear. For the truly paranoid: we could
1401
+ # detect a change by checking if self.table has changed
1402
+ # value.
1403
+ cursor[0] = entry
1404
+
1405
+ if maybe_resize and 3*self.fill > 2*self.mask:
1406
+ self.resize()
1407
+ else:
1408
+ old_value = cursor.value
1409
+ cursor.value = entry.value
1410
+ Py_XDECREF(old_value)
1411
+
1412
+ def __delitem__(self, k):
1413
+ """
1414
+ Delete the value corresponding to a key.
1415
+
1416
+ EXAMPLES::
1417
+
1418
+ sage: from sage.structure.coerce_dict import TripleDict
1419
+ sage: L = TripleDict()
1420
+ sage: a = 'a'; b = 'b'; c = 'c'
1421
+ sage: L[a,b,c] = -1
1422
+ sage: (a,b,c) in L
1423
+ True
1424
+ sage: del L[a,b,c]
1425
+ sage: len(L)
1426
+ 0
1427
+ sage: (a,b,c) in L
1428
+ False
1429
+ """
1430
+ try:
1431
+ k1, k2, k3 = k
1432
+ except (TypeError, ValueError):
1433
+ raise KeyError(k)
1434
+ cdef triple_cell* cursor = self.lookup(<PyObject*>k1, <PyObject*>k2, <PyObject*>k3)
1435
+ if not valid(cursor.key_id1):
1436
+ raise KeyError(k)
1437
+ L = extract_triple_cell(cursor)
1438
+ self.used -= 1
1439
+
1440
+ def items(self):
1441
+ """
1442
+ Iterate over the ``(key, value)`` pairs of this :class:`TripleDict`.
1443
+
1444
+ EXAMPLES::
1445
+
1446
+ sage: from sage.structure.coerce_dict import TripleDict
1447
+ sage: L = TripleDict()
1448
+ sage: L[1,2,3] = None
1449
+ sage: L.items()
1450
+ <...generator object at ...>
1451
+ sage: list(L.items())
1452
+ [((1, 2, 3), None)]
1453
+ """
1454
+ cdef size_t i = 0
1455
+ while i <= self.mask:
1456
+ cursor = &(self.table[i])
1457
+ i += 1
1458
+ if valid(cursor.key_id1):
1459
+ key1 = <object>(cursor.key_weakref1)
1460
+ key2 = <object>(cursor.key_weakref2)
1461
+ key3 = <object>(cursor.key_weakref3)
1462
+ value = <object>(cursor.value)
1463
+ if type(key1) is KeyedRef:
1464
+ key1 = <object>PyWeakref_GET_OBJECT(key1)
1465
+ if key1 is None:
1466
+ print("found defunct key1")
1467
+ continue
1468
+ if type(key2) is KeyedRef:
1469
+ key2 = <object>PyWeakref_GET_OBJECT(key2)
1470
+ if key2 is None:
1471
+ print("found defunct key2")
1472
+ continue
1473
+ if type(key3) is KeyedRef:
1474
+ key3 = <object>PyWeakref_GET_OBJECT(key3)
1475
+ if key3 is None:
1476
+ print("found defunct key3")
1477
+ continue
1478
+ if type(value) is KeyedRef:
1479
+ value = <object>PyWeakref_GET_OBJECT(value)
1480
+ if value is None:
1481
+ print("found defunct value")
1482
+ continue
1483
+ yield ((key1, key2, key3), value)
1484
+
1485
+ def copy(self):
1486
+ """
1487
+ Return a copy of this :class:`TripleDict` as Python dict.
1488
+
1489
+ EXAMPLES::
1490
+
1491
+ sage: from sage.structure.coerce_dict import TripleDict
1492
+ sage: L = TripleDict()
1493
+ sage: L[1,2,3] = 42
1494
+ sage: L.copy()
1495
+ {(1, 2, 3): 42}
1496
+ """
1497
+ return dict(self.items())
1498
+
1499
+ def __reduce__(self):
1500
+ """
1501
+ Note that we don't expect equality as this class concerns itself with
1502
+ object identity rather than object equality.
1503
+
1504
+ EXAMPLES::
1505
+
1506
+ sage: from sage.structure.coerce_dict import TripleDict
1507
+ sage: L = TripleDict()
1508
+ sage: L[1,2,3] = True
1509
+ sage: loads(dumps(L)) == L
1510
+ False
1511
+ sage: list(loads(dumps(L)).items())
1512
+ [((1, 2, 3), True)]
1513
+ """
1514
+ return TripleDict, (self.copy(),)
1515
+
1516
+ # The Cython supplied tp_traverse and tp_clear do not take the
1517
+ # dynamically allocated table into account, so we have to supply our
1518
+ # own. The only additional link to follow (that Cython does pick up
1519
+ # and we have to replicate here) is the "eraser" which in its closure
1520
+ # stores a reference back to the dictionary itself (meaning that
1521
+ # TripleDicts only disappear on cyclic GC).
1522
+ cdef int TripleDict_traverse(TripleDict self, visitproc visit, void* arg) noexcept:
1523
+ if not self.mask:
1524
+ return 0
1525
+ Py_VISIT3(<PyObject*>self.eraser, visit, arg)
1526
+ cdef size_t i
1527
+ for i in range(self.mask + 1):
1528
+ cursor = &self.table[i]
1529
+ if valid(cursor.key_id1):
1530
+ Py_VISIT3(cursor.key_weakref1, visit, arg)
1531
+ Py_VISIT3(cursor.key_weakref2, visit, arg)
1532
+ Py_VISIT3(cursor.key_weakref3, visit, arg)
1533
+ Py_VISIT3(cursor.value, visit, arg)
1534
+
1535
+
1536
+ cdef int TripleDict_clear(TripleDict self) noexcept:
1537
+ if not self.mask:
1538
+ return 0
1539
+ cdef size_t mask = self.mask
1540
+ self.mask = 0 # Setting mask to 0 immediately prevents recursion
1541
+ self.used = 0
1542
+ self.fill = 0
1543
+ # Set self.eraser to None safely
1544
+ cdef object eraser = self.eraser
1545
+ self.eraser = None
1546
+ for i in range(mask + 1):
1547
+ cursor = &(self.table[i])
1548
+ if valid(cursor.key_id1):
1549
+ cursor.key_id1 = deleted_key
1550
+ Py_CLEAR(cursor.key_weakref1)
1551
+ Py_CLEAR(cursor.key_weakref2)
1552
+ Py_CLEAR(cursor.key_weakref3)
1553
+ Py_CLEAR(cursor.value)
1554
+
1555
+
1556
+ (<PyTypeObject*>TripleDict).tp_traverse = <traverseproc>(&TripleDict_traverse)
1557
+ (<PyTypeObject*>TripleDict).tp_clear = <inquiry>(&TripleDict_clear)