xref: /third_party/python/Modules/_heapqmodule.c (revision 7db96d56)
1/* Drop in replacement for heapq.py
2
3C implementation derived directly from heapq.py in Py2.3
4which was written by Kevin O'Connor, augmented by Tim Peters,
5annotated by François Pinard, and converted to C by Raymond Hettinger.
6
7*/
8
9#ifndef Py_BUILD_CORE_BUILTIN
10#  define Py_BUILD_CORE_MODULE 1
11#endif
12
13#include "Python.h"
14#include "pycore_list.h"          // _PyList_ITEMS()
15
16#include "clinic/_heapqmodule.c.h"
17
18
19/*[clinic input]
20module _heapq
21[clinic start generated code]*/
22/*[clinic end generated code: output=da39a3ee5e6b4b0d input=d7cca0a2e4c0ceb3]*/
23
24static int
25siftdown(PyListObject *heap, Py_ssize_t startpos, Py_ssize_t pos)
26{
27    PyObject *newitem, *parent, **arr;
28    Py_ssize_t parentpos, size;
29    int cmp;
30
31    assert(PyList_Check(heap));
32    size = PyList_GET_SIZE(heap);
33    if (pos >= size) {
34        PyErr_SetString(PyExc_IndexError, "index out of range");
35        return -1;
36    }
37
38    /* Follow the path to the root, moving parents down until finding
39       a place newitem fits. */
40    arr = _PyList_ITEMS(heap);
41    newitem = arr[pos];
42    while (pos > startpos) {
43        parentpos = (pos - 1) >> 1;
44        parent = arr[parentpos];
45        Py_INCREF(newitem);
46        Py_INCREF(parent);
47        cmp = PyObject_RichCompareBool(newitem, parent, Py_LT);
48        Py_DECREF(parent);
49        Py_DECREF(newitem);
50        if (cmp < 0)
51            return -1;
52        if (size != PyList_GET_SIZE(heap)) {
53            PyErr_SetString(PyExc_RuntimeError,
54                            "list changed size during iteration");
55            return -1;
56        }
57        if (cmp == 0)
58            break;
59        arr = _PyList_ITEMS(heap);
60        parent = arr[parentpos];
61        newitem = arr[pos];
62        arr[parentpos] = newitem;
63        arr[pos] = parent;
64        pos = parentpos;
65    }
66    return 0;
67}
68
69static int
70siftup(PyListObject *heap, Py_ssize_t pos)
71{
72    Py_ssize_t startpos, endpos, childpos, limit;
73    PyObject *tmp1, *tmp2, **arr;
74    int cmp;
75
76    assert(PyList_Check(heap));
77    endpos = PyList_GET_SIZE(heap);
78    startpos = pos;
79    if (pos >= endpos) {
80        PyErr_SetString(PyExc_IndexError, "index out of range");
81        return -1;
82    }
83
84    /* Bubble up the smaller child until hitting a leaf. */
85    arr = _PyList_ITEMS(heap);
86    limit = endpos >> 1;         /* smallest pos that has no child */
87    while (pos < limit) {
88        /* Set childpos to index of smaller child.   */
89        childpos = 2*pos + 1;    /* leftmost child position  */
90        if (childpos + 1 < endpos) {
91            PyObject* a = arr[childpos];
92            PyObject* b = arr[childpos + 1];
93            Py_INCREF(a);
94            Py_INCREF(b);
95            cmp = PyObject_RichCompareBool(a, b, Py_LT);
96            Py_DECREF(a);
97            Py_DECREF(b);
98            if (cmp < 0)
99                return -1;
100            childpos += ((unsigned)cmp ^ 1);   /* increment when cmp==0 */
101            arr = _PyList_ITEMS(heap);         /* arr may have changed */
102            if (endpos != PyList_GET_SIZE(heap)) {
103                PyErr_SetString(PyExc_RuntimeError,
104                                "list changed size during iteration");
105                return -1;
106            }
107        }
108        /* Move the smaller child up. */
109        tmp1 = arr[childpos];
110        tmp2 = arr[pos];
111        arr[childpos] = tmp2;
112        arr[pos] = tmp1;
113        pos = childpos;
114    }
115    /* Bubble it up to its final resting place (by sifting its parents down). */
116    return siftdown(heap, startpos, pos);
117}
118
119/*[clinic input]
120_heapq.heappush
121
122    heap: object(subclass_of='&PyList_Type')
123    item: object
124    /
125
126Push item onto heap, maintaining the heap invariant.
127[clinic start generated code]*/
128
129static PyObject *
130_heapq_heappush_impl(PyObject *module, PyObject *heap, PyObject *item)
131/*[clinic end generated code: output=912c094f47663935 input=7c69611f3698aceb]*/
132{
133    if (PyList_Append(heap, item))
134        return NULL;
135
136    if (siftdown((PyListObject *)heap, 0, PyList_GET_SIZE(heap)-1))
137        return NULL;
138    Py_RETURN_NONE;
139}
140
141static PyObject *
142heappop_internal(PyObject *heap, int siftup_func(PyListObject *, Py_ssize_t))
143{
144    PyObject *lastelt, *returnitem;
145    Py_ssize_t n;
146
147    /* raises IndexError if the heap is empty */
148    n = PyList_GET_SIZE(heap);
149    if (n == 0) {
150        PyErr_SetString(PyExc_IndexError, "index out of range");
151        return NULL;
152    }
153
154    lastelt = PyList_GET_ITEM(heap, n-1) ;
155    Py_INCREF(lastelt);
156    if (PyList_SetSlice(heap, n-1, n, NULL)) {
157        Py_DECREF(lastelt);
158        return NULL;
159    }
160    n--;
161
162    if (!n)
163        return lastelt;
164    returnitem = PyList_GET_ITEM(heap, 0);
165    PyList_SET_ITEM(heap, 0, lastelt);
166    if (siftup_func((PyListObject *)heap, 0)) {
167        Py_DECREF(returnitem);
168        return NULL;
169    }
170    return returnitem;
171}
172
173/*[clinic input]
174_heapq.heappop
175
176    heap: object(subclass_of='&PyList_Type')
177    /
178
179Pop the smallest item off the heap, maintaining the heap invariant.
180[clinic start generated code]*/
181
182static PyObject *
183_heapq_heappop_impl(PyObject *module, PyObject *heap)
184/*[clinic end generated code: output=96dfe82d37d9af76 input=91487987a583c856]*/
185{
186    return heappop_internal(heap, siftup);
187}
188
189static PyObject *
190heapreplace_internal(PyObject *heap, PyObject *item, int siftup_func(PyListObject *, Py_ssize_t))
191{
192    PyObject *returnitem;
193
194    if (PyList_GET_SIZE(heap) == 0) {
195        PyErr_SetString(PyExc_IndexError, "index out of range");
196        return NULL;
197    }
198
199    returnitem = PyList_GET_ITEM(heap, 0);
200    Py_INCREF(item);
201    PyList_SET_ITEM(heap, 0, item);
202    if (siftup_func((PyListObject *)heap, 0)) {
203        Py_DECREF(returnitem);
204        return NULL;
205    }
206    return returnitem;
207}
208
209
210/*[clinic input]
211_heapq.heapreplace
212
213    heap: object(subclass_of='&PyList_Type')
214    item: object
215    /
216
217Pop and return the current smallest value, and add the new item.
218
219This is more efficient than heappop() followed by heappush(), and can be
220more appropriate when using a fixed-size heap.  Note that the value
221returned may be larger than item!  That constrains reasonable uses of
222this routine unless written as part of a conditional replacement:
223
224    if item > heap[0]:
225        item = heapreplace(heap, item)
226[clinic start generated code]*/
227
228static PyObject *
229_heapq_heapreplace_impl(PyObject *module, PyObject *heap, PyObject *item)
230/*[clinic end generated code: output=82ea55be8fbe24b4 input=719202ac02ba10c8]*/
231{
232    return heapreplace_internal(heap, item, siftup);
233}
234
235/*[clinic input]
236_heapq.heappushpop
237
238    heap: object(subclass_of='&PyList_Type')
239    item: object
240    /
241
242Push item on the heap, then pop and return the smallest item from the heap.
243
244The combined action runs more efficiently than heappush() followed by
245a separate call to heappop().
246[clinic start generated code]*/
247
248static PyObject *
249_heapq_heappushpop_impl(PyObject *module, PyObject *heap, PyObject *item)
250/*[clinic end generated code: output=67231dc98ed5774f input=5dc701f1eb4a4aa7]*/
251{
252    PyObject *returnitem;
253    int cmp;
254
255    if (PyList_GET_SIZE(heap) == 0) {
256        Py_INCREF(item);
257        return item;
258    }
259
260    PyObject* top = PyList_GET_ITEM(heap, 0);
261    Py_INCREF(top);
262    cmp = PyObject_RichCompareBool(top, item, Py_LT);
263    Py_DECREF(top);
264    if (cmp < 0)
265        return NULL;
266    if (cmp == 0) {
267        Py_INCREF(item);
268        return item;
269    }
270
271    if (PyList_GET_SIZE(heap) == 0) {
272        PyErr_SetString(PyExc_IndexError, "index out of range");
273        return NULL;
274    }
275
276    returnitem = PyList_GET_ITEM(heap, 0);
277    Py_INCREF(item);
278    PyList_SET_ITEM(heap, 0, item);
279    if (siftup((PyListObject *)heap, 0)) {
280        Py_DECREF(returnitem);
281        return NULL;
282    }
283    return returnitem;
284}
285
286static Py_ssize_t
287keep_top_bit(Py_ssize_t n)
288{
289    int i = 0;
290
291    while (n > 1) {
292        n >>= 1;
293        i++;
294    }
295    return n << i;
296}
297
298/* Cache friendly version of heapify()
299   -----------------------------------
300
301   Build-up a heap in O(n) time by performing siftup() operations
302   on nodes whose children are already heaps.
303
304   The simplest way is to sift the nodes in reverse order from
305   n//2-1 to 0 inclusive.  The downside is that children may be
306   out of cache by the time their parent is reached.
307
308   A better way is to not wait for the children to go out of cache.
309   Once a sibling pair of child nodes have been sifted, immediately
310   sift their parent node (while the children are still in cache).
311
312   Both ways build child heaps before their parents, so both ways
313   do the exact same number of comparisons and produce exactly
314   the same heap.  The only difference is that the traversal
315   order is optimized for cache efficiency.
316*/
317
318static PyObject *
319cache_friendly_heapify(PyObject *heap, int siftup_func(PyListObject *, Py_ssize_t))
320{
321    Py_ssize_t i, j, m, mhalf, leftmost;
322
323    m = PyList_GET_SIZE(heap) >> 1;         /* index of first childless node */
324    leftmost = keep_top_bit(m + 1) - 1;     /* leftmost node in row of m */
325    mhalf = m >> 1;                         /* parent of first childless node */
326
327    for (i = leftmost - 1 ; i >= mhalf ; i--) {
328        j = i;
329        while (1) {
330            if (siftup_func((PyListObject *)heap, j))
331                return NULL;
332            if (!(j & 1))
333                break;
334            j >>= 1;
335        }
336    }
337
338    for (i = m - 1 ; i >= leftmost ; i--) {
339        j = i;
340        while (1) {
341            if (siftup_func((PyListObject *)heap, j))
342                return NULL;
343            if (!(j & 1))
344                break;
345            j >>= 1;
346        }
347    }
348    Py_RETURN_NONE;
349}
350
351static PyObject *
352heapify_internal(PyObject *heap, int siftup_func(PyListObject *, Py_ssize_t))
353{
354    Py_ssize_t i, n;
355
356    /* For heaps likely to be bigger than L1 cache, we use the cache
357       friendly heapify function.  For smaller heaps that fit entirely
358       in cache, we prefer the simpler algorithm with less branching.
359    */
360    n = PyList_GET_SIZE(heap);
361    if (n > 2500)
362        return cache_friendly_heapify(heap, siftup_func);
363
364    /* Transform bottom-up.  The largest index there's any point to
365       looking at is the largest with a child index in-range, so must
366       have 2*i + 1 < n, or i < (n-1)/2.  If n is even = 2*j, this is
367       (2*j-1)/2 = j-1/2 so j-1 is the largest, which is n//2 - 1.  If
368       n is odd = 2*j+1, this is (2*j+1-1)/2 = j so j-1 is the largest,
369       and that's again n//2-1.
370    */
371    for (i = (n >> 1) - 1 ; i >= 0 ; i--)
372        if (siftup_func((PyListObject *)heap, i))
373            return NULL;
374    Py_RETURN_NONE;
375}
376
377/*[clinic input]
378_heapq.heapify
379
380    heap: object(subclass_of='&PyList_Type')
381    /
382
383Transform list into a heap, in-place, in O(len(heap)) time.
384[clinic start generated code]*/
385
386static PyObject *
387_heapq_heapify_impl(PyObject *module, PyObject *heap)
388/*[clinic end generated code: output=e63a636fcf83d6d0 input=53bb7a2166febb73]*/
389{
390    return heapify_internal(heap, siftup);
391}
392
393static int
394siftdown_max(PyListObject *heap, Py_ssize_t startpos, Py_ssize_t pos)
395{
396    PyObject *newitem, *parent, **arr;
397    Py_ssize_t parentpos, size;
398    int cmp;
399
400    assert(PyList_Check(heap));
401    size = PyList_GET_SIZE(heap);
402    if (pos >= size) {
403        PyErr_SetString(PyExc_IndexError, "index out of range");
404        return -1;
405    }
406
407    /* Follow the path to the root, moving parents down until finding
408       a place newitem fits. */
409    arr = _PyList_ITEMS(heap);
410    newitem = arr[pos];
411    while (pos > startpos) {
412        parentpos = (pos - 1) >> 1;
413        parent = arr[parentpos];
414        Py_INCREF(parent);
415        Py_INCREF(newitem);
416        cmp = PyObject_RichCompareBool(parent, newitem, Py_LT);
417        Py_DECREF(parent);
418        Py_DECREF(newitem);
419        if (cmp < 0)
420            return -1;
421        if (size != PyList_GET_SIZE(heap)) {
422            PyErr_SetString(PyExc_RuntimeError,
423                            "list changed size during iteration");
424            return -1;
425        }
426        if (cmp == 0)
427            break;
428        arr = _PyList_ITEMS(heap);
429        parent = arr[parentpos];
430        newitem = arr[pos];
431        arr[parentpos] = newitem;
432        arr[pos] = parent;
433        pos = parentpos;
434    }
435    return 0;
436}
437
438static int
439siftup_max(PyListObject *heap, Py_ssize_t pos)
440{
441    Py_ssize_t startpos, endpos, childpos, limit;
442    PyObject *tmp1, *tmp2, **arr;
443    int cmp;
444
445    assert(PyList_Check(heap));
446    endpos = PyList_GET_SIZE(heap);
447    startpos = pos;
448    if (pos >= endpos) {
449        PyErr_SetString(PyExc_IndexError, "index out of range");
450        return -1;
451    }
452
453    /* Bubble up the smaller child until hitting a leaf. */
454    arr = _PyList_ITEMS(heap);
455    limit = endpos >> 1;         /* smallest pos that has no child */
456    while (pos < limit) {
457        /* Set childpos to index of smaller child.   */
458        childpos = 2*pos + 1;    /* leftmost child position  */
459        if (childpos + 1 < endpos) {
460            PyObject* a = arr[childpos + 1];
461            PyObject* b = arr[childpos];
462            Py_INCREF(a);
463            Py_INCREF(b);
464            cmp = PyObject_RichCompareBool(a, b, Py_LT);
465            Py_DECREF(a);
466            Py_DECREF(b);
467            if (cmp < 0)
468                return -1;
469            childpos += ((unsigned)cmp ^ 1);   /* increment when cmp==0 */
470            arr = _PyList_ITEMS(heap);         /* arr may have changed */
471            if (endpos != PyList_GET_SIZE(heap)) {
472                PyErr_SetString(PyExc_RuntimeError,
473                                "list changed size during iteration");
474                return -1;
475            }
476        }
477        /* Move the smaller child up. */
478        tmp1 = arr[childpos];
479        tmp2 = arr[pos];
480        arr[childpos] = tmp2;
481        arr[pos] = tmp1;
482        pos = childpos;
483    }
484    /* Bubble it up to its final resting place (by sifting its parents down). */
485    return siftdown_max(heap, startpos, pos);
486}
487
488
489/*[clinic input]
490_heapq._heappop_max
491
492    heap: object(subclass_of='&PyList_Type')
493    /
494
495Maxheap variant of heappop.
496[clinic start generated code]*/
497
498static PyObject *
499_heapq__heappop_max_impl(PyObject *module, PyObject *heap)
500/*[clinic end generated code: output=9e77aadd4e6a8760 input=362c06e1c7484793]*/
501{
502    return heappop_internal(heap, siftup_max);
503}
504
505/*[clinic input]
506_heapq._heapreplace_max
507
508    heap: object(subclass_of='&PyList_Type')
509    item: object
510    /
511
512Maxheap variant of heapreplace.
513[clinic start generated code]*/
514
515static PyObject *
516_heapq__heapreplace_max_impl(PyObject *module, PyObject *heap,
517                             PyObject *item)
518/*[clinic end generated code: output=8ad7545e4a5e8adb input=f2dd27cbadb948d7]*/
519{
520    return heapreplace_internal(heap, item, siftup_max);
521}
522
523/*[clinic input]
524_heapq._heapify_max
525
526    heap: object(subclass_of='&PyList_Type')
527    /
528
529Maxheap variant of heapify.
530[clinic start generated code]*/
531
532static PyObject *
533_heapq__heapify_max_impl(PyObject *module, PyObject *heap)
534/*[clinic end generated code: output=2cb028beb4a8b65e input=c1f765ee69f124b8]*/
535{
536    return heapify_internal(heap, siftup_max);
537}
538
539static PyMethodDef heapq_methods[] = {
540    _HEAPQ_HEAPPUSH_METHODDEF
541    _HEAPQ_HEAPPUSHPOP_METHODDEF
542    _HEAPQ_HEAPPOP_METHODDEF
543    _HEAPQ_HEAPREPLACE_METHODDEF
544    _HEAPQ_HEAPIFY_METHODDEF
545    _HEAPQ__HEAPPOP_MAX_METHODDEF
546    _HEAPQ__HEAPIFY_MAX_METHODDEF
547    _HEAPQ__HEAPREPLACE_MAX_METHODDEF
548    {NULL, NULL}           /* sentinel */
549};
550
551PyDoc_STRVAR(module_doc,
552"Heap queue algorithm (a.k.a. priority queue).\n\
553\n\
554Heaps are arrays for which a[k] <= a[2*k+1] and a[k] <= a[2*k+2] for\n\
555all k, counting elements from 0.  For the sake of comparison,\n\
556non-existing elements are considered to be infinite.  The interesting\n\
557property of a heap is that a[0] is always its smallest element.\n\
558\n\
559Usage:\n\
560\n\
561heap = []            # creates an empty heap\n\
562heappush(heap, item) # pushes a new item on the heap\n\
563item = heappop(heap) # pops the smallest item from the heap\n\
564item = heap[0]       # smallest item on the heap without popping it\n\
565heapify(x)           # transforms list into a heap, in-place, in linear time\n\
566item = heapreplace(heap, item) # pops and returns smallest item, and adds\n\
567                               # new item; the heap size is unchanged\n\
568\n\
569Our API differs from textbook heap algorithms as follows:\n\
570\n\
571- We use 0-based indexing.  This makes the relationship between the\n\
572  index for a node and the indexes for its children slightly less\n\
573  obvious, but is more suitable since Python uses 0-based indexing.\n\
574\n\
575- Our heappop() method returns the smallest item, not the largest.\n\
576\n\
577These two make it possible to view the heap as a regular Python list\n\
578without surprises: heap[0] is the smallest item, and heap.sort()\n\
579maintains the heap invariant!\n");
580
581
582PyDoc_STRVAR(__about__,
583"Heap queues\n\
584\n\
585[explanation by Fran\xc3\xa7ois Pinard]\n\
586\n\
587Heaps are arrays for which a[k] <= a[2*k+1] and a[k] <= a[2*k+2] for\n\
588all k, counting elements from 0.  For the sake of comparison,\n\
589non-existing elements are considered to be infinite.  The interesting\n\
590property of a heap is that a[0] is always its smallest element.\n"
591"\n\
592The strange invariant above is meant to be an efficient memory\n\
593representation for a tournament.  The numbers below are `k', not a[k]:\n\
594\n\
595                                   0\n\
596\n\
597                  1                                 2\n\
598\n\
599          3               4                5               6\n\
600\n\
601      7       8       9       10      11      12      13      14\n\
602\n\
603    15 16   17 18   19 20   21 22   23 24   25 26   27 28   29 30\n\
604\n\
605\n\
606In the tree above, each cell `k' is topping `2*k+1' and `2*k+2'.  In\n\
607a usual binary tournament we see in sports, each cell is the winner\n\
608over the two cells it tops, and we can trace the winner down the tree\n\
609to see all opponents s/he had.  However, in many computer applications\n\
610of such tournaments, we do not need to trace the history of a winner.\n\
611To be more memory efficient, when a winner is promoted, we try to\n\
612replace it by something else at a lower level, and the rule becomes\n\
613that a cell and the two cells it tops contain three different items,\n\
614but the top cell \"wins\" over the two topped cells.\n"
615"\n\
616If this heap invariant is protected at all time, index 0 is clearly\n\
617the overall winner.  The simplest algorithmic way to remove it and\n\
618find the \"next\" winner is to move some loser (let's say cell 30 in the\n\
619diagram above) into the 0 position, and then percolate this new 0 down\n\
620the tree, exchanging values, until the invariant is re-established.\n\
621This is clearly logarithmic on the total number of items in the tree.\n\
622By iterating over all items, you get an O(n ln n) sort.\n"
623"\n\
624A nice feature of this sort is that you can efficiently insert new\n\
625items while the sort is going on, provided that the inserted items are\n\
626not \"better\" than the last 0'th element you extracted.  This is\n\
627especially useful in simulation contexts, where the tree holds all\n\
628incoming events, and the \"win\" condition means the smallest scheduled\n\
629time.  When an event schedule other events for execution, they are\n\
630scheduled into the future, so they can easily go into the heap.  So, a\n\
631heap is a good structure for implementing schedulers (this is what I\n\
632used for my MIDI sequencer :-).\n"
633"\n\
634Various structures for implementing schedulers have been extensively\n\
635studied, and heaps are good for this, as they are reasonably speedy,\n\
636the speed is almost constant, and the worst case is not much different\n\
637than the average case.  However, there are other representations which\n\
638are more efficient overall, yet the worst cases might be terrible.\n"
639"\n\
640Heaps are also very useful in big disk sorts.  You most probably all\n\
641know that a big sort implies producing \"runs\" (which are pre-sorted\n\
642sequences, which size is usually related to the amount of CPU memory),\n\
643followed by a merging passes for these runs, which merging is often\n\
644very cleverly organised[1].  It is very important that the initial\n\
645sort produces the longest runs possible.  Tournaments are a good way\n\
646to that.  If, using all the memory available to hold a tournament, you\n\
647replace and percolate items that happen to fit the current run, you'll\n\
648produce runs which are twice the size of the memory for random input,\n\
649and much better for input fuzzily ordered.\n"
650"\n\
651Moreover, if you output the 0'th item on disk and get an input which\n\
652may not fit in the current tournament (because the value \"wins\" over\n\
653the last output value), it cannot fit in the heap, so the size of the\n\
654heap decreases.  The freed memory could be cleverly reused immediately\n\
655for progressively building a second heap, which grows at exactly the\n\
656same rate the first heap is melting.  When the first heap completely\n\
657vanishes, you switch heaps and start a new run.  Clever and quite\n\
658effective!\n\
659\n\
660In a word, heaps are useful memory structures to know.  I use them in\n\
661a few applications, and I think it is good to keep a `heap' module\n\
662around. :-)\n"
663"\n\
664--------------------\n\
665[1] The disk balancing algorithms which are current, nowadays, are\n\
666more annoying than clever, and this is a consequence of the seeking\n\
667capabilities of the disks.  On devices which cannot seek, like big\n\
668tape drives, the story was quite different, and one had to be very\n\
669clever to ensure (far in advance) that each tape movement will be the\n\
670most effective possible (that is, will best participate at\n\
671\"progressing\" the merge).  Some tapes were even able to read\n\
672backwards, and this was also used to avoid the rewinding time.\n\
673Believe me, real good tape sorts were quite spectacular to watch!\n\
674From all times, sorting has always been a Great Art! :-)\n");
675
676
677static int
678heapq_exec(PyObject *m)
679{
680    PyObject *about = PyUnicode_FromString(__about__);
681    if (PyModule_AddObject(m, "__about__", about) < 0) {
682        Py_DECREF(about);
683        return -1;
684    }
685    return 0;
686}
687
688static struct PyModuleDef_Slot heapq_slots[] = {
689    {Py_mod_exec, heapq_exec},
690    {0, NULL}
691};
692
693static struct PyModuleDef _heapqmodule = {
694    PyModuleDef_HEAD_INIT,
695    "_heapq",
696    module_doc,
697    0,
698    heapq_methods,
699    heapq_slots,
700    NULL,
701    NULL,
702    NULL
703};
704
705PyMODINIT_FUNC
706PyInit__heapq(void)
707{
708    return PyModuleDef_Init(&_heapqmodule);
709}
710