2020#include < vector>
2121
2222#include " absl/log/check.h"
23+ #include " absl/types/span.h"
24+ #include " ortools/base/strong_int.h"
25+
26+ namespace internal {
27+ template <typename T, bool IsStrong>
28+ struct ArrayIndexTypeHelper {
29+ using type = T;
30+ };
31+
32+ template <typename T>
33+ struct ArrayIndexTypeHelper <T, true > {
34+ using type = typename T::ValueType;
35+ };
36+
37+ template <typename T>
38+ using ArrayIndexType =
39+ typename ArrayIndexTypeHelper<T, util_intops::IsStrongInt<T>::value>::type;
40+ } // namespace internal
2341
2442// Adjustable k-ary heap for std::pair<Priority, Index> classes containing a
2543// priority and an index referring to an array where the relevant data is
@@ -53,24 +71,25 @@ class AdjustableKAryHeap {
5371
5472 // Construct a k-heap from an existing vector, tracking original indices.
5573 // `universe_size` is the maximum possible index in `elements`.
56- explicit AdjustableKAryHeap (const std::vector< Aggregate>& elements,
74+ explicit AdjustableKAryHeap (const absl::Span< const Aggregate> elements,
5775 HeapIndex universe_size) {
5876 Load (elements, universe_size);
5977 }
6078
61- explicit AdjustableKAryHeap (const std::vector< Index>& indices,
62- const std::vector< Priority>& priorities,
79+ explicit AdjustableKAryHeap (const absl::Span< const Index> indices,
80+ const absl::Span< const Priority> priorities,
6381 HeapIndex universe_size) {
6482 Load (indices, priorities, universe_size);
6583 }
6684
6785 void Clear () {
6886 data_.clear ();
6987 heap_positions_.clear ();
70- heap_size_ = 0 ;
88+ heap_size_ = HeapIndex ( 0 ) ;
7189 }
7290
73- void Load (const std::vector<Aggregate>& elements, HeapIndex universe_size) {
91+ void Load (const absl::Span<const Aggregate> elements,
92+ HeapIndex universe_size) {
7493 data_.resize (elements.size ());
7594 heap_size_ = elements.size ();
7695 std::copy (elements.begin (), elements.end (), data_.begin ());
@@ -81,14 +100,21 @@ class AdjustableKAryHeap {
81100 BuildHeap ();
82101 }
83102
84- void Load (const std::vector<Index>& indices,
85- const std::vector<Priority>& priorities, HeapIndex universe_size) {
103+ void Load (const absl::Span<const Index> indices,
104+ const absl::Span<const Priority> priorities,
105+ HeapIndex universe_size) {
106+ CHECK_EQ (indices.size (), priorities.size ());
107+ data_.resize (indices.size ());
108+ indices_.resize (indices.size ());
109+ priorities_.resize (indices.size ());
86110 std::copy (indices.begin (), indices.end (), indices_.begin ());
87111 std::copy (priorities.begin (), priorities.end (), priorities_.begin ());
88- heap_size_ = indices.size ();
89- heap_positions_.resize (universe_size, kNonExistent );
90- for (HeapIndex i = 0 ; i < data_.size (); ++i) {
91- heap_positions_[indices_[i]] = i;
112+ heap_size_ = HeapIndex (indices.size ());
113+ heap_positions_.resize (GetHeapIndexAsArrayIndex (universe_size),
114+ kNonExistent );
115+ for (HeapIndex i{0 }; i < HeapIndex (data_.size ()); ++i) {
116+ heap_positions_[GetIndexAsArrayIndex (
117+ indices_[GetHeapIndexAsArrayIndex (i)])] = i;
92118 }
93119 BuildHeap ();
94120 }
@@ -98,7 +124,7 @@ class AdjustableKAryHeap {
98124 // This will CHECK-fail if the heap is empty (through Top()).
99125 void Pop () {
100126 CHECK (!IsEmpty ());
101- CHECK (RemoveAtHeapPosition (0 ));
127+ CHECK (RemoveAtHeapPosition (HeapIndex ( 0 ) ));
102128 }
103129
104130 // Returns the index of the top element, without modifying the heap.
@@ -109,31 +135,47 @@ class AdjustableKAryHeap {
109135 return data_[0 ].second ;
110136 }
111137
112- // Returns the index of the top element, without modifying the heap.
138+ // Returns the index of the bottom element, without modifying the heap.
139+ // This operation does not remove the element from the heap.
140+ Index BottomIndex () const {
141+ CHECK (!IsEmpty ());
142+ return data_[GetHeapIndexAsArrayIndex (GetLowestPriorityChild (HeapIndex (0 )))]
143+ .second ;
144+ }
145+
146+ // Returns the priority of the top element, without modifying the heap.
113147 // Note that this does not remove the element from the heap, Pop() must be
114148 // called explicitly.
115-
116149 Priority TopPriority () const {
117150 CHECK (!IsEmpty ());
118151 return data_[0 ].first ;
119152 }
120153
154+ // Returns the priority of the bottom element, without modifying the heap.
155+ // Note that this does not remove the element from the heap.
156+ Priority BottomPriority () const {
157+ CHECK (!IsEmpty ());
158+ return data_[GetHeapIndexAsArrayIndex (GetLowestPriorityChild (HeapIndex (0 )))]
159+ .first ;
160+ }
161+
121162 // Returns the number of elements in the heap.
122163 HeapIndex heap_size () const { return heap_size_; }
123164
124165 // True iff the heap is empty.
125- bool IsEmpty () const { return heap_size () == 0 ; }
166+ bool IsEmpty () const { return heap_size () == HeapIndex ( 0 ) ; }
126167
127168 // Insert an element into the heap.
128169 void Insert (Aggregate element) {
129170 const Index index = element.second ;
130- if (index >= heap_positions_.size ()) {
131- heap_positions_.resize (index + 1 , kNonExistent );
171+ if (index >= Index ( heap_positions_.size () )) {
172+ heap_positions_.resize (GetIndexAsArrayIndex ( index) + 1 , kNonExistent );
132173 }
133174 if (GetHeapPosition (index) == kNonExistent ) {
134- heap_positions_[index] = heap_size_;
135- if (heap_size_ < data_.size ()) {
136- data_[heap_size_] = element;
175+ heap_positions_[GetIndexAsArrayIndex (index)] = heap_size_;
176+
177+ if (heap_size_ < HeapIndex (data_.size ())) {
178+ data_[GetHeapIndexAsArrayIndex (heap_size_)] = element;
137179 } else {
138180 data_.push_back (element);
139181 }
@@ -155,9 +197,9 @@ class AdjustableKAryHeap {
155197 void Update (Aggregate element) {
156198 DCHECK (!IsEmpty ());
157199 const HeapIndex heap_position = GetHeapPosition (element.second );
158- DCHECK_GE (heap_position, 0 );
159- DCHECK_LT (heap_position, heap_positions_.size ());
160- data_[heap_position] = element;
200+ DCHECK_GE (heap_position, HeapIndex ( 0 ) );
201+ DCHECK_LT (heap_position, HeapIndex ( heap_positions_.size () ));
202+ data_[GetHeapIndexAsArrayIndex ( heap_position) ] = element;
161203 if (HasPriority (heap_position, Parent (heap_position))) {
162204 SiftUp (heap_position);
163205 } else {
@@ -172,53 +214,55 @@ class AdjustableKAryHeap {
172214
173215 // Checks that the heap is well-formed.
174216 bool CheckHeapProperty () const {
175- for (HeapIndex i = heap_size () - 1 ; i >= Arity; --i) {
217+ for (HeapIndex i = heap_size () - HeapIndex (1 );
218+ GetHeapIndexAsArrayIndex (i) >= Arity; --i) {
176219 CHECK (HasPriority (Parent (i), i))
177220 << " Parent " << Parent (i) << " with priority " << priority (Parent (i))
178221 << " does not have priority over " << i << " with priority "
179222 << priority (i) << " , heap_size = " << heap_size ()
180223 << " , priority difference = " << priority (i) - priority (Parent (i));
181224 }
182- CHECK_LE (heap_size (), heap_positions_.size ());
183- CHECK_LE (heap_size (), data_.size ());
225+ CHECK_LE (heap_size (), HeapIndex ( heap_positions_.size () ));
226+ CHECK_LE (heap_size (), HeapIndex ( data_.size () ));
184227 return true ;
185228 }
186229
187230 private:
188231 // Gets the current position of element with index i in the heap.
189232 HeapIndex GetHeapPosition (Index i) const {
190- DCHECK_GE (i, 0 );
191- DCHECK_LT (i, heap_positions_.size ());
192- return heap_positions_[i ];
233+ DCHECK_GE (i, Index ( 0 ) );
234+ DCHECK_LT (i, Index ( heap_positions_.size () ));
235+ return heap_positions_[GetIndexAsArrayIndex (i) ];
193236 }
194237
195238 // Removes an element at a given heap position.
196239 bool RemoveAtHeapPosition (HeapIndex heap_index) {
197240 DCHECK (!IsEmpty ());
198- DCHECK_GE (heap_index, 0 );
241+ DCHECK_GE (GetHeapIndexAsArrayIndex (heap_index), 0 );
242+
199243 if (heap_index >= heap_size ()) return false ;
200- PerformSwap (heap_index, heap_size () - 1 );
244+ PerformSwap (heap_index, heap_size () - HeapIndex ( 1 ) );
201245 --heap_size_;
202246 if (HasPriority (heap_index, Parent (heap_index))) {
203247 SiftUp (heap_index);
204248 } else {
205249 SiftDown (heap_index);
206250 }
207- heap_positions_[index (heap_size_)] = kNonExistent ;
251+ heap_positions_[GetIndexAsArrayIndex ( index (heap_size_) )] = kNonExistent ;
208252 return true ;
209253 }
210254
211255 // Maintains heap property by sifting down starting from the end,
212256 void BuildHeap () {
213- for (HeapIndex i = Parent (heap_size ()); i >= 0 ; --i) {
257+ for (HeapIndex i = Parent (heap_size ()); i >= HeapIndex ( 0 ) ; --i) {
214258 SiftDown (i);
215259 }
216260 DCHECK (CheckHeapProperty ());
217261 }
218262
219263 // Maintains heap property by sifting up an element.
220264 void SiftUp (HeapIndex index) {
221- while (index > 0 && HasPriority (index, Parent (index))) {
265+ while (index > HeapIndex ( 0 ) && HasPriority (index, Parent (index))) {
222266 PerformSwap (index, Parent (index));
223267 index = Parent (index);
224268 }
@@ -238,7 +282,8 @@ class AdjustableKAryHeap {
238282 // smallest (resp. largest) key for a min- (resp. max-) heap.
239283 // Returns index if there are no such children.
240284 HeapIndex GetHighestPriorityChild (HeapIndex index) const {
241- const HeapIndex right_bound = std::min (RightChild (index) + 1 , heap_size ());
285+ const HeapIndex right_bound =
286+ std::min (RightChild (index) + HeapIndex (1 ), heap_size ());
242287 HeapIndex highest_priority_child = index;
243288 for (HeapIndex i = LeftChild (index); i < right_bound; ++i) {
244289 if (HasPriority (i, highest_priority_child)) {
@@ -248,18 +293,37 @@ class AdjustableKAryHeap {
248293 return highest_priority_child;
249294 }
250295
296+ // Finds the child with the lowest priority, i.e. the child with the
297+ // largest (resp. smallest) key for a min- (resp. max-) heap.
298+ // Returns index if there are no such children.
299+ HeapIndex GetLowestPriorityChild (HeapIndex index) const {
300+ const HeapIndex right_bound =
301+ std::min (RightChild (index) + HeapIndex (1 ), heap_size ());
302+ HeapIndex highest_priority_child = index;
303+ for (HeapIndex i = LeftChild (index); i < right_bound; ++i) {
304+ if (!HasPriority (i, highest_priority_child)) {
305+ highest_priority_child = i;
306+ }
307+ }
308+ return highest_priority_child;
309+ }
310+
251311 // Swaps two elements of data_, while also making sure heap_positions_ is
252312 // properly maintained.
253313 void PerformSwap (HeapIndex i, HeapIndex j) {
254- std::swap (data_[i], data_[j]);
255- std::swap (heap_positions_[index (i)], heap_positions_[index (j)]);
314+ std::swap (data_[GetIndexAsArrayIndex (i)], data_[GetIndexAsArrayIndex (j)]);
315+ std::swap (heap_positions_[GetIndexAsArrayIndex (index (i))],
316+ heap_positions_[GetIndexAsArrayIndex (index (j))]);
256317 }
257318
258319 // Compares two elements based on whether we are dealing with a min- or a
259320 // max-heap. Returns true if (data indexed by) i has more priority
260321 // than j. Note that we only use operator::<.
261322 bool HasPriority (HeapIndex i, HeapIndex j) const {
262- return IsMaxHeap ? data_[j] < data_[i] : data_[i] < data_[j];
323+ return IsMaxHeap
324+ ? data_[GetIndexAsArrayIndex (j)] < data_[GetIndexAsArrayIndex (i)]
325+ : data_[GetIndexAsArrayIndex (i)] <
326+ data_[GetIndexAsArrayIndex (j)];
263327 }
264328
265329 // Since Arity is a (small) constant, we expect compilers to avoid
@@ -268,23 +332,51 @@ class AdjustableKAryHeap {
268332 // Powers of 2 are guaranteed to be quick thanks to simple shifts.
269333
270334 // Gets the leftmost child index of a given node
271- HeapIndex LeftChild (HeapIndex index) const { return Arity * index + 1 ; }
335+ HeapIndex LeftChild (HeapIndex index) const {
336+ return Arity * index + HeapIndex (1 );
337+ }
272338
273339 // Gets the rightmost child index of a given node
274- HeapIndex RightChild (HeapIndex index) const { return Arity * (index + 1 ); }
340+ HeapIndex RightChild (HeapIndex index) const {
341+ return Arity * (index + HeapIndex (1 ));
342+ }
275343
276344 // For division, the optimization is more uncertain, although a simple
277345 // multiplication and a shift might be used by the compiler.
278346 // Of course, powers of 2 are guaranteed to be quick thanks to simple shifts.
279347
280348 // Gets the parent index of a given index.
281- HeapIndex Parent (HeapIndex index) const { return (index - 1 ) / Arity; }
349+ HeapIndex Parent (HeapIndex index) const {
350+ return (index - HeapIndex (1 )) / Arity;
351+ }
282352
283353 // Returns the index of the element at position i in the heap.
284- Index index (HeapIndex i) const { return data_[i].second ; }
354+ Index index (HeapIndex i) const {
355+ return data_[GetIndexAsArrayIndex (i)].second ;
356+ }
285357
286358 // Returns the index of the element at position i in the heap.
287- Priority priority (HeapIndex i) const { return data_[i].first ; }
359+ Priority priority (HeapIndex i) const {
360+ return data_[GetHeapIndexAsArrayIndex (i)].first ;
361+ }
362+
363+ // Returns the index as an array index.
364+ internal::ArrayIndexType<Index> GetIndexAsArrayIndex (Index i) const {
365+ if constexpr (util_intops::IsStrongInt<Index>::value) {
366+ return i.value ();
367+ } else {
368+ return i;
369+ }
370+ }
371+ // Returns the heap index as an array index.
372+ internal::ArrayIndexType<HeapIndex> GetHeapIndexAsArrayIndex (
373+ HeapIndex i) const {
374+ if constexpr (util_intops::IsStrongInt<HeapIndex>::value) {
375+ return i.value ();
376+ } else {
377+ return i;
378+ }
379+ }
288380
289381 // The heap is stored as a vector.
290382 std::vector<Aggregate> data_;
@@ -303,10 +395,10 @@ class AdjustableKAryHeap {
303395 // The number of elements currently in the heap. This may be updated
304396 // either when removing an element (which is not removed from data_), or
305397 // adding a new one.
306- HeapIndex heap_size_ = 0 ;
398+ HeapIndex heap_size_ = HeapIndex( 0 ) ;
307399
308400 // The index for Aggregates not in the heap.
309- const Index kNonExistent = - 1 ;
401+ const Index kNonExistent = Index(- 1 ) ;
310402};
311403
312404#endif // ORTOOLS_ALGORITHMS_ADJUSTABLE_K_ARY_HEAP_H_
0 commit comments