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