Ticket #715: trac715_two_tripledicts.patch

File trac715_two_tripledicts.patch, 25.7 KB (added by SimonKing, 8 years ago)

Use weak references to the keys of TripleDict. Compare by "==" or by "is", depending on the application. Use weak references for storing actions.

  • sage/categories/functor.pxd

    # HG changeset patch
    # User Simon King <simon.king@uni-jena.de>
    # Date 1324507434 -3600
    # Node ID 634489af2f27ac285708c991309b0dd5d38f9854
    # Parent  6120ec4f0db167c65c125f80b2d405ccb49d1da1
    #715: Use weak references for caching coerce maps, actions and polynomial rings.
    Introduce a second variant of `TripleDict`.
    
    diff --git a/sage/categories/functor.pxd b/sage/categories/functor.pxd
    a b  
    11from sage.structure.sage_object cimport SageObject
    22
    33cdef class Functor(SageObject):
     4    cdef __weakref__
    45    cdef object __domain
    5     cdef object __codomain
    6  No newline at end of file
     6    cdef object __codomain
  • sage/rings/polynomial/polynomial_ring_constructor.py

    diff --git a/sage/rings/polynomial/polynomial_ring_constructor.py b/sage/rings/polynomial/polynomial_ring_constructor.py
    a b  
    5353from sage.categories.commutative_rings import CommutativeRings
    5454_CommutativeRings = CommutativeRings()
    5555
    56 _cache = {}
     56import weakref
     57_cache = weakref.WeakValueDictionary()
    5758
    5859def PolynomialRing(base_ring, arg1=None, arg2=None,
    5960                   sparse=False, order='degrevlex',
  • sage/structure/coerce.pxd

    diff --git a/sage/structure/coerce.pxd b/sage/structure/coerce.pxd
    a b  
    44from parent cimport Parent
    55from sage.categories.action cimport Action
    66
    7 from coerce_dict cimport TripleDict, TripleDictIter
     7from coerce_dict cimport TripleDict, TripleDictById, TripleDictIter
    88
    99cdef class CoercionModel_cache_maps(CoercionModel):
    1010    # This MUST be a mapping to tuples, where each
    1111    # tuple contains at least two elements that are either
    1212    # None or of type Morphism.
    13     cdef TripleDict _coercion_maps
     13    cdef TripleDictById _coercion_maps
    1414   
    1515    # This MUST be a mapping to actions.
    1616    cdef TripleDict _action_maps
  • sage/structure/coerce.pyx

    diff --git a/sage/structure/coerce.pyx b/sage/structure/coerce.pyx
    a b  
    9191import sys, traceback
    9292
    9393from coerce_actions import LeftModuleAction, RightModuleAction, IntegerMulAction
     94from weakref import ref
     95from sage.misc.constant_function import ConstantFunction
     96NoneFunction = ConstantFunction(False)
    9497
    9598cpdef py_scalar_parent(py_type):
    9699    """
     
    207210        # This MUST be a mapping of tuples, where each
    208211        # tuple contains at least two elements that are either
    209212        # None or of type Map.
    210         self._coercion_maps = TripleDict(lookup_dict_size, threshold=lookup_dict_threshold)
    211         # This MUST be a mapping to actions. 
    212         self._action_maps = TripleDict(lookup_dict_size, threshold=lookup_dict_threshold)
     213        self._coercion_maps = TripleDictById(lookup_dict_size, threshold=lookup_dict_threshold)
     214        # This MUST be a mapping to actions.
     215        self._action_maps = TripleDictById(lookup_dict_size, threshold=lookup_dict_threshold)
    213216        # This is a mapping from Parents to Parents, storing the result of division in the given parent.
    214217        self._division_parents = TripleDict(lookup_dict_size, threshold=lookup_dict_threshold)
    215218
     
    12051208
    12061209        """
    12071210        try:
    1208             return self._action_maps.get(R, S, op)
     1211            action = self._action_maps.get(R, S, op)()
     1212            if action is False:
     1213                return None
     1214            if action is not None:
     1215                return action
    12091216        except KeyError:
    1210             action = self.discover_action(R, S, op)
    1211             action = self.verify_action(action, R, S, op)
    1212             self._action_maps.set(R, S, op, action)
    1213             return action
     1217            pass
     1218        action = self.discover_action(R, S, op)
     1219        action = self.verify_action(action, R, S, op)
     1220        if action is None:
     1221            self._action_maps.set(R, S, op, NoneFunction)
     1222        else:
     1223            self._action_maps.set(R, S, op, ref(action))
     1224        return action
    12141225           
    12151226    cpdef verify_action(self, action, R, S, op, bint fix=True):
    12161227        r"""
  • sage/structure/coerce_dict.pxd

    diff --git a/sage/structure/coerce_dict.pxd b/sage/structure/coerce_dict.pxd
    a b  
     1cdef class TripleDictEraser
     2
    13cdef class TripleDict:
    24    cdef Py_ssize_t _size
    3     cdef buckets
     5    cdef list buckets
    46    cdef double threshold
    5     cdef get(self, k1, k2, k3)
    6     cdef set(self, k1, k2, k3, value)
    7    
     7    cdef TripleDictEraser eraser
     8    cdef get(self, object k1, object k2, object k3)
     9    cdef set(self, object k1, object k2, object k3, value)
     10
     11cdef class TripleDictById(TripleDict):
     12    pass
     13
    814cdef class TripleDictIter:
    915    cdef TripleDict pairs
    1016    cdef buckets, bucket_iter
     17
     18cdef class TripleDictEraser:
     19    cdef TripleDict D
  • sage/structure/coerce_dict.pyx

    diff --git a/sage/structure/coerce_dict.pyx b/sage/structure/coerce_dict.pyx
    a b  
    11#*****************************************************************************
    22#       Copyright (C) 2007 Robert Bradshaw <robertwb@math.washington.edu>
     3#                     2011 Simon King <simon.king@uni-jena.de>
    34#
    45#  Distributed under the terms of the GNU General Public License (GPL)
    56#
     
    910
    1011include "../ext/python_list.pxi"
    1112
     13from sage.misc.constant_function import ConstantFunction
     14
     15############################################
     16# The following code is responsible for
     17# removing dead references from the cache
     18############################################
     19
     20cdef class TripleDictEraser:
     21    """
     22    Erases items from a :class:`TripleDict` when a weak reference becomes invalid.
     23
     24    This is of internal use only. Instances of this class will be passed as a callback
     25    function when creating a weak reference.
     26
     27    EXAMPLES::
     28
     29        sage: from sage.structure.coerce_dict import TripleDict
     30        sage: class A: pass
     31        sage: a = A()
     32        sage: T = TripleDict(11)
     33        sage: T[a,ZZ,None] = 1
     34        sage: T[ZZ,a,1] = 2
     35        sage: T[a,a,ZZ] = 3
     36        sage: len(T)
     37        3
     38        sage: del a
     39        sage: import gc
     40        sage: n = gc.collect()
     41        sage: len(T)
     42        0
     43
     44    AUTHOR:
     45
     46    Simon King (2011-12)
     47    """
     48
     49    def __init__(self, D):
     50        """
     51        INPUT:
     52
     53        A :class:`TripleDict`.
     54
     55        EXAMPLES::
     56
     57            sage: from sage.structure.coerce_dict import TripleDict, TripleDictEraser
     58            sage: D = TripleDict(11)
     59            sage: TripleDictEraser(D)
     60            <sage.structure.coerce_dict.TripleDictEraser object at ...>
     61
     62        """
     63        self.D = D
     64    def __call__(self, r):
     65        """
     66        INPUT:
     67
     68        A weak reference
     69
     70        When this is called with a weak reference ``r``, then each item containing ``r``
     71        is removed from the associated :class:`TripleDict`. Normally, this only happens
     72        when a weak reference becomes invalid.
     73
     74        EXAMPLES::
     75
     76            sage: from sage.structure.coerce_dict import TripleDict
     77            sage: class A: pass
     78            sage: a = A()
     79            sage: T = TripleDict(11)
     80            sage: T[a,ZZ,None] = 1
     81            sage: T[ZZ,a,1] = 2
     82            sage: T[a,a,ZZ] = 3
     83            sage: len(T)
     84            3
     85            sage: del a
     86            sage: import gc
     87            sage: n = gc.collect()
     88            sage: len(T)    # indirect doctest
     89            0
     90
     91        """
     92        # r is a (weak) reference (typically to a parent),
     93        # and we remove that parent from the cache self.D
     94        # Each weak reference can only occur once in the list.
     95        cdef list bucket
     96        for bucket in self.D.buckets:
     97            for i from 0 <= i < PyList_GET_SIZE(bucket) by 4:
     98                if bucket[i] is r or bucket[i+1] is r:
     99                    del bucket[i:i+4]
     100                    self.D._size -= 1
     101                    return
     102
     103from weakref import ref
    12104
    13105cdef class TripleDict:
    14106    """
     
    20112       - All keys must be sequence of exactly three elements. All sequence
    21113         types (tuple, list, etc.) map to the same item.
    22114       - Comparison is done using the 'is' rather than '==' operator.
    23        
     115
     116    By trac ticket #715, it uses weak references on the first two
     117    items of the key, if they are weakly referenceable. Otherwise, a
     118    :class:`~sage.misc.constant_function.ConstantFunction` is used.
     119
    24120    There are special cdef set/get methods for faster access.
    25121    It is bare-bones in the sense that not all dictionary methods are
    26122    implemented.
     
    30126    and each bucket is of the form [k1, k2, k3, value, k1, k2, k3, value, ...]
    31127    on which a linear search is performed.
    32128   
    33     To spread objects evenly, the size should ideally be a prime, and certainly
    34     not divisible by 2.
     129    To spread objects evenly, the size should ideally be a prime.
    35130   
    36131   
    37     EXAMPLES: 
     132    EXAMPLES::
    38133   
    39134        sage: from sage.structure.coerce_dict import TripleDict
    40135        sage: L = TripleDict(31)
     
    83178        ...
    84179        KeyError: 'a'
    85180       
    86     The following illustrates why even sizes are bad.
    87         sage: L = TripleDict(4, L)
    88         sage: L.stats()
    89         (0, 250.25, 1001)
    90         sage: L.bucket_lens()
    91         [1001, 0, 0, 0]
     181    Here, we show that weak references are indeed in use::
    92182
     183        sage: class A: pass
     184        sage: a = A()
     185        sage: T = TripleDict(11)
     186        sage: T[a,ZZ,None] = 1
     187        sage: T[ZZ,a,1] = 2
     188        sage: T[a,a,ZZ] = 3
     189        sage: len(T)
     190        3
     191        sage: del a
     192        sage: import gc
     193        sage: n = gc.collect()
     194        sage: len(T)
     195        0
    93196
    94     AUTHOR:
    95        -- Robert Bradshaw, 2007-08
     197    We encourage to use objects for the first two constituents of the key
     198    that allow for weak references. However, other objects still work.
     199
     200    AUTHORS:
     201
     202    - Robert Bradshaw, 2007-08
     203    - Simon King, 2011-12
    96204    """
    97205   
    98206    def __init__(self, size, data=None, threshold=0):
    99207        """
    100208        Create a special dict using triples for keys.
    101209       
    102         EXAMPLES:
     210        EXAMPLES::
     211
    103212            sage: from sage.structure.coerce_dict import TripleDict
    104213            sage: L = TripleDict(31)
    105214            sage: a = 'a'; b = 'b'; c = 'c'
     
    111220        self.threshold = threshold
    112221        self.buckets = [[] for i from 0 <= i <  size]
    113222        self._size = 0
     223        self.eraser = TripleDictEraser(self)
    114224        if data is not None:
    115225            for k, v in data.iteritems():
    116226                self[k] = v
     
    119229        """
    120230        The number of items in self.
    121231       
    122         EXAMPLES:
     232        EXAMPLES::
     233
    123234            sage: from sage.structure.coerce_dict import TripleDict
    124235            sage: L = TripleDict(37)
    125236            sage: a = 'a'; b = 'b'; c = 'c'
     
    136247        """
    137248        The distribution of items in buckets.
    138249       
    139         OUTPUT:
    140             (min, avg, max)
     250        OUTPUT:
     251
     252        `(min, avg, max)`
    141253       
    142         EXAMPLES:
     254        EXAMPLES::
     255
    143256            sage: from sage.structure.coerce_dict import TripleDict
    144257            sage: L = TripleDict(37)
    145258            sage: for i in range(100): L[i,i,i] = None
     
    171284        """
    172285        The distribution of items in buckets.
    173286       
    174         OUTPUT:
    175             A list of how many items are in each bucket.
     287        OUTPUT:
     288
     289        A list of how many items are in each bucket.
    176290       
    177         EXAMPLES:
     291        EXAMPLES::
     292
    178293            sage: from sage.structure.coerce_dict import TripleDict
    179294            sage: L = TripleDict(37)
    180295            sage: for i in range(100): L[i,i,i] = None
     
    194309        """
    195310        The actual buckets of self, for debugging.
    196311       
    197         EXAMPLE:
     312        EXAMPLES::
     313
    198314            sage: from sage.structure.coerce_dict import TripleDict
    199315            sage: L = TripleDict(3)
    200316            sage: L[0,0,0] = None
     
    205321       
    206322    def __getitem__(self, k):
    207323        """
    208         EXAMPLES:
     324        EXAMPLES::
     325
    209326            sage: from sage.structure.coerce_dict import TripleDict
    210327            sage: L = TripleDict(31)
    211328            sage: a = 'a'; b = 'b'; c = 'c'
     
    219336            raise KeyError, k
    220337        return self.get(k1, k2, k3)
    221338           
    222     cdef get(self, k1, k2, k3):
    223         cdef Py_ssize_t h = (<Py_ssize_t><void *>k1 + 13*<Py_ssize_t><void *>k2 ^ 503*<Py_ssize_t><void *>k3)
     339    cdef get(self, object k1, object k2, object k3):
     340        cdef Py_ssize_t h = (hash(k1)+ 13*hash(k2)^ 503*hash(k3))
    224341        if h < 0: h = -h
    225342        cdef Py_ssize_t i
    226         bucket = <object>PyList_GET_ITEM(self.buckets, h % PyList_GET_SIZE(self.buckets))
     343        cdef list bucket = self.buckets[h % PyList_GET_SIZE(self.buckets)]
    227344        for i from 0 <= i < PyList_GET_SIZE(bucket) by 4:
    228             if PyList_GET_ITEM(bucket, i) == <PyObject*>k1 and \
    229                PyList_GET_ITEM(bucket, i+1) == <PyObject*>k2 and \
    230                PyList_GET_ITEM(bucket, i+2) == <PyObject*>k3:
    231                 return <object>PyList_GET_ITEM(bucket, i+3)
     345            if bucket[i]() == k1 and \
     346               bucket[i+1]() == k2 and \
     347               bucket[i+2] == k3:
     348                return bucket[i+3]
    232349        raise KeyError, (k1, k2, k3)
    233350       
    234351    def __setitem__(self, k, value):
    235352        """
    236         EXAMPLES:
     353        EXAMPLES::
     354
    237355            sage: from sage.structure.coerce_dict import TripleDict
    238356            sage: L = TripleDict(31)
    239357            sage: a = 'a'; b = 'b'; c = 'c'
     
    241359            sage: L[a,b,c]
    242360            -1
    243361        """
     362        cdef object k1,k2,k3
    244363        try:
    245364            k1, k2, k3 = k
    246365        except (TypeError,ValueError):
    247366            raise KeyError, k
    248367        self.set(k1, k2, k3, value)
    249368           
    250     cdef set(self, k1, k2, k3, value):
     369    cdef set(self, object k1, object k2, object k3, value):
    251370        if self.threshold and self._size > len(self.buckets) * self.threshold:
    252371            self.resize()
    253         cdef Py_ssize_t h = (<Py_ssize_t><void *>k1 + 13*<Py_ssize_t><void *>k2 ^ 503*<Py_ssize_t><void *>k3)
     372        cdef Py_ssize_t h = (hash(k1)+ 13*hash(k2)^ 503*hash(k3))
    254373        if h < 0: h = -h
    255374        cdef Py_ssize_t i
    256         bucket = <object>PyList_GET_ITEM(self.buckets, h % PyList_GET_SIZE(self.buckets))
     375        cdef list bucket = self.buckets[h % PyList_GET_SIZE(self.buckets)]
    257376        for i from 0 <= i < PyList_GET_SIZE(bucket) by 4:
    258             if PyList_GET_ITEM(bucket, i) == <PyObject*>k1 and \
    259                PyList_GET_ITEM(bucket, i+1) == <PyObject*>k2 and \
    260                PyList_GET_ITEM(bucket, i+2) == <PyObject*>k3:
     377            if bucket[i]() == k1 and \
     378               bucket[i+1]() == k2 and \
     379               bucket[i+2] == k3:
    261380                bucket[i+3] = value
    262381                return
    263         bucket += [k1, k2, k3, value]
     382        try:
     383            r1 = ref(k1,self.eraser)
     384        except TypeError:
     385            r1 = ConstantFunction(k1)
     386        try:
     387            r2 = ref(k2,self.eraser)
     388        except TypeError:
     389            r2 = ConstantFunction(k2)
     390        bucket += [r1, r2, k3, value]
    264391        self._size += 1
    265392           
    266393    def __delitem__(self, k):
    267394        """
    268         EXAMPLES:
     395        EXAMPLES::
     396
    269397            sage: from sage.structure.coerce_dict import TripleDict
    270398            sage: L = TripleDict(31)
    271399            sage: a = 'a'; b = 'b'; c = 'c'
     
    274402            sage: len(L)
    275403            0
    276404        """
     405        cdef object k1,k2,k3
    277406        try:
    278407            k1, k2, k3 = k
    279408        except (TypeError,ValueError):
    280409            raise KeyError, k
    281         cdef Py_ssize_t h = (<Py_ssize_t><void *>k1 + 13*<Py_ssize_t><void *>k2 ^ 503*<Py_ssize_t><void *>k3)
     410        cdef Py_ssize_t h = (hash(k1)+ 13*hash(k2)^ 503*hash(k3))
    282411        if h < 0: h = -h
    283412        cdef Py_ssize_t i
    284         bucket = <object>PyList_GET_ITEM(self.buckets, h % PyList_GET_SIZE(self.buckets))
     413        cdef list bucket = self.buckets[h % PyList_GET_SIZE(self.buckets)]
    285414        for i from 0 <= i < PyList_GET_SIZE(bucket) by 4:
    286             if PyList_GET_ITEM(bucket, i) == <PyObject*>k1 and \
    287                PyList_GET_ITEM(bucket, i+1) == <PyObject*>k2 and \
    288                PyList_GET_ITEM(bucket, i+2) == <PyObject*>k3:
     415            if bucket[i]() == k1 and \
     416               bucket[i+1]() == k2 and \
     417               bucket[i+2] == k3:
    289418                del bucket[i:i+4]
    290419                self._size -= 1
    291420                return
     
    298427        If the number of buckets is 0 or not given, it resizes self to the
    299428        smallest prime that is at least twice as large as self.
    300429       
    301         EXAMPLES:
     430        EXAMPLES::
     431
    302432            sage: from sage.structure.coerce_dict import TripleDict
    303433            sage: L = TripleDict(8)
    304434            sage: for i in range(100): L[i,i,i] = None
     
    317447           
    318448    def iteritems(self):
    319449        """
    320         EXAMPLES:
     450        EXAMPLES::
     451
    321452            sage: from sage.structure.coerce_dict import TripleDict
    322453            sage: L = TripleDict(31)
    323454            sage: L[1,2,3] = None
     
    331462        Note that we don't expect equality as this class concerns itself with
    332463        object identity rather than object equality.
    333464
    334         EXAMPLES:
     465        EXAMPLES::
     466
    335467            sage: from sage.structure.coerce_dict import TripleDict
    336468            sage: L = TripleDict(31)
    337469            sage: L[1,2,3] = True
     
    342474        """
    343475        return TripleDict, (len(self.buckets), dict(self.iteritems()), self.threshold)
    344476       
     477cdef class TripleDictById(TripleDict):
     478    """
     479    This is a hashtable specifically designed for (read) speed in
     480    the coercion model.
     481   
     482    It differs from a python dict in the following important ways:
     483   
     484       - All keys must be sequence of exactly three elements. All sequence
     485         types (tuple, list, etc.) map to the same item.
     486       - Comparison is done using the 'is' rather than '==' operator.
     487
     488    By trac ticket #715, it uses weak references on the first two
     489    items of the key, if they are weakly referenceable. Otherwise, a
     490    :class:`~sage.misc.constant_function.ConstantFunction` is used.
     491
     492    There are special cdef set/get methods for faster access.
     493    It is bare-bones in the sense that not all dictionary methods are
     494    implemented.
     495   
     496    It is implemented as a list of lists (hereafter called buckets). The bucket
     497    is chosen according to a very simple hash based on the object pointer.
     498    and each bucket is of the form [k1, k2, k3, value, k1, k2, k3, value, ...]
     499    on which a linear search is performed.
     500   
     501    To spread objects evenly, the size should ideally be a prime, and certainly
     502    not divisible by 2.
     503
     504    EXAMPLES::
     505
     506        sage: from sage.structure.coerce_dict import TripleDictById
     507        sage: L = TripleDictById(31)
     508        sage: a = 'a'; b = 'b'; c = 'c'
     509        sage: L[a,b,c] = 1
     510        sage: L[a,b,c]
     511        1
     512        sage: L[c,b,a] = -1
     513        sage: list(L.iteritems())     # random order of output.
     514        [(('c', 'b', 'a'), -1), (('a', 'b', 'c'), 1)]
     515        sage: del L[a,b,c]
     516        sage: list(L.iteritems())
     517        [(('c', 'b', 'a'), -1)]
     518        sage: len(L)
     519        1
     520        sage: L.stats()             # min, avg, max (bucket length)
     521        (0, 0.032258064516129031, 1)
     522        sage: L.bucket_lens()       # random layout
     523        [0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
     524        sage: for i in range(1000):
     525        ...       L[i,i,i] = i
     526        sage: len(L)
     527        1001
     528        sage: L.stats()             # random
     529        (31, 32.29032258064516, 35)
     530        sage: L.bucket_lens()       # random layout
     531        [33, 34, 32, 32, 35, 32, 31, 33, 34, 31, 32, 34, 32, 31, 31, 32, 32, 31, 31, 33, 32, 32, 32, 33, 33, 33, 31, 33, 33, 32, 31]
     532        sage: L = TripleDictById(101, L)
     533        sage: L.stats()             # random
     534        (8, 9.9108910891089117, 11)
     535        sage: L = TripleDictById(3, L)
     536        sage: L.stats()             # random
     537        (291, 333.66666666666669, 410)
     538        sage: L[c,b,a]
     539        -1
     540        sage: L[a,b,c]
     541        Traceback (most recent call last):
     542        ...
     543        KeyError: ('a', 'b', 'c')
     544        sage: L[a]
     545        Traceback (most recent call last):
     546        ...
     547        KeyError: 'a'
     548        sage: L[a] = 1
     549        Traceback (most recent call last):
     550        ...
     551        KeyError: 'a'
     552
     553    Here, we show that weak references are indeed in use::
     554
     555        sage: class A: pass
     556        sage: a = A()
     557        sage: T = TripleDictById(11)
     558        sage: T[a,ZZ,None] = 1
     559        sage: T[ZZ,a,1] = 2
     560        sage: T[a,a,ZZ] = 3
     561        sage: len(T)
     562        3
     563        sage: del a
     564        sage: import gc
     565        sage: n = gc.collect()
     566        sage: len(T)
     567        0
     568
     569    We encourage to use objects for the first two constituents of the key
     570    that allow for weak references. However, other objects still work.
     571    Note that the keys are tested for identity, not for equality::
     572
     573        sage: T[1,1,1] = 1
     574        sage: T[1,1,1]
     575        Traceback (most recent call last):
     576        ...
     577        KeyError: (1, 1, 1)
     578        sage: x = 1
     579        sage: T[x,x,x] = 1
     580        sage: T[x,x,x]
     581        1
     582
     583    The following illustrates why even sizes are bad.
     584    ::
     585
     586        sage: L = TripleDictById(4, L)
     587        sage: L.stats()
     588        (0, 250.25, 1001)
     589        sage: L.bucket_lens()
     590        [1001, 0, 0, 0]
     591
     592    AUTHORS:
     593
     594    - Robert Bradshaw, 2007-08
     595    - Simon King, 2011-12
     596
     597    """
     598    def __reduce__(self):
     599        """
     600        TESTS::
     601
     602            sage: from sage.structure.coerce_dict import TripleDictById
     603            sage: L = TripleDictById(8)
     604            sage: for i in range(100): L[i,i,i] = None
     605            sage: len(loads(dumps(L)))
     606            100
     607
     608        """
     609        return TripleDictById, (len(self.buckets), dict(self.iteritems()), self.threshold)
     610    def resize(self, int buckets=0):
     611        """
     612        Changes the number of buckets of self, while preserving the contents.
     613       
     614        If the number of buckets is 0 or not given, it resizes self to the
     615        smallest prime that is at least twice as large as self.
     616       
     617        EXAMPLES::
     618
     619            sage: from sage.structure.coerce_dict import TripleDictById
     620            sage: L = TripleDictById(8)
     621            sage: for i in range(100): L[i,i,i] = None
     622            sage: L.bucket_lens() # random
     623            [50, 0, 0, 0, 50, 0, 0, 0]
     624            sage: L.resize(7)
     625            sage: L.bucket_lens() # random
     626            [15, 14, 14, 14, 14, 15, 14]
     627            sage: L.resize()
     628            sage: len(L.bucket_lens())
     629            17
     630        """
     631        if buckets == 0:
     632            buckets = next_odd_prime(2*len(self.buckets))
     633        cdef TripleDictById new = TripleDictById(buckets, self)
     634        self.buckets = new.buckets
     635    cdef get(self, k1, k2, k3):
     636        cdef Py_ssize_t h = (<Py_ssize_t><void *>k1 + 13*<Py_ssize_t><void *>k2 ^ 503*<Py_ssize_t><void *>k3)
     637        if h < 0: h = -h
     638        cdef Py_ssize_t i
     639        cdef list bucket = self.buckets[h % PyList_GET_SIZE(self.buckets)]
     640        for i from 0 <= i < PyList_GET_SIZE(bucket) by 4:
     641            if bucket[i]() is k1 and \
     642               bucket[i+1]() is k2 and \
     643               bucket[i+2] == k3:
     644                return bucket[i+3]
     645        raise KeyError, (k1, k2, k3)
     646
     647    cdef set(self, k1, k2, k3, value):
     648        if self.threshold and self._size > len(self.buckets) * self.threshold:
     649            self.resize()
     650        cdef Py_ssize_t h = (<Py_ssize_t><void *>k1 + 13*<Py_ssize_t><void *>k2 ^ 503*<Py_ssize_t><void *>k3)
     651        if h < 0: h = -h
     652        cdef Py_ssize_t i
     653        cdef list bucket = self.buckets[h % PyList_GET_SIZE(self.buckets)]
     654        for i from 0 <= i < PyList_GET_SIZE(bucket) by 4:
     655            if bucket[i]() is k1 and \
     656               bucket[i+1]() is k2 and \
     657               bucket[i+2] == k3:
     658                bucket[i+3] = value
     659                return
     660        try:
     661            r1 = ref(k1,self.eraser)
     662        except TypeError:
     663            r1 = ConstantFunction(k1)
     664        try:
     665            r2 = ref(k2,self.eraser)
     666        except TypeError:
     667            r2 = ConstantFunction(k2)
     668        bucket += [r1, r2, k3, value]
     669        self._size += 1       
     670    def __delitem__(self, k):
     671        """
     672        EXAMPLES::
     673
     674            sage: from sage.structure.coerce_dict import TripleDictById
     675            sage: L = TripleDictById(31)
     676            sage: a = 'a'; b = 'b'; c = 15
     677            sage: L[a,b,c] = -1
     678            sage: del L[a,b,15]
     679            Traceback (most recent call last):
     680            ...
     681            KeyError: ('a', 'b', 15)
     682            sage: del L[a,b,c]
     683            sage: len(L)
     684            0
     685        """
     686        try:
     687            k1, k2, k3 = k
     688        except (TypeError,ValueError):
     689            raise KeyError, k
     690        cdef Py_ssize_t h = (<Py_ssize_t><void *>k1 + 13*<Py_ssize_t><void *>k2 ^ 503*<Py_ssize_t><void *>k3)
     691        if h < 0: h = -h
     692        cdef Py_ssize_t i
     693        cdef list bucket = self.buckets[h % PyList_GET_SIZE(self.buckets)]
     694        for i from 0 <= i < PyList_GET_SIZE(bucket) by 4:
     695            if bucket[i]() is k1 and \
     696               bucket[i+1]() is k2 and \
     697               bucket[i+2] is k3:
     698                del bucket[i:i+4]
     699                self._size -= 1
     700                return
     701        raise KeyError, k
     702       
     703       
     704
     705
    345706
    346707cdef class TripleDictIter:
    347708    def __init__(self, pairs):
     
    383744            k2 = self.bucket_iter.next()
    384745            k3 = self.bucket_iter.next()
    385746            value = self.bucket_iter.next()
    386             return ((k1, k2, k3), value)
     747            return ((k1(), k2(), k3), value)
    387748        except StopIteration:
    388749            self.bucket_iter = None
    389750            return self.next()