1181254a7Smrg /** 2*b1e83836Smrg This module provides a `BinaryHeap` (aka priority queue) 3181254a7Smrg adaptor that makes a binary heap out of any user-provided random-access range. 4181254a7Smrg 5181254a7Smrg This module is a submodule of $(MREF std, container). 6181254a7Smrg 7*b1e83836Smrg Source: $(PHOBOSSRC std/container/binaryheap.d) 8181254a7Smrg 9181254a7Smrg Copyright: 2010- Andrei Alexandrescu. All rights reserved by the respective holders. 10181254a7Smrg 11181254a7Smrg License: Distributed under the Boost Software License, Version 1.0. 12181254a7Smrg (See accompanying file LICENSE_1_0.txt or copy at $(HTTP 13181254a7Smrg boost.org/LICENSE_1_0.txt)). 14181254a7Smrg 15181254a7Smrg Authors: $(HTTP erdani.com, Andrei Alexandrescu) 16181254a7Smrg */ 17181254a7Smrg module std.container.binaryheap; 18181254a7Smrg 19181254a7Smrg import std.range.primitives; 20181254a7Smrg import std.traits; 21181254a7Smrg 22181254a7Smrg public import std.container.util; 23181254a7Smrg 24181254a7Smrg /// 25181254a7Smrg @system unittest 26181254a7Smrg { 27181254a7Smrg import std.algorithm.comparison : equal; 28181254a7Smrg import std.range : take; 29181254a7Smrg auto maxHeap = heapify([4, 7, 3, 1, 5]); 30181254a7Smrg assert(maxHeap.take(3).equal([7, 5, 4])); 31181254a7Smrg 32181254a7Smrg auto minHeap = heapify!"a > b"([4, 7, 3, 1, 5]); 33181254a7Smrg assert(minHeap.take(3).equal([1, 3, 4])); 34181254a7Smrg } 35181254a7Smrg 36181254a7Smrg // BinaryHeap 37181254a7Smrg /** 38181254a7Smrg Implements a $(HTTP en.wikipedia.org/wiki/Binary_heap, binary heap) 39181254a7Smrg container on top of a given random-access range type (usually $(D 40*b1e83836Smrg T[])) or a random-access container type (usually `Array!T`). The 41*b1e83836Smrg documentation of `BinaryHeap` will refer to the underlying range or 42181254a7Smrg container as the $(I store) of the heap. 43181254a7Smrg 44181254a7Smrg The binary heap induces structure over the underlying store such that 45*b1e83836Smrg accessing the largest element (by using the `front` property) is a 46181254a7Smrg $(BIGOH 1) operation and extracting it (by using the $(D 47181254a7Smrg removeFront()) method) is done fast in $(BIGOH log n) time. 48181254a7Smrg 49*b1e83836Smrg If `less` is the less-than operator, which is the default option, 50*b1e83836Smrg then `BinaryHeap` defines a so-called max-heap that optimizes 51181254a7Smrg extraction of the $(I largest) elements. To define a min-heap, 52181254a7Smrg instantiate BinaryHeap with $(D "a > b") as its predicate. 53181254a7Smrg 54*b1e83836Smrg Simply extracting elements from a `BinaryHeap` container is 55*b1e83836Smrg tantamount to lazily fetching elements of `Store` in descending 56*b1e83836Smrg order. Extracting elements from the `BinaryHeap` to completion 57181254a7Smrg leaves the underlying store sorted in ascending order but, again, 58181254a7Smrg yields elements in descending order. 59181254a7Smrg 60*b1e83836Smrg If `Store` is a range, the `BinaryHeap` cannot grow beyond the 61*b1e83836Smrg size of that range. If `Store` is a container that supports $(D 62*b1e83836Smrg insertBack), the `BinaryHeap` may grow by adding elements to the 63181254a7Smrg container. 64181254a7Smrg */ 65181254a7Smrg struct BinaryHeap(Store, alias less = "a < b") 66181254a7Smrg if (isRandomAccessRange!(Store) || isRandomAccessRange!(typeof(Store.init[]))) 67181254a7Smrg { 68181254a7Smrg import std.algorithm.comparison : min; 69181254a7Smrg import std.algorithm.mutation : move, swapAt; 70181254a7Smrg import std.algorithm.sorting : HeapOps; 71181254a7Smrg import std.exception : enforce; 72181254a7Smrg import std.functional : binaryFun; 73181254a7Smrg import std.typecons : RefCounted, RefCountedAutoInitialize; 74181254a7Smrg 75181254a7Smrg static if (isRandomAccessRange!Store) 76181254a7Smrg alias Range = Store; 77181254a7Smrg else 78181254a7Smrg alias Range = typeof(Store.init[]); 79181254a7Smrg alias percolate = HeapOps!(less, Range).percolate; 80181254a7Smrg alias buildHeap = HeapOps!(less, Range).buildHeap; 81181254a7Smrg 82181254a7Smrg // Really weird @@BUG@@: if you comment out the "private:" label below, 83181254a7Smrg // std.algorithm can't unittest anymore 84181254a7Smrg //private: 85181254a7Smrg 86181254a7Smrg // The payload includes the support store and the effective length 87181254a7Smrg private static struct Data 88181254a7Smrg { 89181254a7Smrg Store _store; 90181254a7Smrg size_t _length; 91181254a7Smrg } 92181254a7Smrg private RefCounted!(Data, RefCountedAutoInitialize.no) _payload; 93181254a7Smrg // Comparison predicate 94181254a7Smrg private alias comp = binaryFun!(less); 95181254a7Smrg // Convenience accessors _storeBinaryHeap96181254a7Smrg private @property ref Store _store() 97181254a7Smrg { 98*b1e83836Smrg assert(_payload.refCountedStore.isInitialized, 99*b1e83836Smrg "BinaryHeap not initialized"); 100181254a7Smrg return _payload._store; 101181254a7Smrg } _lengthBinaryHeap102181254a7Smrg private @property ref size_t _length() 103181254a7Smrg { 104*b1e83836Smrg assert(_payload.refCountedStore.isInitialized, 105*b1e83836Smrg "BinaryHeap not initialized"); 106181254a7Smrg return _payload._length; 107181254a7Smrg } 108181254a7Smrg 109181254a7Smrg // Asserts that the heap property is respected. assertValidBinaryHeap110181254a7Smrg private void assertValid() 111181254a7Smrg { 112181254a7Smrg debug 113181254a7Smrg { 114181254a7Smrg import std.conv : to; 115181254a7Smrg if (!_payload.refCountedStore.isInitialized) return; 116181254a7Smrg if (_length < 2) return; 117181254a7Smrg for (size_t n = _length - 1; n >= 1; --n) 118181254a7Smrg { 119181254a7Smrg auto parentIdx = (n - 1) / 2; 120181254a7Smrg assert(!comp(_store[parentIdx], _store[n]), to!string(n)); 121181254a7Smrg } 122181254a7Smrg } 123181254a7Smrg } 124181254a7Smrg 125181254a7Smrg // @@@BUG@@@: add private here, std.algorithm doesn't unittest anymore popBinaryHeap126181254a7Smrg /*private*/ void pop(Store store) 127181254a7Smrg { 128181254a7Smrg assert(!store.empty, "Cannot pop an empty store."); 129181254a7Smrg if (store.length == 1) return; 130181254a7Smrg auto t1 = store[].moveFront(); 131181254a7Smrg auto t2 = store[].moveBack(); 132181254a7Smrg store.front = move(t2); 133181254a7Smrg store.back = move(t1); 134181254a7Smrg percolate(store[], 0, store.length - 1); 135181254a7Smrg } 136181254a7Smrg 137181254a7Smrg public: 138181254a7Smrg 139181254a7Smrg /** 140*b1e83836Smrg Converts the store `s` into a heap. If `initialSize` is 141*b1e83836Smrg specified, only the first `initialSize` elements in `s` 142181254a7Smrg are transformed into a heap, after which the heap can grow up 143*b1e83836Smrg to `r.length` (if `Store` is a range) or indefinitely (if 144*b1e83836Smrg `Store` is a container with `insertBack`). Performs 145*b1e83836Smrg $(BIGOH min(r.length, initialSize)) evaluations of `less`. 146181254a7Smrg */ 147181254a7Smrg this(Store s, size_t initialSize = size_t.max) 148181254a7Smrg { 149181254a7Smrg acquire(s, initialSize); 150181254a7Smrg } 151181254a7Smrg 152181254a7Smrg /** 153*b1e83836Smrg Takes ownership of a store. After this, manipulating `s` may make 154181254a7Smrg the heap work incorrectly. 155181254a7Smrg */ 156181254a7Smrg void acquire(Store s, size_t initialSize = size_t.max) 157181254a7Smrg { 158181254a7Smrg _payload.refCountedStore.ensureInitialized(); 159181254a7Smrg _store = move(s); 160181254a7Smrg _length = min(_store.length, initialSize); 161181254a7Smrg if (_length < 2) return; 162181254a7Smrg buildHeap(_store[]); 163181254a7Smrg assertValid(); 164181254a7Smrg } 165181254a7Smrg 166181254a7Smrg /** 167181254a7Smrg Takes ownership of a store assuming it already was organized as a 168181254a7Smrg heap. 169181254a7Smrg */ 170181254a7Smrg void assume(Store s, size_t initialSize = size_t.max) 171181254a7Smrg { 172181254a7Smrg _payload.refCountedStore.ensureInitialized(); 173181254a7Smrg _store = s; 174181254a7Smrg _length = min(_store.length, initialSize); 175181254a7Smrg assertValid(); 176181254a7Smrg } 177181254a7Smrg 178181254a7Smrg /** 179*b1e83836Smrg Clears the heap. Returns the portion of the store from `0` up to 180*b1e83836Smrg `length`, which satisfies the $(LINK2 https://en.wikipedia.org/wiki/Heap_(data_structure), 181181254a7Smrg heap property). 182181254a7Smrg */ releaseBinaryHeap183181254a7Smrg auto release() 184181254a7Smrg { 185181254a7Smrg if (!_payload.refCountedStore.isInitialized) 186181254a7Smrg { 187181254a7Smrg return typeof(_store[0 .. _length]).init; 188181254a7Smrg } 189181254a7Smrg assertValid(); 190181254a7Smrg auto result = _store[0 .. _length]; 191181254a7Smrg _payload = _payload.init; 192181254a7Smrg return result; 193181254a7Smrg } 194181254a7Smrg 195181254a7Smrg /** 196*b1e83836Smrg Returns `true` if the heap is _empty, `false` otherwise. 197181254a7Smrg */ emptyBinaryHeap198181254a7Smrg @property bool empty() 199181254a7Smrg { 200181254a7Smrg return !length; 201181254a7Smrg } 202181254a7Smrg 203181254a7Smrg /** 204*b1e83836Smrg Returns a duplicate of the heap. The `dup` method is available only if the 205181254a7Smrg underlying store supports it. 206181254a7Smrg */ 207181254a7Smrg static if (is(typeof((Store s) { return s.dup; }(Store.init)) == Store)) 208181254a7Smrg { dupBinaryHeap209181254a7Smrg @property BinaryHeap dup() 210181254a7Smrg { 211181254a7Smrg BinaryHeap result; 212181254a7Smrg if (!_payload.refCountedStore.isInitialized) return result; 213181254a7Smrg result.assume(_store.dup, length); 214181254a7Smrg return result; 215181254a7Smrg } 216181254a7Smrg } 217181254a7Smrg 218181254a7Smrg /** 219181254a7Smrg Returns the _length of the heap. 220181254a7Smrg */ lengthBinaryHeap221181254a7Smrg @property size_t length() 222181254a7Smrg { 223181254a7Smrg return _payload.refCountedStore.isInitialized ? _length : 0; 224181254a7Smrg } 225181254a7Smrg 226181254a7Smrg /** 227181254a7Smrg Returns the _capacity of the heap, which is the length of the 228181254a7Smrg underlying store (if the store is a range) or the _capacity of the 229181254a7Smrg underlying store (if the store is a container). 230181254a7Smrg */ capacityBinaryHeap231181254a7Smrg @property size_t capacity() 232181254a7Smrg { 233181254a7Smrg if (!_payload.refCountedStore.isInitialized) return 0; 234181254a7Smrg static if (is(typeof(_store.capacity) : size_t)) 235181254a7Smrg { 236181254a7Smrg return _store.capacity; 237181254a7Smrg } 238181254a7Smrg else 239181254a7Smrg { 240181254a7Smrg return _store.length; 241181254a7Smrg } 242181254a7Smrg } 243181254a7Smrg 244181254a7Smrg /** 245181254a7Smrg Returns a copy of the _front of the heap, which is the largest element 246*b1e83836Smrg according to `less`. 247181254a7Smrg */ 248181254a7Smrg @property ElementType!Store front() 249181254a7Smrg { 250181254a7Smrg enforce(!empty, "Cannot call front on an empty heap."); 251181254a7Smrg return _store.front; 252181254a7Smrg } 253181254a7Smrg 254181254a7Smrg /** 255181254a7Smrg Clears the heap by detaching it from the underlying store. 256181254a7Smrg */ clearBinaryHeap257181254a7Smrg void clear() 258181254a7Smrg { 259181254a7Smrg _payload = _payload.init; 260181254a7Smrg } 261181254a7Smrg 262181254a7Smrg /** 263*b1e83836Smrg Inserts `value` into the store. If the underlying store is a range 264181254a7Smrg and $(D length == capacity), throws an exception. 265181254a7Smrg */ 266181254a7Smrg size_t insert(ElementType!Store value) 267181254a7Smrg { 268181254a7Smrg static if (is(typeof(_store.insertBack(value)))) 269181254a7Smrg { 270181254a7Smrg _payload.refCountedStore.ensureInitialized(); 271181254a7Smrg if (length == _store.length) 272181254a7Smrg { 273181254a7Smrg // reallocate 274181254a7Smrg _store.insertBack(value); 275181254a7Smrg } 276181254a7Smrg else 277181254a7Smrg { 278181254a7Smrg // no reallocation 279181254a7Smrg _store[_length] = value; 280181254a7Smrg } 281181254a7Smrg } 282181254a7Smrg else 283181254a7Smrg { 284181254a7Smrg import std.traits : isDynamicArray; 285181254a7Smrg static if (isDynamicArray!Store) 286181254a7Smrg { 287181254a7Smrg if (length == _store.length) 288181254a7Smrg _store.length = (length < 6 ? 8 : length * 3 / 2); 289181254a7Smrg _store[_length] = value; 290181254a7Smrg } 291181254a7Smrg else 292181254a7Smrg { 293181254a7Smrg // can't grow 294181254a7Smrg enforce(length < _store.length, 295181254a7Smrg "Cannot grow a heap created over a range"); 296181254a7Smrg } 297181254a7Smrg } 298181254a7Smrg 299181254a7Smrg // sink down the element 300181254a7Smrg for (size_t n = _length; n; ) 301181254a7Smrg { 302181254a7Smrg auto parentIdx = (n - 1) / 2; 303181254a7Smrg if (!comp(_store[parentIdx], _store[n])) break; // done! 304181254a7Smrg // must swap and continue 305181254a7Smrg _store.swapAt(parentIdx, n); 306181254a7Smrg n = parentIdx; 307181254a7Smrg } 308181254a7Smrg ++_length; 309181254a7Smrg debug(BinaryHeap) assertValid(); 310181254a7Smrg return 1; 311181254a7Smrg } 312181254a7Smrg 313181254a7Smrg /** 314181254a7Smrg Removes the largest element from the heap. 315181254a7Smrg */ removeFrontBinaryHeap316181254a7Smrg void removeFront() 317181254a7Smrg { 318181254a7Smrg enforce(!empty, "Cannot call removeFront on an empty heap."); 319181254a7Smrg if (_length > 1) 320181254a7Smrg { 321181254a7Smrg auto t1 = _store[].moveFront(); 322181254a7Smrg auto t2 = _store[].moveAt(_length - 1); 323181254a7Smrg _store.front = move(t2); 324181254a7Smrg _store[_length - 1] = move(t1); 325181254a7Smrg } 326181254a7Smrg --_length; 327181254a7Smrg percolate(_store[], 0, _length); 328181254a7Smrg } 329181254a7Smrg 330181254a7Smrg /// ditto 331181254a7Smrg alias popFront = removeFront; 332181254a7Smrg 333181254a7Smrg /** 334181254a7Smrg Removes the largest element from the heap and returns a copy of 335181254a7Smrg it. The element still resides in the heap's store. For performance 336*b1e83836Smrg reasons you may want to use `removeFront` with heaps of objects 337181254a7Smrg that are expensive to copy. 338181254a7Smrg */ 339181254a7Smrg ElementType!Store removeAny() 340181254a7Smrg { 341181254a7Smrg removeFront(); 342181254a7Smrg return _store[_length]; 343181254a7Smrg } 344181254a7Smrg 345181254a7Smrg /** 346*b1e83836Smrg Replaces the largest element in the store with `value`. 347181254a7Smrg */ 348181254a7Smrg void replaceFront(ElementType!Store value) 349181254a7Smrg { 350181254a7Smrg // must replace the top 351181254a7Smrg assert(!empty, "Cannot call replaceFront on an empty heap."); 352181254a7Smrg _store.front = value; 353181254a7Smrg percolate(_store[], 0, _length); 354181254a7Smrg debug(BinaryHeap) assertValid(); 355181254a7Smrg } 356181254a7Smrg 357181254a7Smrg /** 358*b1e83836Smrg If the heap has room to grow, inserts `value` into the store and 359*b1e83836Smrg returns `true`. Otherwise, if $(D less(value, front)), calls $(D 360*b1e83836Smrg replaceFront(value)) and returns again `true`. Otherwise, leaves 361*b1e83836Smrg the heap unaffected and returns `false`. This method is useful in 362*b1e83836Smrg scenarios where the smallest `k` elements of a set of candidates 363181254a7Smrg must be collected. 364181254a7Smrg */ 365181254a7Smrg bool conditionalInsert(ElementType!Store value) 366181254a7Smrg { 367181254a7Smrg _payload.refCountedStore.ensureInitialized(); 368181254a7Smrg if (_length < _store.length) 369181254a7Smrg { 370181254a7Smrg insert(value); 371181254a7Smrg return true; 372181254a7Smrg } 373181254a7Smrg 374181254a7Smrg assert(!_store.empty, "Cannot replace front of an empty heap."); 375181254a7Smrg if (!comp(value, _store.front)) return false; // value >= largest 376181254a7Smrg _store.front = value; 377181254a7Smrg 378181254a7Smrg percolate(_store[], 0, _length); 379181254a7Smrg debug(BinaryHeap) assertValid(); 380181254a7Smrg return true; 381181254a7Smrg } 382181254a7Smrg 383181254a7Smrg /** 384181254a7Smrg Swapping is allowed if the heap is full. If $(D less(value, front)), the 385*b1e83836Smrg method exchanges store.front and value and returns `true`. Otherwise, it 386*b1e83836Smrg leaves the heap unaffected and returns `false`. 387181254a7Smrg */ 388181254a7Smrg bool conditionalSwap(ref ElementType!Store value) 389181254a7Smrg { 390181254a7Smrg _payload.refCountedStore.ensureInitialized(); 391*b1e83836Smrg assert(_length == _store.length, 392*b1e83836Smrg "length and number of stored items out of sync"); 393181254a7Smrg assert(!_store.empty, "Cannot swap front of an empty heap."); 394181254a7Smrg 395181254a7Smrg if (!comp(value, _store.front)) return false; // value >= largest 396181254a7Smrg 397181254a7Smrg import std.algorithm.mutation : swap; 398181254a7Smrg swap(_store.front, value); 399181254a7Smrg 400181254a7Smrg percolate(_store[], 0, _length); 401181254a7Smrg debug(BinaryHeap) assertValid(); 402181254a7Smrg 403181254a7Smrg return true; 404181254a7Smrg } 405181254a7Smrg } 406181254a7Smrg 407181254a7Smrg /// Example from "Introduction to Algorithms" Cormen et al, p 146 408181254a7Smrg @system unittest 409181254a7Smrg { 410181254a7Smrg import std.algorithm.comparison : equal; 411181254a7Smrg int[] a = [ 4, 1, 3, 2, 16, 9, 10, 14, 8, 7 ]; 412181254a7Smrg auto h = heapify(a); 413181254a7Smrg // largest element 414181254a7Smrg assert(h.front == 16); 415181254a7Smrg // a has the heap property 416181254a7Smrg assert(equal(a, [ 16, 14, 10, 8, 7, 9, 3, 2, 4, 1 ])); 417181254a7Smrg } 418181254a7Smrg 419*b1e83836Smrg /// `BinaryHeap` implements the standard input range interface, allowing 420181254a7Smrg /// lazy iteration of the underlying range in descending order. 421181254a7Smrg @system unittest 422181254a7Smrg { 423181254a7Smrg import std.algorithm.comparison : equal; 424181254a7Smrg import std.range : take; 425181254a7Smrg int[] a = [4, 1, 3, 2, 16, 9, 10, 14, 8, 7]; 426181254a7Smrg auto top5 = heapify(a).take(5); 427181254a7Smrg assert(top5.equal([16, 14, 10, 9, 8])); 428181254a7Smrg } 429181254a7Smrg 430181254a7Smrg /** 431*b1e83836Smrg Convenience function that returns a `BinaryHeap!Store` object 432*b1e83836Smrg initialized with `s` and `initialSize`. 433181254a7Smrg */ 434181254a7Smrg BinaryHeap!(Store, less) heapify(alias less = "a < b", Store)(Store s, 435181254a7Smrg size_t initialSize = size_t.max) 436181254a7Smrg { 437181254a7Smrg 438181254a7Smrg return BinaryHeap!(Store, less)(s, initialSize); 439181254a7Smrg } 440181254a7Smrg 441181254a7Smrg /// 442181254a7Smrg @system unittest 443181254a7Smrg { 444181254a7Smrg import std.conv : to; 445181254a7Smrg import std.range.primitives; 446181254a7Smrg { 447181254a7Smrg // example from "Introduction to Algorithms" Cormen et al., p 146 448181254a7Smrg int[] a = [ 4, 1, 3, 2, 16, 9, 10, 14, 8, 7 ]; 449181254a7Smrg auto h = heapify(a); 450181254a7Smrg h = heapify!"a < b"(a); 451181254a7Smrg assert(h.front == 16); 452181254a7Smrg assert(a == [ 16, 14, 10, 8, 7, 9, 3, 2, 4, 1 ]); 453181254a7Smrg auto witness = [ 16, 14, 10, 9, 8, 7, 4, 3, 2, 1 ]; 454181254a7Smrg for (; !h.empty; h.removeFront(), witness.popFront()) 455181254a7Smrg { 456181254a7Smrg assert(!witness.empty); 457181254a7Smrg assert(witness.front == h.front); 458181254a7Smrg } 459181254a7Smrg assert(witness.empty); 460181254a7Smrg } 461181254a7Smrg { 462181254a7Smrg int[] a = [ 4, 1, 3, 2, 16, 9, 10, 14, 8, 7 ]; 463181254a7Smrg int[] b = new int[a.length]; 464181254a7Smrg BinaryHeap!(int[]) h = BinaryHeap!(int[])(b, 0); foreach(e;a)465181254a7Smrg foreach (e; a) 466181254a7Smrg { 467181254a7Smrg h.insert(e); 468181254a7Smrg } 469181254a7Smrg assert(b == [ 16, 14, 10, 8, 7, 3, 9, 1, 4, 2 ], to!string(b)); 470181254a7Smrg } 471181254a7Smrg } 472181254a7Smrg 473181254a7Smrg @system unittest 474181254a7Smrg { 475181254a7Smrg // Test range interface. 476181254a7Smrg import std.algorithm.comparison : equal; 477181254a7Smrg int[] a = [4, 1, 3, 2, 16, 9, 10, 14, 8, 7]; 478181254a7Smrg auto h = heapify(a); 479181254a7Smrg static assert(isInputRange!(typeof(h))); 480181254a7Smrg assert(h.equal([16, 14, 10, 9, 8, 7, 4, 3, 2, 1])); 481181254a7Smrg } 482181254a7Smrg 483*b1e83836Smrg // https://issues.dlang.org/show_bug.cgi?id=15675 484*b1e83836Smrg @system unittest 485181254a7Smrg { 486181254a7Smrg import std.container.array : Array; 487181254a7Smrg 488181254a7Smrg Array!int elements = [1, 2, 10, 12]; 489181254a7Smrg auto heap = heapify(elements); 490181254a7Smrg assert(heap.front == 12); 491181254a7Smrg } 492181254a7Smrg 493*b1e83836Smrg // https://issues.dlang.org/show_bug.cgi?id=16072 494*b1e83836Smrg @system unittest 495181254a7Smrg { 496181254a7Smrg auto q = heapify!"a > b"([2, 4, 5]); 497181254a7Smrg q.insert(1); 498181254a7Smrg q.insert(6); 499181254a7Smrg assert(q.front == 1); 500181254a7Smrg 501181254a7Smrg // test more multiple grows 502181254a7Smrg int[] arr; 503181254a7Smrg auto r = heapify!"a < b"(arr); 504181254a7Smrg foreach (i; 0 .. 100) 505181254a7Smrg r.insert(i); 506181254a7Smrg 507181254a7Smrg assert(r.front == 99); 508181254a7Smrg } 509181254a7Smrg 510181254a7Smrg @system unittest 511181254a7Smrg { 512181254a7Smrg import std.algorithm.comparison : equal; 513181254a7Smrg int[] a = [4, 1, 3, 2, 16, 9, 10, 14, 8, 7]; 514181254a7Smrg auto heap = heapify(a); 515181254a7Smrg auto dup = heap.dup(); 516181254a7Smrg assert(dup.equal([16, 14, 10, 9, 8, 7, 4, 3, 2, 1])); 517181254a7Smrg } 518181254a7Smrg 519181254a7Smrg @safe unittest 520181254a7Smrg { 521181254a7Smrg static struct StructWithoutDup 522181254a7Smrg { 523181254a7Smrg int[] a; 524*b1e83836Smrg @disable StructWithoutDup dup(); 525181254a7Smrg alias a this; 526181254a7Smrg } 527181254a7Smrg 528181254a7Smrg // Assert Binary heap can be created when Store doesn't have dup 529181254a7Smrg // if dup is not used. 530181254a7Smrg assert(__traits(compiles, () 531181254a7Smrg { 532181254a7Smrg auto s = StructWithoutDup([1,2]); 533181254a7Smrg auto h = heapify(s); 534181254a7Smrg })); 535181254a7Smrg 536181254a7Smrg // Assert dup can't be used on BinaryHeaps when Store doesn't have dup 537181254a7Smrg assert(!__traits(compiles, () 538181254a7Smrg { 539181254a7Smrg auto s = StructWithoutDup([1,2]); 540181254a7Smrg auto h = heapify(s); 541181254a7Smrg h.dup(); 542181254a7Smrg })); 543181254a7Smrg } 544181254a7Smrg 545181254a7Smrg @safe unittest 546181254a7Smrg { 547181254a7Smrg static struct StructWithDup 548181254a7Smrg { 549181254a7Smrg int[] a; dupStructWithDup550181254a7Smrg StructWithDup dup() 551181254a7Smrg { 552181254a7Smrg StructWithDup d; 553181254a7Smrg return d; 554181254a7Smrg } 555181254a7Smrg alias a this; 556181254a7Smrg } 557181254a7Smrg 558181254a7Smrg // Assert dup can be used on BinaryHeaps when Store has dup 559181254a7Smrg assert(__traits(compiles, () 560181254a7Smrg { 561181254a7Smrg auto s = StructWithDup([1, 2]); 562181254a7Smrg auto h = heapify(s); 563181254a7Smrg h.dup(); 564181254a7Smrg })); 565181254a7Smrg } 566181254a7Smrg 567181254a7Smrg @system unittest 568181254a7Smrg { 569181254a7Smrg import std.algorithm.comparison : equal; 570181254a7Smrg import std.internal.test.dummyrange; 571181254a7Smrg 572181254a7Smrg alias RefRange = DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Random); 573181254a7Smrg 574181254a7Smrg RefRange a; 575181254a7Smrg RefRange b; 576181254a7Smrg a.reinit(); 577181254a7Smrg b.reinit(); 578181254a7Smrg 579181254a7Smrg auto heap = heapify(a); foreach(ref elem;b)580181254a7Smrg foreach (ref elem; b) 581181254a7Smrg { 582181254a7Smrg heap.conditionalSwap(elem); 583181254a7Smrg } 584181254a7Smrg 585181254a7Smrg assert(equal(heap, [ 5, 5, 4, 4, 3, 3, 2, 2, 1, 1])); 586181254a7Smrg assert(equal(b, [10, 9, 8, 7, 6, 6, 7, 8, 9, 10])); 587181254a7Smrg } 588181254a7Smrg 589*b1e83836Smrg // https://issues.dlang.org/show_bug.cgi?id=17314 590*b1e83836Smrg @system unittest 591181254a7Smrg { 592181254a7Smrg import std.algorithm.comparison : equal; 593181254a7Smrg int[] a = [5]; 594181254a7Smrg auto heap = heapify(a); 595181254a7Smrg heap.insert(6); 596181254a7Smrg assert(equal(heap, [6, 5])); 597181254a7Smrg } 598