1 //
2 //=======================================================================
3 // Copyright 2009 Trustees of Indiana University
4 // Authors: Jeremiah J. Willcock, Andrew Lumsdaine
5 //
6 // Distributed under the Boost Software License, Version 1.0. (See
7 // accompanying file LICENSE_1_0.txt or copy at
8 // http://www.boost.org/LICENSE_1_0.txt)
9 //=======================================================================
10 //
11 #ifndef BOOST_D_ARY_HEAP_HPP
12 #define BOOST_D_ARY_HEAP_HPP
13 
14 #include <vector>
15 #include <cstddef>
16 #include <algorithm>
17 #include <utility>
18 #include <boost/assert.hpp>
19 #include <boost/static_assert.hpp>
20 #include <boost/shared_array.hpp>
21 #include <boost/property_map/property_map.hpp>
22 
23 // WARNING: it is not safe to copy a d_ary_heap_indirect and then modify one of
24 // the copies.  The class is required to be copyable so it can be passed around
25 // (without move support from C++11), but it deep-copies the heap contents yet
26 // shallow-copies the index_in_heap_map.
27 
28 namespace boost
29 {
30 
31 // Swap two elements in a property map without assuming they model
32 // LvaluePropertyMap -- currently not used
33 template < typename PropMap >
property_map_swap(PropMap prop_map,const typename boost::property_traits<PropMap>::key_type & ka,const typename boost::property_traits<PropMap>::key_type & kb)34 inline void property_map_swap(PropMap prop_map,
35     const typename boost::property_traits< PropMap >::key_type& ka,
36     const typename boost::property_traits< PropMap >::key_type& kb)
37 {
38     typename boost::property_traits< PropMap >::value_type va
39         = get(prop_map, ka);
40     put(prop_map, ka, get(prop_map, kb));
41     put(prop_map, kb, va);
42 }
43 
44 namespace detail
45 {
46     template < typename Value > class fixed_max_size_vector
47     {
48         boost::shared_array< Value > m_data;
49         std::size_t m_size;
50 
51     public:
52         typedef std::size_t size_type;
fixed_max_size_vector(std::size_t max_size)53         fixed_max_size_vector(std::size_t max_size)
54         : m_data(new Value[max_size]), m_size(0)
55         {
56         }
size() const57         std::size_t size() const { return m_size; }
empty() const58         bool empty() const { return m_size == 0; }
operator [](std::size_t i)59         Value& operator[](std::size_t i) { return m_data[i]; }
operator [](std::size_t i) const60         const Value& operator[](std::size_t i) const { return m_data[i]; }
push_back(Value v)61         void push_back(Value v) { m_data[m_size++] = v; }
pop_back()62         void pop_back() { --m_size; }
back()63         Value& back() { return m_data[m_size - 1]; }
back() const64         const Value& back() const { return m_data[m_size - 1]; }
65     };
66 }
67 
68 // D-ary heap using an indirect compare operator (use identity_property_map
69 // as DistanceMap to get a direct compare operator).  This heap appears to be
70 // commonly used for Dijkstra's algorithm for its good practical performance
71 // on some platforms; asymptotically, it has an O(lg N) decrease-key
72 // operation while that can be done in constant time on a relaxed heap.  The
73 // implementation is mostly based on the binary heap page on Wikipedia and
74 // online sources that state that the operations are the same for d-ary
75 // heaps.  This code is not based on the old Boost d-ary heap code.
76 //
77 // - d_ary_heap_indirect is a model of UpdatableQueue as is needed for
78 //   dijkstra_shortest_paths.
79 //
80 // - Value must model Assignable.
81 // - Arity must be at least 2 (optimal value appears to be 4, both in my and
82 //   third-party experiments).
83 // - IndexInHeapMap must be a ReadWritePropertyMap from Value to
84 //   Container::size_type (to store the index of each stored value within the
85 //   heap for decrease-key aka update).
86 // - DistanceMap must be a ReadablePropertyMap from Value to something
87 //   (typedef'ed as distance_type).
88 // - Compare must be a BinaryPredicate used as a less-than operator on
89 //   distance_type.
90 // - Container must be a random-access, contiguous container (in practice,
91 //   the operations used probably require that it is std::vector<Value>).
92 //
93 template < typename Value, std::size_t Arity, typename IndexInHeapPropertyMap,
94     typename DistanceMap, typename Compare = std::less< Value >,
95     typename Container = std::vector< Value > >
96 class d_ary_heap_indirect
97 {
98     BOOST_STATIC_ASSERT(Arity >= 2);
99 
100 public:
101     typedef typename Container::size_type size_type;
102     typedef Value value_type;
103     typedef typename boost::property_traits< DistanceMap >::value_type key_type;
104     typedef DistanceMap key_map;
105 
d_ary_heap_indirect(DistanceMap distance,IndexInHeapPropertyMap index_in_heap,const Compare & compare=Compare (),const Container & data=Container ())106     d_ary_heap_indirect(DistanceMap distance,
107         IndexInHeapPropertyMap index_in_heap,
108         const Compare& compare = Compare(), const Container& data = Container())
109     : compare(compare)
110     , data(data)
111     , distance(distance)
112     , index_in_heap(index_in_heap)
113     {
114     }
115     /* Implicit copy constructor */
116     /* Implicit assignment operator */
117 
size() const118     size_type size() const { return data.size(); }
119 
empty() const120     bool empty() const { return data.empty(); }
121 
push(const Value & v)122     void push(const Value& v)
123     {
124         size_type index = data.size();
125         data.push_back(v);
126         put(index_in_heap, v, index);
127         preserve_heap_property_up(index);
128         verify_heap();
129     }
130 
top()131     Value& top()
132     {
133         BOOST_ASSERT(!this->empty());
134         return data[0];
135     }
136 
top() const137     const Value& top() const
138     {
139         BOOST_ASSERT(!this->empty());
140         return data[0];
141     }
142 
pop()143     void pop()
144     {
145         BOOST_ASSERT(!this->empty());
146         put(index_in_heap, data[0], (size_type)(-1));
147         if (data.size() != 1)
148         {
149             data[0] = data.back();
150             put(index_in_heap, data[0], (size_type)(0));
151             data.pop_back();
152             preserve_heap_property_down();
153             verify_heap();
154         }
155         else
156         {
157             data.pop_back();
158         }
159     }
160 
161     // This function assumes the key has been updated (using an external write
162     // to the distance map or such)
163     // See
164     // http://coding.derkeiler.com/Archive/General/comp.theory/2007-05/msg00043.html
update(const Value & v)165     void update(const Value& v)
166     { /* decrease-key */
167         size_type index = get(index_in_heap, v);
168         preserve_heap_property_up(index);
169         verify_heap();
170     }
171 
contains(const Value & v) const172     bool contains(const Value& v) const
173     {
174         size_type index = get(index_in_heap, v);
175         return (index != (size_type)(-1));
176     }
177 
push_or_update(const Value & v)178     void push_or_update(const Value& v)
179     { /* insert if not present, else update */
180         size_type index = get(index_in_heap, v);
181         if (index == (size_type)(-1))
182         {
183             index = data.size();
184             data.push_back(v);
185             put(index_in_heap, v, index);
186         }
187         preserve_heap_property_up(index);
188         verify_heap();
189     }
190 
keys() const191     DistanceMap keys() const { return distance; }
192 
193 private:
194     Compare compare;
195     Container data;
196     DistanceMap distance;
197     IndexInHeapPropertyMap index_in_heap;
198 
199     // The distances being compared using compare and that are stored in the
200     // distance map
201     typedef typename boost::property_traits< DistanceMap >::value_type
202         distance_type;
203 
204     // Get the parent of a given node in the heap
parent(size_type index)205     static size_type parent(size_type index) { return (index - 1) / Arity; }
206 
207     // Get the child_idx'th child of a given node; 0 <= child_idx < Arity
child(size_type index,std::size_t child_idx)208     static size_type child(size_type index, std::size_t child_idx)
209     {
210         return index * Arity + child_idx + 1;
211     }
212 
213     // Swap two elements in the heap by index, updating index_in_heap
swap_heap_elements(size_type index_a,size_type index_b)214     void swap_heap_elements(size_type index_a, size_type index_b)
215     {
216         using std::swap;
217         Value value_a = data[index_a];
218         Value value_b = data[index_b];
219         data[index_a] = value_b;
220         data[index_b] = value_a;
221         put(index_in_heap, value_a, index_b);
222         put(index_in_heap, value_b, index_a);
223     }
224 
225     // Emulate the indirect_cmp that is now folded into this heap class
compare_indirect(const Value & a,const Value & b) const226     bool compare_indirect(const Value& a, const Value& b) const
227     {
228         return compare(get(distance, a), get(distance, b));
229     }
230 
231     // Verify that the array forms a heap; commented out by default
verify_heap() const232     void verify_heap() const
233     {
234         // This is a very expensive test so it should be disabled even when
235         // NDEBUG is not defined
236 #if 0
237       for (size_t i = 1; i < data.size(); ++i) {
238         if (compare_indirect(data[i], data[parent(i)])) {
239           BOOST_ASSERT (!"Element is smaller than its parent");
240         }
241       }
242 #endif
243     }
244 
245     // Starting at a node, move up the tree swapping elements to preserve the
246     // heap property
preserve_heap_property_up(size_type index)247     void preserve_heap_property_up(size_type index)
248     {
249         size_type orig_index = index;
250         size_type num_levels_moved = 0;
251         // The first loop just saves swaps that need to be done in order to
252         // avoid aliasing issues in its search; there is a second loop that does
253         // the necessary swap operations
254         if (index == 0)
255             return; // Do nothing on root
256         Value currently_being_moved = data[index];
257         distance_type currently_being_moved_dist
258             = get(distance, currently_being_moved);
259         for (;;)
260         {
261             if (index == 0)
262                 break; // Stop at root
263             size_type parent_index = parent(index);
264             Value parent_value = data[parent_index];
265             if (compare(
266                     currently_being_moved_dist, get(distance, parent_value)))
267             {
268                 ++num_levels_moved;
269                 index = parent_index;
270                 continue;
271             }
272             else
273             {
274                 break; // Heap property satisfied
275             }
276         }
277         // Actually do the moves -- move num_levels_moved elements down in the
278         // tree, then put currently_being_moved at the top
279         index = orig_index;
280         for (size_type i = 0; i < num_levels_moved; ++i)
281         {
282             size_type parent_index = parent(index);
283             Value parent_value = data[parent_index];
284             put(index_in_heap, parent_value, index);
285             data[index] = parent_value;
286             index = parent_index;
287         }
288         data[index] = currently_being_moved;
289         put(index_in_heap, currently_being_moved, index);
290         verify_heap();
291     }
292 
293     // From the root, swap elements (each one with its smallest child) if there
294     // are any parent-child pairs that violate the heap property
preserve_heap_property_down()295     void preserve_heap_property_down()
296     {
297         if (data.empty())
298             return;
299         size_type index = 0;
300         Value currently_being_moved = data[0];
301         distance_type currently_being_moved_dist
302             = get(distance, currently_being_moved);
303         size_type heap_size = data.size();
304         Value* data_ptr = &data[0];
305         for (;;)
306         {
307             size_type first_child_index = child(index, 0);
308             if (first_child_index >= heap_size)
309                 break; /* No children */
310             Value* child_base_ptr = data_ptr + first_child_index;
311             size_type smallest_child_index = 0;
312             distance_type smallest_child_dist
313                 = get(distance, child_base_ptr[smallest_child_index]);
314             if (first_child_index + Arity <= heap_size)
315             {
316                 // Special case for a statically known loop count (common case)
317                 for (size_t i = 1; i < Arity; ++i)
318                 {
319                     Value i_value = child_base_ptr[i];
320                     distance_type i_dist = get(distance, i_value);
321                     if (compare(i_dist, smallest_child_dist))
322                     {
323                         smallest_child_index = i;
324                         smallest_child_dist = i_dist;
325                     }
326                 }
327             }
328             else
329             {
330                 for (size_t i = 1; i < heap_size - first_child_index; ++i)
331                 {
332                     distance_type i_dist = get(distance, child_base_ptr[i]);
333                     if (compare(i_dist, smallest_child_dist))
334                     {
335                         smallest_child_index = i;
336                         smallest_child_dist = i_dist;
337                     }
338                 }
339             }
340             if (compare(smallest_child_dist, currently_being_moved_dist))
341             {
342                 swap_heap_elements(
343                     smallest_child_index + first_child_index, index);
344                 index = smallest_child_index + first_child_index;
345                 continue;
346             }
347             else
348             {
349                 break; // Heap property satisfied
350             }
351         }
352         verify_heap();
353     }
354 };
355 
356 } // namespace boost
357 
358 #endif // BOOST_D_ARY_HEAP_HPP
359