Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
53 / 53
100.00% covered (success)
100.00%
17 / 17
CRAP
100.00% covered (success)
100.00%
1 / 1
PriorityQueue
100.00% covered (success)
100.00%
53 / 53
100.00% covered (success)
100.00%
17 / 17
32
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 getType
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 insert
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
2
 getInsertPosition
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
6
 getInsertFIFO
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getInsertLIFO
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getInsertHighest
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
3
 getInsertLowest
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
3
 increaseAll
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 pop
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 delete
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 setPriority
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
3
 get
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getAll
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 count
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 serialize
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 unserialize
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2/**
3 * Jingga
4 *
5 * PHP Version 8.1
6 *
7 * @package   phpOMS\Stdlib\Queue
8 * @copyright Dennis Eichhorn
9 * @license   OMS License 2.0
10 * @version   1.0.0
11 * @link      https://jingga.app
12 */
13declare(strict_types=1);
14
15namespace phpOMS\Stdlib\Queue;
16
17use phpOMS\Contract\SerializableInterface;
18use phpOMS\Stdlib\Base\Exception\InvalidEnumValue;
19
20/**
21 * Priority queue class.
22 *
23 * @package phpOMS\Stdlib\Queue
24 * @license OMS License 2.0
25 * @link    https://jingga.app
26 * @since   1.0.0
27 */
28class PriorityQueue implements \Countable, SerializableInterface
29{
30    /**
31     * Queue type.
32     *
33     * @var int
34     * @since 1.0.0
35     */
36    private int $type = PriorityMode::FIFO;
37
38    /**
39     * Queue.
40     *
41     * @var array<int, array{data:mixed, priority:float}>
42     * @since 1.0.0
43     */
44    private array $queue = [];
45
46    /**
47     * Constructor.
48     *
49     * @throws InvalidEnumValue This exception is thrown if the priority mode is invalid
50     *
51     * @since 1.0.0
52     */
53    public function __construct(int $type = PriorityMode::FIFO)
54    {
55        if (!PriorityMode::isValidValue($type)) {
56            throw new InvalidEnumValue($type);
57        }
58
59        $this->type = $type;
60    }
61
62    /**
63     * Get priority queue type
64     *
65     * @return int
66     *
67     * @since 1.0.0
68     */
69    public function getType() : int
70    {
71        return $this->type;
72    }
73
74    /**
75     * Insert element into queue.
76     *
77     * @param mixed $data     Queue element
78     * @param float $priority Priority of this element
79     *
80     * @return int
81     *
82     * @since 1.0.0
83     */
84    public function insert(mixed $data, float $priority = 1.0) : int
85    {
86        do {
87            $key = \mt_rand();
88        } while (isset($this->queue[$key]));
89
90        if (empty($this->queue)) {
91            $this->queue[$key] = ['data' => $data, 'priority' => $priority];
92        } else {
93            $pos = $this->getInsertPosition($priority);
94
95            $this->queue = \array_slice($this->queue, 0, $pos, true)
96                + [$key => ['data' => $data, 'priority' => $priority]]
97                + \array_slice($this->queue, $pos, null, true);
98        }
99
100        return $key;
101    }
102
103    /**
104     * Get insert position
105     *
106     * @param float $priority Priority of new element
107     *
108     * @return int
109     *
110     * @throws InvalidEnumValue
111     *
112     * @since 1.0.0
113     */
114    private function getInsertPosition(float $priority) : int
115    {
116        switch($this->type) {
117            case PriorityMode::FIFO:
118                return $this->getInsertFIFO();
119            case PriorityMode::LIFO:
120                return $this->getInsertLIFO();
121            case PriorityMode::HIGHEST:
122                return $this->getInsertHighest($priority);
123            case PriorityMode::LOWEST:
124                return $this->getInsertLowest($priority);
125            default:
126                throw new InvalidEnumValue($this->type); // @codeCoverageIgnore
127        }
128    }
129
130    /**
131     * Get insert position
132     *
133     * @return int
134     *
135     * @since 1.0.0
136     */
137    private function getInsertFIFO() : int
138    {
139        return 0;
140    }
141
142    /**
143     * Get insert position
144     *
145     * @return int
146     *
147     * @since 1.0.0
148     */
149    private function getInsertLIFO() : int
150    {
151        return \count($this->queue);
152    }
153
154    /**
155     * Get insert position
156     *
157     * @param float $priority Priority of new element
158     *
159     * @return int
160     *
161     * @since 1.0.0
162     */
163    private function getInsertHighest(float $priority) : int
164    {
165        $pos = 0;
166        foreach ($this->queue as $ele) {
167            if ($ele['priority'] > $priority) {
168                break;
169            }
170
171            ++$pos;
172        }
173
174        return $pos;
175    }
176
177    /**
178     * Get insert position
179     *
180     * @param float $priority Priority of new element
181     *
182     * @return int
183     *
184     * @since 1.0.0
185     */
186    private function getInsertLowest(float $priority) : int
187    {
188        $pos = 0;
189        foreach ($this->queue as $ele) {
190            if ($ele['priority'] < $priority) {
191                break;
192            }
193
194            ++$pos;
195        }
196
197        return $pos;
198    }
199
200    /**
201     * Increase all queue priorities.
202     *
203     * @param float $increase Value to increase every element
204     *
205     * @return void
206     *
207     * @since 1.0.0
208     */
209    public function increaseAll(float $increase = 1.0) : void
210    {
211        foreach ($this->queue as $key => &$ele) {
212            $ele['priority'] += $increase;
213        }
214    }
215
216    /**
217     * Pop element.
218     *
219     * @return array
220     *
221     * @since 1.0.0
222     */
223    public function pop() : array
224    {
225        return \array_pop($this->queue) ?? [];
226    }
227
228    /**
229     * Delete element.
230     *
231     * @param int $id Element to delete
232     *
233     * @return bool
234     *
235     * @since 1.0.0
236     */
237    public function delete(int $id) : bool
238    {
239        if (isset($this->queue[$id])) {
240            unset($this->queue[$id]);
241
242            return true;
243        }
244
245        return false;
246    }
247
248    /**
249     * Set element priority.
250     *
251     * @param int   $id       Element ID
252     * @param float $priority Element priority
253     *
254     * @return void
255     *
256     * @since 1.0.0
257     */
258    public function setPriority(int $id, float $priority) : void
259    {
260        if ($this->type === PriorityMode::FIFO || $this->type === PriorityMode::LIFO) {
261            $this->queue[$id]['priority'] = $priority;
262
263            return;
264        }
265
266        $temp = $this->queue[$id];
267        $this->delete($id);
268
269        $pos = $this->getInsertPosition($priority);
270
271        $this->queue = \array_slice($this->queue, 0, $pos, true)
272            + [$id => ['data' => $temp['data'], 'priority' => $priority]]
273            + \array_slice($this->queue, $pos, null, true);
274    }
275
276    /**
277     * Get element
278     *
279     * @param int $id Element ID
280     *
281     * @return array
282     *
283     * @since 1.0.0
284     */
285    public function get(int $id) : array
286    {
287        return $this->queue[$id] ?? [];
288    }
289
290    /**
291     * Get element
292     *
293     * @return array<int, array{data:mixed, priority:float}>
294     *
295     * @since 1.0.0
296     */
297    public function getAll() : array
298    {
299        return $this->queue;
300    }
301
302    /**
303     * {@inheritdoc}
304     */
305    public function count() : int
306    {
307        return \count($this->queue);
308    }
309
310    /**
311     * {@inheritdoc}
312     */
313    public function serialize() : string
314    {
315        return (string) \json_encode($this->queue);
316    }
317
318    /**
319     * Unserialize queue.
320     *
321     * @param string $data Data to unserialze
322     *
323     * @return void
324     *
325     * @since 1.0.0
326     */
327    public function unserialize(mixed $data) : void
328    {
329        $this->queue = \json_decode($data, true); /* @phpstan-ignore-line */
330    }
331}