1 /**
2 License: public domain
3 Authors: Simon Bürger
4 */
5 module jive.priorityqueue;
6 
7 import jive.internal;
8 import jive.array;
9 import std.algorithm;
10 import std.range;
11 import std.functional;
12 
13 /**
14  * Priority queue which allows fast access to the smallest element.
15  * Implemented as binary heap embedded into a jive.Array.
16  */
17 struct PriorityQueue(V, alias _pred = "a < b")
18 {
19 	// NOTE: attributes (pure/nothrow/...) depend on the predicate and will
20 	//       be inferred by the compiler. No need to state them explicitly
21 
22 	private Array!V arr;
23 
24 	mixin PredicateHelper!(_pred, V);
25 
26 	static if(dynamicPred)
27 	{
28 		@disable this();
29 
30 		this(_pred p)
31 		{
32 			this.pred = p;
33 		}
34 
35 		/** constructor that gets content from arbitrary range */
36 		this(Stuff)(_pred p, Stuff data)
37 			if(isInputRange!Stuff && is(ElementType!Stuff:V))
38 		{
39 			this.pred = p;
40 
41 			static if(hasLength!Stuff)
42 				arr.reserve(data.length);
43 
44 			foreach(ref x; data)
45 				push(x);
46 		}
47 	}
48 	else
49 	{
50 		/** constructor that gets content from arbitrary range */
51 		this(Stuff)(Stuff data)
52 			if(isInputRange!Stuff && is(ElementType!Stuff:V))
53 		{
54 			static if(hasLength!Stuff)
55 				arr.reserve(data.length);
56 
57 			foreach(ref x; data)
58 				push(x);
59 		}
60 	}
61 
62 	/** returns: true if set is empty */
63 	bool empty() const @property
64 	{
65 		return arr.empty;
66 	}
67 
68 	/** returns: number of elements in the set */
69 	size_t length() const @property
70 	{
71 		return arr.length;
72 	}
73 
74 	/** number of elements this structure can hold without further allocations */
75 	size_t capacity() const pure nothrow @property @safe
76 	{
77 		return arr.capacity;
78 	}
79 
80 	/**
81 	 * Allocated heap memory in bytes.
82 	 * This is recursive if V has a `.memUsage` property.
83 	 */
84 	size_t memUsage() const pure nothrow @property @trusted
85 	{
86 		return arr.memUsage;
87 	}
88 
89 	/** Allocate memory for s elements. Does nothing if s < length. */
90 	void reserve(size_t s)
91 	{
92 		arr.reserve(s);
93 	}
94 
95 	/** removes all elements but keeps allocated memory */
96 	void clear()
97 	{
98 		arr.clear();
99 	}
100 
101 	/** clear the heap and return all elements (unordered!) */
102 	Array!V release()
103 	{
104 		return move(arr);
105 	}
106 
107 	/** returns: first element (i.e. the smallest) */
108 	ref inout(V) front() inout @property
109 	{
110 		return arr.front;
111 	}
112 
113 	/**
114 	 * Remove the first element (i.e. the smallest) from the queue.
115 	 * returns: the removed element
116 	 */
117 	V pop(string file = __FILE__, int line = __LINE__)()
118 	{
119 		if(boundsChecks && empty)
120 			assert(false, boundsCheckMsg!(file, line));
121 		swap(arr[0], arr.back);
122 		V r = arr.popBack;
123 		if(!empty)
124 			percolateDown(0);
125 		return r;
126 	}
127 
128 	/** ditto */
129 	alias popFront = pop;
130 
131 	/**
132 	 * Add an element to the queue.
133 	 */
134 	void push(V value)
135 	{
136 		arr.pushBack(move(value));
137 		percolateUp(cast(int)length-1);
138 	}
139 
140 	/** Add multiple elements to the queue. */
141 	void push(Stuff)(Stuff data)
142 		if(!is(Stuff:V) && isInputRange!Stuff && is(ElementType!Stuff:V))
143 	{
144 		static if(hasLength!Stuff)
145 			arr.reserve(length + data.length, true);
146 
147 		foreach(ref x; data)
148 			pushBack(x);
149 	}
150 
151 	alias pushBack = push;
152 
153 	/* internals (private) */
154 
155 	private int parent(int i) { return (i-1)/2; }
156 	private int left(int i) { return 2*i+1; }
157 	private int right(int i) { return 2*i+2; }
158 
159 	private int smallerChild(int i)
160 	{
161 		if(right(i) < length && pred(arr[right(i)], arr[left(i)]))
162 			return right(i);
163 		else
164 			return left(i);
165 	}
166 
167 	private void percolateUp(int i)
168 	{
169 		auto x  = move(arr[i]);
170 
171 		for(int p = parent(i); i != 0 && pred(x, arr[p]); i = p, p = parent(i))
172 			arr[i] = move(arr[p]);
173 
174 		arr[i] = move(x);
175 	}
176 
177 	private void percolateDown(int i)
178 	{
179 		auto x = move(arr[i]);
180 
181 		for(int c = smallerChild(i); c < length && pred(arr[c], x); i = c, c = smallerChild(i))
182 			arr[i] = move(arr[c]);
183 
184 		arr[i] = move(x);
185 	}
186 }
187 
188 /// basic usage
189 @nogc nothrow pure @safe unittest
190 {
191 	// custom predicate turns a min-heap into a max-heap
192 	PriorityQueue!int q;
193 	q.push(7);
194 	q.push(3);
195 	q.push(5);
196 	q.push(3);
197 
198 	assert(q.length == 4); // note that duplicates are kept
199 
200 	assert(q.pop == 3);
201 	assert(q.pop == 3);
202 	assert(q.pop == 5);
203 	assert(q.pop == 7);
204 
205 	assert(q.empty);
206 }
207 
208 /// custom predicate (without state)
209 /*@nogc*/ nothrow pure @safe unittest
210 {
211 	// custom predicate turns a min-heap into a max-heap
212 	PriorityQueue!(int, "a > b") q;
213 	q.push(7);
214 
215 	q.push([9,2,8,3,4,1,6,5,8,0]);
216 
217 	assert(q.pop == 9);
218 	assert(q.pop == 8);
219 	assert(q.pop == 8);
220 	assert(q.pop == 7);
221 	q.push(18);
222 	assert(q.pop == 18);
223 	assert(q.pop == 6);
224 	assert(q.pop == 5);
225 	q.clear;
226 	assert(q.empty);
227 }
228 
229 /// custom predicate (with state)
230 unittest
231 {
232 	// sometimes, you need a custom predicate/comparator that contains state.
233 	// For example this int-comparator puts one selected item first, followed
234 	// by all other integers in their usual order.
235 	struct Cmp
236 	{
237 		int priority;
238 
239 		@disable this();
240 
241 		this(int p)
242 		{
243 			this.priority = p;
244 		}
245 
246 		// this is understood as a comparison 'a < b'.
247 		bool opCall(int a, int b)
248 		{
249 			if(b == priority)
250 				return false;
251 			if(a == priority)
252 				return true;
253 			return a < b;
254 		}
255 	}
256 
257 	// the constructor now takes an instance of the comparator
258 	auto q = PriorityQueue!(int, Cmp)(Cmp(3));
259 
260 	q.push([2,3,4,1,5,0]);
261 	assert(q.pop == 3);
262 	assert(q.pop == 0);
263 	assert(q.pop == 1);
264 	assert(q.pop == 2);
265 	assert(q.pop == 4);
266 	assert(q.pop == 5);
267 }
268 
269 // check move-semantics
270 unittest
271 {
272 	struct S
273 	{
274 		int x = -1;
275 		alias x this;
276 		this(this)
277 		{
278 			// default-constructed objects might be copied, others may not.
279 			assert(x == -1);
280 		}
281 	}
282 
283 	PriorityQueue!S q;
284 	q.push(S(2));
285 	q.push(S(4));
286 	q.push(S(3));
287 	q.push(S(1));
288 	q.push(S(0));
289 	assert(q.pop == S(0));
290 	assert(q.pop == S(1));
291 	assert(q.pop == S(2));
292 }