Implement queue

This commit is contained in:
Dennis Eichhorn 2018-09-09 23:35:35 +02:00
parent 105d4f355b
commit 4b791bf12d
4 changed files with 360 additions and 65 deletions

View File

@ -26,10 +26,8 @@ use phpOMS\Stdlib\Base\Enum;
*/ */
abstract class PriorityMode extends Enum abstract class PriorityMode extends Enum
{ {
public const FIFO = 1; public const FIFO = 1;
public const LIFO = 2; public const LIFO = 2;
public const EARLIEST_DEADLINE = 4; public const HIGHEST = 4;
public const SHORTEST_JOB = 8; public const LOWEST = 8;
public const HIGHEST = 16;
public const LOWEST = 32;
} }

View File

@ -24,14 +24,13 @@ namespace phpOMS\Stdlib\Queue;
*/ */
class PriorityQueue implements \Countable, \Serializable class PriorityQueue implements \Countable, \Serializable
{ {
/** /**
* Queue elements. * Queue type.
* *
* @var int * @var int
* @since 1.0.0 * @since 1.0.0
*/ */
private $count = 0; private $type = PriorityMode::FIFO;
/** /**
* Queue. * Queue.
@ -46,46 +45,151 @@ class PriorityQueue implements \Countable, \Serializable
* *
* @since 1.0.0 * @since 1.0.0
*/ */
public function __construct() public function __construct(int $type = PriorityMode::FIFO)
{ {
$this->type = $type;
}
/**
* Get priority queue type
*
* @return int
*
* @since 1.0.0
*/
public function getType() : int
{
return $this->type;
} }
/** /**
* Insert element into queue. * Insert element into queue.
* *
* @param mixed $data Queue element * @param mixed $data Queue element
* @param string $job Job cmd
* @param float $priority Priority of this element * @param float $priority Priority of this element
* *
* @return int * @return int
* *
* @since 1.0.0 * @since 1.0.0
*/ */
public function insert($data, string $job, float $priority = 1.0) : int public function insert($data, $priority = 1) : int
{ {
do { do {
$key = \rand(); $key = \mt_rand();
} while (\array_key_exists($key, $this->queue)); } while (isset($this->queue[$key]));
if ($this->count === 0) { if (\count($this->queue) === 0) {
$this->queue[$key] = ['key' => $key, 'job' => $job, 'priority' => $priority]; $this->queue[$key] = ['data' => $data, 'priority' => $priority];
} else { } else {
$pos = 0; $pos = $this->getInsertPosition($priority);
foreach ($this->queue as $ele) {
if ($ele['priority'] < $priority) {
break;
}
$pos++; $this->queue = \array_slice($this->queue, 0, $pos, true)
} + [$key => ['data' => $data, 'priority' => $priority]]
+ \array_slice($this->queue, $pos, null, true);
$original = [];
\array_splice($original, $pos, 0, [$key => ['key' => $key, 'job' => $job, 'priority' => $priority]]);
} }
return $key; return $key;
} }
/**
* Get insert position
*
* @param float $priority Priority of new element
*
* @return int
*
* @since 1.0.0
*/
private function getInsertPosition($priority) : int
{
switch($this->type) {
case PriorityMode::FIFO:
return $this->getInsertFIFO($priority);
case PriorityMode::LIFO:
return $this->getInsertLIFO($priority);
case PriorityMode::HIGHEST:
return $this->getInsertHighest($priority);
case PriorityMode::LOWEST:
return $this->getInsertLowest($priority);
default:
throw new \InvalidArgumentException();
}
}
/**
* Get insert position
*
* @param float $priority Priority of new element
*
* @return int
*
* @since 1.0.0
*/
private function getInsertFIFO($priority) : int
{
return 0;
}
/**
* Get insert position
*
* @param float $priority Priority of new element
*
* @return int
*
* @since 1.0.0
*/
private function getInsertLIFO($priority) : int
{
return \count($this->queue);
}
/**
* Get insert position
*
* @param float $priority Priority of new element
*
* @return int
*
* @since 1.0.0
*/
private function getInsertHighest($priority) : int
{
$pos = 0;
foreach ($this->queue as $ele) {
if ($ele['priority'] > $priority) {
break;
}
++$pos;
}
return $pos;
}
/**
* Get insert position
*
* @param float $priority Priority of new element
*
* @return int
*
* @since 1.0.0
*/
private function getInsertLowest($priority) : int
{
$pos = 0;
foreach ($this->queue as $ele) {
if ($ele['priority'] < $priority) {
break;
}
++$pos;
}
return $pos;
}
/** /**
* Increase all queue priorities. * Increase all queue priorities.
* *
@ -95,7 +199,7 @@ class PriorityQueue implements \Countable, \Serializable
* *
* @since 1.0.0 * @since 1.0.0
*/ */
public function increaseAll(float $increase = 0.1) : void public function increaseAll($increase = 1) : void
{ {
foreach ($this->queue as $key => &$ele) { foreach ($this->queue as $key => &$ele) {
$ele['priority'] += $increase; $ele['priority'] += $increase;
@ -109,68 +213,83 @@ class PriorityQueue implements \Countable, \Serializable
* *
* @since 1.0.0 * @since 1.0.0
*/ */
public function pop() public function pop() : array
{ {
return array_pop($this->queue); return \array_pop($this->queue);
} }
/** /**
* Delete element. * Delete element.
* *
* @param int $id Element to delete * @param mixed $id Element to delete
* *
* @return void * @return bool
* *
* @since 1.0.0 * @since 1.0.0
*/ */
public function delete(int $id = null) : void public function delete($id) : bool
{ {
if ($id === null) { if (isset($this->queue[$id])) {
$this->remove();
} else {
unset($this->queue[$id]); unset($this->queue[$id]);
}
}
/** return true;
* Delete last element. }
*
* @return mixed return false;
*
* @since 1.0.0
*/
public function remove()
{
return array_pop($this->queue);
} }
/** /**
* Set element priority. * Set element priority.
* *
* @param int $id Element ID * @param mixed $id Element ID
* @param float $priority Element priority * @param mixed $priority Element priority
* *
* @return void * @return void
* *
* @since 1.0.0 * @since 1.0.0
*/ */
public function setPriority(int $id, float $priority) : void public function setPriority($id, $priority) : void
{ {
$this->queue[$id]['priority'] = $priority; if ($this->type === PriorityMode::FIFO || $this->type === PriorityMode::LIFO) {
$this->queue[$id]['priority'] = $priority;
return;
}
$temp = $this->queue[$id];
$this->delete($id);
$pos = $this->getInsertPosition($priority);
$this->queue = \array_slice($this->queue, 0, $pos, true)
+ [$id => ['data' => $temp['data'], 'priority' => $priority]]
+ \array_slice($this->queue, $pos, null, true);
} }
/** /**
* Set element priority. * Get element
* *
* @param int $id Element ID * @param mixed $id Element ID
* *
* @return float * @return array
* *
* @since 1.0.0 * @since 1.0.0
*/ */
public function getPriority(int $id) : float public function get($id) : array
{ {
return $this->queue[$id]['priority']; return $this->queue[$id] ?? [];
}
/**
* Get element
*
* @return array
*
* @since 1.0.0
*/
public function getAll() : array
{
return $this->queue;
} }
/** /**
@ -178,7 +297,7 @@ class PriorityQueue implements \Countable, \Serializable
*/ */
public function count() : int public function count() : int
{ {
return $this->count; return \count($this->queue);
} }
/** /**
@ -201,6 +320,5 @@ class PriorityQueue implements \Countable, \Serializable
public function unserialize($data) public function unserialize($data)
{ {
$this->queue = \json_decode($data); $this->queue = \json_decode($data);
$this->count = \count($this->queue);
} }
} }

View File

@ -19,12 +19,10 @@ class PriorityModeTest extends \PHPUnit\Framework\TestCase
{ {
public function testEnums() public function testEnums()
{ {
self::assertEquals(6, \count(PriorityMode::getConstants())); self::assertEquals(4, \count(PriorityMode::getConstants()));
self::assertEquals(1, PriorityMode::FIFO); self::assertEquals(1, PriorityMode::FIFO);
self::assertEquals(2, PriorityMode::LIFO); self::assertEquals(2, PriorityMode::LIFO);
self::assertEquals(4, PriorityMode::EARLIEST_DEADLINE); self::assertEquals(4, PriorityMode::HIGHEST);
self::assertEquals(8, PriorityMode::SHORTEST_JOB); self::assertEquals(8, PriorityMode::LOWEST);
self::assertEquals(16, PriorityMode::HIGHEST);
self::assertEquals(32, PriorityMode::LOWEST);
} }
} }

View File

@ -14,11 +14,192 @@
namespace phpOMS\tests\Stdlib\Queue; namespace phpOMS\tests\Stdlib\Queue;
use phpOMS\Stdlib\Queue\PriorityQueue; use phpOMS\Stdlib\Queue\PriorityQueue;
use phpOMS\Stdlib\Queue\PriorityMode;
class PriorityQueueTest extends \PHPUnit\Framework\TestCase class PriorityQueueTest extends \PHPUnit\Framework\TestCase
{ {
public function testPlaceholder() public function testDefault()
{ {
self::markTestIncomplete(); $queue = new PriorityQueue();
self::assertEquals(0, $queue->count());
self::assertEquals(PriorityMode::FIFO, $queue->getType());
}
public function testGeneral()
{
$queue = new PriorityQueue();
self::assertGreaterThan(0, $id1 = $queue->insert('a'));
self::assertEquals(1, $queue->count());
self::assertGreaterThan(0, $id2 = $queue->insert('b', 2));
self::assertEquals(2, $queue->count());
self::assertGreaterThan(0, $id3 = $queue->insert('c', -1));
self::assertEquals(3, $queue->count());
$queue->increaseAll(2);
self::assertEquals([
['data' => 'c', 'priority' => 1],
['data' => 'b', 'priority' => 4],
['data' => 'a', 'priority' => 3],
], \array_values($queue->getAll())
);
$queue->setPriority($id1, 3);
self::assertEquals(3, $queue->get($id1)['priority']);
}
public function testFIFO()
{
$queue = new PriorityQueue(PriorityMode::FIFO);
self::assertGreaterThan(0, $id1 = $queue->insert('a'));
self::assertEquals(1, $queue->count());
self::assertGreaterThan(0, $id2 = $queue->insert('b', 2));
self::assertEquals(2, $queue->count());
self::assertGreaterThan(0, $id3 = $queue->insert('c', -1));
self::assertEquals(3, $queue->count());
self::assertEquals([
['data' => 'c', 'priority' => -1],
['data' => 'b', 'priority' => 2],
['data' => 'a', 'priority' => 1],
], \array_values($queue->getAll())
);
$queue->setPriority($id1, -2);
self::assertEquals(-2, $queue->get($id1)['priority']);
self::assertEquals([
['data' => 'c', 'priority' => -1],
['data' => 'b', 'priority' => 2],
['data' => 'a', 'priority' => -2],
], \array_values($queue->getAll())
);
self::assertEquals(['data' => 'a', 'priority' => -2], $queue->get($id1));
self::assertEquals(['data' => 'a', 'priority' => -2], $queue->pop());
self::assertEquals(2, $queue->count());
self::assertTrue($queue->delete($id3));
self::assertFalse($queue->delete($id3));
self::assertEquals(1, $queue->count());
}
public function testLIFO()
{
$queue = new PriorityQueue(PriorityMode::LIFO);
self::assertGreaterThan(0, $id1 = $queue->insert('a'));
self::assertEquals(1, $queue->count());
self::assertGreaterThan(0, $id2 = $queue->insert('b', 2));
self::assertEquals(2, $queue->count());
self::assertGreaterThan(0, $id3 = $queue->insert('c', -1));
self::assertEquals(3, $queue->count());
self::assertEquals([
['data' => 'a', 'priority' => 1],
['data' => 'b', 'priority' => 2],
['data' => 'c', 'priority' => -1],
], \array_values($queue->getAll())
);
$queue->setPriority($id1, 3);
self::assertEquals(3, $queue->get($id1)['priority']);
self::assertEquals([
['data' => 'a', 'priority' => 3],
['data' => 'b', 'priority' => 2],
['data' => 'c', 'priority' => -1],
], \array_values($queue->getAll())
);
self::assertEquals(['data' => 'c', 'priority' => -1], $queue->get($id3));
self::assertEquals(['data' => 'c', 'priority' => -1], $queue->pop());
self::assertEquals(2, $queue->count());
self::assertTrue($queue->delete($id1));
self::assertFalse($queue->delete($id1));
self::assertEquals(1, $queue->count());
}
public function testHighest()
{
$queue = new PriorityQueue(PriorityMode::HIGHEST);
self::assertGreaterThan(0, $id1 = $queue->insert('a'));
self::assertEquals(1, $queue->count());
self::assertGreaterThan(0, $id2 = $queue->insert('b', 2));
self::assertEquals(2, $queue->count());
self::assertGreaterThan(0, $id3 = $queue->insert('c', -1));
self::assertEquals(3, $queue->count());
self::assertEquals([
['data' => 'c', 'priority' => -1],
['data' => 'a', 'priority' => 1],
['data' => 'b', 'priority' => 2],
], \array_values($queue->getAll())
);
$queue->setPriority($id1, 3);
self::assertEquals(3, $queue->get($id1)['priority']);
self::assertEquals([
['data' => 'c', 'priority' => -1],
['data' => 'b', 'priority' => 2],
['data' => 'a', 'priority' => 3],
], \array_values($queue->getAll())
);
self::assertEquals(['data' => 'a', 'priority' => 3], $queue->get($id1));
self::assertEquals(['data' => 'a', 'priority' => 3], $queue->pop());
self::assertEquals(2, $queue->count());
self::assertTrue($queue->delete($id2));
self::assertFalse($queue->delete($id2));
self::assertEquals(1, $queue->count());
}
public function testLowest()
{
$queue = new PriorityQueue(PriorityMode::LOWEST);
self::assertGreaterThan(0, $id1 = $queue->insert('a'));
self::assertEquals(1, $queue->count());
self::assertGreaterThan(0, $id2 = $queue->insert('b', 2));
self::assertEquals(2, $queue->count());
self::assertGreaterThan(0, $id3 = $queue->insert('c', -1));
self::assertEquals(3, $queue->count());
self::assertEquals([
['data' => 'b', 'priority' => 2],
['data' => 'a', 'priority' => 1],
['data' => 'c', 'priority' => -1],
], \array_values($queue->getAll())
);
$queue->setPriority($id3, 2);
self::assertEquals(2, $queue->get($id3)['priority']);
self::assertEquals([
['data' => 'b', 'priority' => 2],
['data' => 'c', 'priority' => 2],
['data' => 'a', 'priority' => 1],
], \array_values($queue->getAll())
);
self::assertEquals(['data' => 'a', 'priority' => 1], $queue->get($id1));
self::assertEquals(['data' => 'a', 'priority' => 1], $queue->pop());
self::assertEquals(2, $queue->count());
self::assertTrue($queue->delete($id2));
self::assertFalse($queue->delete($id2));
self::assertEquals(1, $queue->count());
} }
} }