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 	/** Allocate memory for s elements. Does nothing if s < length. */
75 	void reserve(size_t s)
76 	{
77 		arr.reserve(s);
78 	}
79 
80 	/** removes all elements but keeps allocated memory */
81 	void clear()
82 	{
83 		arr.clear();
84 	}
85 
86 	/** clear the heap and return all elements (unordered!) */
87 	Array!V release()
88 	{
89 		return move(arr);
90 	}
91 
92 	/** returns: first element (i.e. the smallest) */
93 	ref inout(V) front() inout @property
94 	{
95 		return arr.front;
96 	}
97 
98 	/**
99 	 * Remove the first element (i.e. the smallest) from the queue.
100 	 * returns: the removed element
101 	 */
102 	V pop(string file = __FILE__, int line = __LINE__)()
103 	{
104 		if(boundsChecks && empty)
105 			assert(false, boundsCheckMsg!(file, line));
106 		swap(arr[0], arr.back);
107 		V r = arr.popBack;
108 		if(!empty)
109 			percolateDown(0);
110 		return r;
111 	}
112 
113 	/** ditto */
114 	alias popFront = pop;
115 
116 	/**
117 	 * Add an element to the queue.
118 	 */
119 	void push(V value)
120 	{
121 		arr.pushBack(move(value));
122 		percolateUp(cast(int)length-1);
123 	}
124 
125 	/** Add multiple elements to the queue. */
126 	void push(Stuff)(Stuff data)
127 		if(!is(Stuff:V) && isInputRange!Stuff && is(ElementType!Stuff:V))
128 	{
129 		static if(hasLength!Stuff)
130 			arr.reserve(length + data.length, true);
131 
132 		foreach(ref x; data)
133 			pushBack(x);
134 	}
135 
136 	alias pushBack = push;
137 
138 	/* internals (private) */
139 
140 	private int parent(int i) { return (i-1)/2; }
141 	private int left(int i) { return 2*i+1; }
142 	private int right(int i) { return 2*i+2; }
143 
144 	private int smallerChild(int i)
145 	{
146 		if(right(i) < length && pred(arr[right(i)], arr[left(i)]))
147 			return right(i);
148 		else
149 			return left(i);
150 	}
151 
152 	private void percolateUp(int i)
153 	{
154 		auto x  = move(arr[i]);
155 
156 		for(int p = parent(i); i != 0 && pred(x, arr[p]); i = p, p = parent(i))
157 			arr[i] = move(arr[p]);
158 
159 		arr[i] = move(x);
160 	}
161 
162 	private void percolateDown(int i)
163 	{
164 		auto x = move(arr[i]);
165 
166 		for(int c = smallerChild(i); c < length && pred(arr[c], x); i = c, c = smallerChild(i))
167 			arr[i] = move(arr[c]);
168 
169 		arr[i] = move(x);
170 	}
171 }
172 
173 /// basic usage
174 @nogc nothrow pure @safe unittest
175 {
176 	// custom predicate turns a min-heap into a max-heap
177 	PriorityQueue!int q;
178 	q.push(7);
179 	q.push(3);
180 	q.push(5);
181 	q.push(3);
182 
183 	assert(q.length == 4); // note that duplicates are kept
184 
185 	assert(q.pop == 3);
186 	assert(q.pop == 3);
187 	assert(q.pop == 5);
188 	assert(q.pop == 7);
189 
190 	assert(q.empty);
191 }
192 
193 /// custom predicate (without state)
194 /*@nogc*/ nothrow pure @safe unittest
195 {
196 	// custom predicate turns a min-heap into a max-heap
197 	PriorityQueue!(int, "a > b") q;
198 	q.push(7);
199 
200 	q.push([9,2,8,3,4,1,6,5,8,0]);
201 
202 	assert(q.pop == 9);
203 	assert(q.pop == 8);
204 	assert(q.pop == 8);
205 	assert(q.pop == 7);
206 	q.push(18);
207 	assert(q.pop == 18);
208 	assert(q.pop == 6);
209 	assert(q.pop == 5);
210 	q.clear;
211 	assert(q.empty);
212 }
213 
214 /// custom predicate (with state)
215 unittest
216 {
217 	// sometimes, you need a custom predicate/comparator that contains state.
218 	// For example this int-comparator puts one selected item first, followed
219 	// by all other integers in their usual order.
220 	struct Cmp
221 	{
222 		int priority;
223 
224 		@disable this();
225 
226 		this(int p)
227 		{
228 			this.priority = p;
229 		}
230 
231 		// this is understood as a comparison 'a < b'.
232 		bool opCall(int a, int b)
233 		{
234 			if(b == priority)
235 				return false;
236 			if(a == priority)
237 				return true;
238 			return a < b;
239 		}
240 	}
241 
242 	// the constructor now takes an instance of the comparator
243 	auto q = PriorityQueue!(int, Cmp)(Cmp(3));
244 
245 	q.push([2,3,4,1,5,0]);
246 	assert(q.pop == 3);
247 	assert(q.pop == 0);
248 	assert(q.pop == 1);
249 	assert(q.pop == 2);
250 	assert(q.pop == 4);
251 	assert(q.pop == 5);
252 }
253 
254 // check move-semantics
255 unittest
256 {
257 	struct S
258 	{
259 		int x = -1;
260 		alias x this;
261 		this(this)
262 		{
263 			// default-constructed objects might be copied, others may not.
264 			assert(x == -1);
265 		}
266 	}
267 
268 	PriorityQueue!S q;
269 	q.push(S(2));
270 	q.push(S(4));
271 	q.push(S(3));
272 	q.push(S(1));
273 	q.push(S(0));
274 	assert(q.pop == S(0));
275 	assert(q.pop == S(1));
276 	assert(q.pop == S(2));
277 }