Mercurial > hg > isophonics-drupal-site
comparison vendor/zendframework/zend-stdlib/src/FastPriorityQueue.php @ 0:4c8ae668cc8c
Initial import (non-working)
author | Chris Cannam |
---|---|
date | Wed, 29 Nov 2017 16:09:58 +0000 |
parents | |
children | 5fb285c0d0e3 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:4c8ae668cc8c |
---|---|
1 <?php | |
2 /** | |
3 * Zend Framework (http://framework.zend.com/) | |
4 * | |
5 * @link http://github.com/zendframework/zf2 for the canonical source repository | |
6 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) | |
7 * @license http://framework.zend.com/license/new-bsd New BSD License | |
8 */ | |
9 | |
10 namespace Zend\Stdlib; | |
11 | |
12 use Iterator; | |
13 use Countable; | |
14 use Serializable; | |
15 use SplPriorityQueue as PhpSplPriorityQueue; | |
16 | |
17 /** | |
18 * This is an efficient implementation of an integer priority queue in PHP | |
19 * | |
20 * This class acts like a queue with insert() and extract(), removing the | |
21 * elements from the queue and it also acts like an Iterator without removing | |
22 * the elements. This behaviour can be used in mixed scenarios with high | |
23 * performance boost. | |
24 */ | |
25 class FastPriorityQueue implements Iterator, Countable, Serializable | |
26 { | |
27 const EXTR_DATA = PhpSplPriorityQueue::EXTR_DATA; | |
28 const EXTR_PRIORITY = PhpSplPriorityQueue::EXTR_PRIORITY; | |
29 const EXTR_BOTH = PhpSplPriorityQueue::EXTR_BOTH; | |
30 | |
31 /** | |
32 * @var integer | |
33 */ | |
34 protected $extractFlag = self::EXTR_DATA; | |
35 | |
36 /** | |
37 * Elements of the queue, divided by priorities | |
38 * | |
39 * @var array | |
40 */ | |
41 protected $values = []; | |
42 | |
43 /** | |
44 * Array of priorities | |
45 * | |
46 * @var array | |
47 */ | |
48 protected $priorities = []; | |
49 | |
50 /** | |
51 * Array of priorities used for the iteration | |
52 * | |
53 * @var array | |
54 */ | |
55 protected $subPriorities = []; | |
56 | |
57 /** | |
58 * Max priority | |
59 * | |
60 * @var integer | |
61 */ | |
62 protected $maxPriority = 0; | |
63 | |
64 /** | |
65 * Total number of elements in the queue | |
66 * | |
67 * @var integer | |
68 */ | |
69 protected $count = 0; | |
70 | |
71 /** | |
72 * Index of the current element in the queue | |
73 * | |
74 * @var integer | |
75 */ | |
76 protected $index = 0; | |
77 | |
78 /** | |
79 * Sub index of the current element in the same priority level | |
80 * | |
81 * @var integer | |
82 */ | |
83 protected $subIndex = 0; | |
84 | |
85 /** | |
86 * Insert an element in the queue with a specified priority | |
87 * | |
88 * @param mixed $value | |
89 * @param integer $priority a positive integer | |
90 */ | |
91 public function insert($value, $priority) | |
92 { | |
93 if (! is_int($priority)) { | |
94 throw new Exception\InvalidArgumentException('The priority must be an integer'); | |
95 } | |
96 $this->values[$priority][] = $value; | |
97 if (! isset($this->priorities[$priority])) { | |
98 $this->priorities[$priority] = $priority; | |
99 $this->maxPriority = max($priority, $this->maxPriority); | |
100 } | |
101 ++$this->count; | |
102 } | |
103 | |
104 /** | |
105 * Extract an element in the queue according to the priority and the | |
106 * order of insertion | |
107 * | |
108 * @return mixed | |
109 */ | |
110 public function extract() | |
111 { | |
112 if (! $this->valid()) { | |
113 return false; | |
114 } | |
115 $value = $this->current(); | |
116 $this->nextAndRemove(); | |
117 return $value; | |
118 } | |
119 | |
120 /** | |
121 * Remove an item from the queue | |
122 * | |
123 * This is different than {@link extract()}; its purpose is to dequeue an | |
124 * item. | |
125 * | |
126 * Note: this removes the first item matching the provided item found. If | |
127 * the same item has been added multiple times, it will not remove other | |
128 * instances. | |
129 * | |
130 * @param mixed $datum | |
131 * @return bool False if the item was not found, true otherwise. | |
132 */ | |
133 public function remove($datum) | |
134 { | |
135 $this->rewind(); | |
136 while ($this->valid()) { | |
137 if (current($this->values[$this->maxPriority]) === $datum) { | |
138 $index = key($this->values[$this->maxPriority]); | |
139 unset($this->values[$this->maxPriority][$index]); | |
140 --$this->count; | |
141 return true; | |
142 } | |
143 $this->next(); | |
144 } | |
145 return false; | |
146 } | |
147 | |
148 /** | |
149 * Get the total number of elements in the queue | |
150 * | |
151 * @return integer | |
152 */ | |
153 public function count() | |
154 { | |
155 return $this->count; | |
156 } | |
157 | |
158 /** | |
159 * Get the current element in the queue | |
160 * | |
161 * @return mixed | |
162 */ | |
163 public function current() | |
164 { | |
165 switch ($this->extractFlag) { | |
166 case self::EXTR_DATA: | |
167 return current($this->values[$this->maxPriority]); | |
168 case self::EXTR_PRIORITY: | |
169 return $this->maxPriority; | |
170 case self::EXTR_BOTH: | |
171 return [ | |
172 'data' => current($this->values[$this->maxPriority]), | |
173 'priority' => $this->maxPriority | |
174 ]; | |
175 } | |
176 } | |
177 | |
178 /** | |
179 * Get the index of the current element in the queue | |
180 * | |
181 * @return integer | |
182 */ | |
183 public function key() | |
184 { | |
185 return $this->index; | |
186 } | |
187 | |
188 /** | |
189 * Set the iterator pointer to the next element in the queue | |
190 * removing the previous element | |
191 */ | |
192 protected function nextAndRemove() | |
193 { | |
194 if (false === next($this->values[$this->maxPriority])) { | |
195 unset($this->priorities[$this->maxPriority]); | |
196 unset($this->values[$this->maxPriority]); | |
197 $this->maxPriority = empty($this->priorities) ? 0 : max($this->priorities); | |
198 $this->subIndex = -1; | |
199 } | |
200 ++$this->index; | |
201 ++$this->subIndex; | |
202 --$this->count; | |
203 } | |
204 | |
205 /** | |
206 * Set the iterator pointer to the next element in the queue | |
207 * without removing the previous element | |
208 */ | |
209 public function next() | |
210 { | |
211 if (false === next($this->values[$this->maxPriority])) { | |
212 unset($this->subPriorities[$this->maxPriority]); | |
213 reset($this->values[$this->maxPriority]); | |
214 $this->maxPriority = empty($this->subPriorities) ? 0 : max($this->subPriorities); | |
215 $this->subIndex = -1; | |
216 } | |
217 ++$this->index; | |
218 ++$this->subIndex; | |
219 } | |
220 | |
221 /** | |
222 * Check if the current iterator is valid | |
223 * | |
224 * @return boolean | |
225 */ | |
226 public function valid() | |
227 { | |
228 return isset($this->values[$this->maxPriority]); | |
229 } | |
230 | |
231 /** | |
232 * Rewind the current iterator | |
233 */ | |
234 public function rewind() | |
235 { | |
236 $this->subPriorities = $this->priorities; | |
237 $this->maxPriority = empty($this->priorities) ? 0 : max($this->priorities); | |
238 $this->index = 0; | |
239 $this->subIndex = 0; | |
240 } | |
241 | |
242 /** | |
243 * Serialize to an array | |
244 * | |
245 * Array will be priority => data pairs | |
246 * | |
247 * @return array | |
248 */ | |
249 public function toArray() | |
250 { | |
251 $array = []; | |
252 foreach (clone $this as $item) { | |
253 $array[] = $item; | |
254 } | |
255 return $array; | |
256 } | |
257 | |
258 /** | |
259 * Serialize | |
260 * | |
261 * @return string | |
262 */ | |
263 public function serialize() | |
264 { | |
265 $clone = clone $this; | |
266 $clone->setExtractFlags(self::EXTR_BOTH); | |
267 | |
268 $data = []; | |
269 foreach ($clone as $item) { | |
270 $data[] = $item; | |
271 } | |
272 | |
273 return serialize($data); | |
274 } | |
275 | |
276 /** | |
277 * Deserialize | |
278 * | |
279 * @param string $data | |
280 * @return void | |
281 */ | |
282 public function unserialize($data) | |
283 { | |
284 foreach (unserialize($data) as $item) { | |
285 $this->insert($item['data'], $item['priority']); | |
286 } | |
287 } | |
288 | |
289 /** | |
290 * Set the extract flag | |
291 * | |
292 * @param integer $flag | |
293 */ | |
294 public function setExtractFlags($flag) | |
295 { | |
296 switch ($flag) { | |
297 case self::EXTR_DATA: | |
298 case self::EXTR_PRIORITY: | |
299 case self::EXTR_BOTH: | |
300 $this->extractFlag = $flag; | |
301 break; | |
302 default: | |
303 throw new Exception\InvalidArgumentException("The extract flag specified is not valid"); | |
304 } | |
305 } | |
306 | |
307 /** | |
308 * Check if the queue is empty | |
309 * | |
310 * @return boolean | |
311 */ | |
312 public function isEmpty() | |
313 { | |
314 return empty($this->values); | |
315 } | |
316 | |
317 /** | |
318 * Does the queue contain the given datum? | |
319 * | |
320 * @param mixed $datum | |
321 * @return bool | |
322 */ | |
323 public function contains($datum) | |
324 { | |
325 foreach ($this->values as $values) { | |
326 if (in_array($datum, $values)) { | |
327 return true; | |
328 } | |
329 } | |
330 return false; | |
331 } | |
332 | |
333 /** | |
334 * Does the queue have an item with the given priority? | |
335 * | |
336 * @param int $priority | |
337 * @return bool | |
338 */ | |
339 public function hasPriority($priority) | |
340 { | |
341 return isset($this->values[$priority]); | |
342 } | |
343 } |