1 /*
2  * This file is part of the MicroPython project, http://micropython.org/
3  *
4  * The MIT License (MIT)
5  *
6  * Copyright (c) 2014 Damien P. George
7  *
8  * Permission is hereby granted, free of charge, to any person obtaining a copy
9  * of this software and associated documentation files (the "Software"), to deal
10  * in the Software without restriction, including without limitation the rights
11  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12  * copies of the Software, and to permit persons to whom the Software is
13  * furnished to do so, subject to the following conditions:
14  *
15  * The above copyright notice and this permission notice shall be included in
16  * all copies or substantial portions of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24  * THE SOFTWARE.
25  */
26 
27 #include "py/objlist.h"
28 #include "py/runtime.h"
29 
30 #if MICROPY_PY_UHEAPQ
31 
32 // the algorithm here is modelled on CPython's heapq.py
33 
uheapq_get_heap(mp_obj_t heap_in)34 STATIC mp_obj_list_t *uheapq_get_heap(mp_obj_t heap_in) {
35     if (!mp_obj_is_type(heap_in, &mp_type_list)) {
36         mp_raise_TypeError(MP_ERROR_TEXT("heap must be a list"));
37     }
38     return MP_OBJ_TO_PTR(heap_in);
39 }
40 
uheapq_heap_siftdown(mp_obj_list_t * heap,mp_uint_t start_pos,mp_uint_t pos)41 STATIC void uheapq_heap_siftdown(mp_obj_list_t *heap, mp_uint_t start_pos, mp_uint_t pos) {
42     mp_obj_t item = heap->items[pos];
43     while (pos > start_pos) {
44         mp_uint_t parent_pos = (pos - 1) >> 1;
45         mp_obj_t parent = heap->items[parent_pos];
46         if (mp_binary_op(MP_BINARY_OP_LESS, item, parent) == mp_const_true) {
47             heap->items[pos] = parent;
48             pos = parent_pos;
49         } else {
50             break;
51         }
52     }
53     heap->items[pos] = item;
54 }
55 
uheapq_heap_siftup(mp_obj_list_t * heap,mp_uint_t pos)56 STATIC void uheapq_heap_siftup(mp_obj_list_t *heap, mp_uint_t pos) {
57     mp_uint_t start_pos = pos;
58     mp_uint_t end_pos = heap->len;
59     mp_obj_t item = heap->items[pos];
60     for (mp_uint_t child_pos = 2 * pos + 1; child_pos < end_pos; child_pos = 2 * pos + 1) {
61         // choose right child if it's <= left child
62         if (child_pos + 1 < end_pos && mp_binary_op(MP_BINARY_OP_LESS, heap->items[child_pos], heap->items[child_pos + 1]) == mp_const_false) {
63             child_pos += 1;
64         }
65         // bubble up the smaller child
66         heap->items[pos] = heap->items[child_pos];
67         pos = child_pos;
68     }
69     heap->items[pos] = item;
70     uheapq_heap_siftdown(heap, start_pos, pos);
71 }
72 
mod_uheapq_heappush(mp_obj_t heap_in,mp_obj_t item)73 STATIC mp_obj_t mod_uheapq_heappush(mp_obj_t heap_in, mp_obj_t item) {
74     mp_obj_list_t *heap = uheapq_get_heap(heap_in);
75     mp_obj_list_append(heap_in, item);
76     uheapq_heap_siftdown(heap, 0, heap->len - 1);
77     return mp_const_none;
78 }
79 STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_uheapq_heappush_obj, mod_uheapq_heappush);
80 
mod_uheapq_heappop(mp_obj_t heap_in)81 STATIC mp_obj_t mod_uheapq_heappop(mp_obj_t heap_in) {
82     mp_obj_list_t *heap = uheapq_get_heap(heap_in);
83     if (heap->len == 0) {
84         mp_raise_msg(&mp_type_IndexError, MP_ERROR_TEXT("empty heap"));
85     }
86     mp_obj_t item = heap->items[0];
87     heap->len -= 1;
88     heap->items[0] = heap->items[heap->len];
89     heap->items[heap->len] = MP_OBJ_NULL; // so we don't retain a pointer
90     if (heap->len) {
91         uheapq_heap_siftup(heap, 0);
92     }
93     return item;
94 }
95 STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_uheapq_heappop_obj, mod_uheapq_heappop);
96 
mod_uheapq_heapify(mp_obj_t heap_in)97 STATIC mp_obj_t mod_uheapq_heapify(mp_obj_t heap_in) {
98     mp_obj_list_t *heap = uheapq_get_heap(heap_in);
99     for (mp_uint_t i = heap->len / 2; i > 0;) {
100         uheapq_heap_siftup(heap, --i);
101     }
102     return mp_const_none;
103 }
104 STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_uheapq_heapify_obj, mod_uheapq_heapify);
105 
106 #if !MICROPY_ENABLE_DYNRUNTIME
107 STATIC const mp_rom_map_elem_t mp_module_uheapq_globals_table[] = {
108     { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_uheapq) },
109     { MP_ROM_QSTR(MP_QSTR_heappush), MP_ROM_PTR(&mod_uheapq_heappush_obj) },
110     { MP_ROM_QSTR(MP_QSTR_heappop), MP_ROM_PTR(&mod_uheapq_heappop_obj) },
111     { MP_ROM_QSTR(MP_QSTR_heapify), MP_ROM_PTR(&mod_uheapq_heapify_obj) },
112 };
113 
114 STATIC MP_DEFINE_CONST_DICT(mp_module_uheapq_globals, mp_module_uheapq_globals_table);
115 
116 const mp_obj_module_t mp_module_uheapq = {
117     .base = { &mp_type_module },
118     .globals = (mp_obj_dict_t *)&mp_module_uheapq_globals,
119 };
120 #endif
121 
122 #endif // MICROPY_PY_UHEAPQ
123