/vendor/zendframework/zend-stdlib/src/FastPriorityQueue.php

https://gitlab.com/reasonat/test8 · PHP · 343 lines · 164 code · 30 blank · 149 comment · 10 complexity · e2210049cbbbdc430ef33599e48e823d MD5 · raw file

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