This commit is contained in:
Dennis Eichhorn 2019-09-03 20:56:05 +02:00
parent 34cc6325da
commit e0aa1406e2
3 changed files with 397 additions and 1 deletions

View File

@ -0,0 +1,227 @@
<?php
/**
* Orange Management
*
* PHP Version 7.4
*
* @package phpOMS\Stdlib\Base
* @copyright Dennis Eichhorn
* @license OMS License 1.0
* @version 1.0.0
* @link https://orange-management.org
*/
declare(strict_types=1);
namespace phpOMS\Stdlib\Base;
/**
* Heap class.
*
* @package phpOMS\Stdlib\Base
* @license OMS License 1.0
* @link https://orange-management.org
* @since 1.0.0
*/
class Heap
{
private \Closure $compare = null;
private array $nodes = [];
public function __construct(\Closure $compare = null)
{
$this->compare = $compare ?? function($a, $b) { return $a <=> $b; };
}
public function insort($x, $lo = 0) : void
{
$hi = \count($this->nodes);
while ($lo < $hi) {
$mid = (int) \floor(($lo + $hi) / 2);
if ($this->compare($x, $this->node[$mid]) < 0) {
$hi = $mid;
} else {
$lo = $mid + 1;
}
}
$this->nodes = \array_splice($this->nodes, $lo, 0, $x);
}
public function push($item) : void
{
$this->nodes[] = $item;
$this->siftDown(0, \count($this->nodes) - 1);
}
public function pop()
{
$last = \array_pop($this->nodes);
if (empty($this->nodes)) {
return $last;
}
$item = $this->nodes[0];
$this->nodes[0] = $last;
$this->siftUp(0);
return $item;
}
public function peek()
{
return $this->nodes[0];
}
public function contains($item) : bool
{
foreach ($this->nodes as $key => $node) {
if (\is_scalar($item)) {
if ($node === $item) {
return true;
}
} else {
if ($item->isEqual($node)) {
return true;
}
}
}
return false;
}
public function replace($new)
{
$old = $this->nodes[0];
$this->nodes[0] = $new;
$this->siftUp(0);
return $old;
}
public function pushpop($item)
{
if (!empty($this->nodes) && $this->compare($this->nodes[0], $item) < 0) {
$temp = $item;
$item = $this->nodes[0];
$this->nodes[0] = $temp;
$this->siftUp(0);
}
return $item;
}
public function heapify() : void
{
for ($i = (int) \floor(\count($this->nodes) / 2); $i > -1; --$i) {
$this->siftUp($i);
}
}
public function update($item) : bool
{
$pos = null;
foreach ($this->nodes as $key => $node) {
if (\is_scalar($item)) {
if ($node === $item) {
$pos = $key;
break;
}
} else {
if ($item->isEqual($node)) {
$pos = $key;
break;
}
}
}
if ($pos === null) {
return false;
}
$this->siftDown(0, $pos);
$this->siftUp($pos);
return true;
}
public function getNLargest(int $n) : array
{
$nodes = $this->nodes;
\uasort($nodes, [$this, 'compare']);
return \array_slice($nodes, 0, $n);
}
public function getNSmallest(int $n): array
{
$nodes = $this->nodes;
\uasort($nodes, [$this, 'compare']);
return \array_slice(\array_reverse($nodes), 0, $n);
}
private function siftDown(int $start, int $pos) : void;
{
$item = $this->nodes[$pos];
while ($pos > $start) {
$pPos = ($pos - 1) >> 1;
$parent = $this->nodes[$pPos];
if ($this->compare($item, $parent) < 0) {
$this->nodes[$pos] = $parent;
$pos = $pPos;
continue;
}
break;
}
$this->nodes[$pos] = $item;
}
private function siftUp(int $pos) : void
{
$ePos = \count($this->nodes);
$sPos = $pos;
$item = $this->nodes[$pos];
$cPos = 2 * $pos + 1;
while ($cPos < $ePos) {
$rPos = $cPos + 1;
if ($rPos < $ePos && $this->compare($this->nodes[$cPos], $this->nodes[$rPos]) > -1) {
$cPos = $rPos;
}
$this->nodes[$pos] = $this->nodes[$cPos];
$pos = $cPos;
$cPos = 2 * $pos + 1;
}
$this->nodes[$pos] = $item;
$this->siftDown($sPos, $pos);
}
public function clear() : void
{
$this->nodes = [];
}
public function empty() : bool
{
return empty($this->nodes);
}
public function size() : int
{
return \count($this->nodes);
}
public function toArray()
{
return $this->nodes;
}
}

View File

@ -57,7 +57,9 @@ class BaseModelMapper extends DataMapperAbstract
'mapper' => OwnsOneModelMapper::class,
'dest' => 'test_base_owns_one_self',
],
]; /**
];
/**
* Has many relation.
*
* @var array<string, array<string, null|string>>

View File

@ -0,0 +1,167 @@
<?php
/**
* Orange Management
*
* PHP Version 7.4
*
* @package tests
* @copyright Dennis Eichhorn
* @license OMS License 1.0
* @version 1.0.0
* @link https://orange-management.org
*/
declare(strict_types=1);
namespace phpOMS\tests\Stdlib\Base;
use phpOMS\Stdlib\Base\Heap;
/**
* @internal
*/
class HeapTest extends \PHPUnit\Framework\TestCase
{
public function testPushAndPop() : void
{
$heap = new Heap();
for ($i = 0; $i < 10; ++$i) {
$heap->push(\mt_rand());
}
$sorted = [];
while (!$heap->empty()) {
$sorted[] = $heap->pop();
}
$sortedFunction = $sorted;
\sort($sortedFunction);
self::assertEquals($sortedFunction, $sorted);
}
public function testPushAndPopCustomComparator() : void
{
$heap = new Heap(function($a, $b) { return ($a <=> $b) * -1; });
for ($i = 0; $i < 10; ++$i) {
$heap->push(\mt_rand());
}
$sorted = [];
while (!$heap->empty()) {
$sorted[] = $heap->pop();
}
$sortedFunction = $sorted;
\sort($sortedFunction);
self::assertEquals(\array_reverse($sortedFunction), $sorted);
}
public function testReplace() : void
{
$heap = new Heap();
for ($i = 1; $i < 6; ++$i) {
$heap->push($i);
}
self::assertEquals(1, $heap->replace(3));
self::assertEquals([2, 3, 3, 4, 5], $heap->toArray());
}
public function testPushPop() : void
{
$heap = new Heap();
for ($i = 1; $i < 6; ++$i) {
$heap->push($i);
}
self::assertEquals(1, $heap->pushpop(6));
self::assertEquals([2, 3, 4, 5, 6], $heap->toArray());
}
public function testContains(): void
{
$heap = new Heap();
for ($i = 1; $i < 6; ++$i) {
$heap->push($i);
}
self::assertTrue($heap->contains(1));
self::assertTrue($heap->contains(2));
self::assertTrue($heap->contains(3));
self::assertTrue($heap->contains(4));
self::assertTrue($heap->contains(5));
self::assertFalse($heap->contains(0));
self::assertFalse($heap->contains(6));
}
public function testPeek() : void
{
$heap = new Heap();
$heap->push(1);
self::assertEqals(1, $heap->peek());
$heap->push(2);
self::assertEqals(1, $heap->peek());
$heap->pop();
self::assertEqals(2, $heap->peek());
}
public function testNSmallest() : void
{
$heap = new Heap();
$heap->push(1);
$heap->push(3);
$heap->push(1);
$heap->push(4);
self::assertEquals([1, 1, 3], $heap->getNSmallest(3));
}
public function testNLargest(): void
{
$heap = new Heap();
$heap->push(1);
$heap->push(3);
$heap->push(1);
$heap->push(4);
$heap->push(4);
self::assertEquals([4, 4, 3], $heap->getNLargest(3));
}
public function testSize(): void
{
$heap = new Heap();
for ($i = 1; $i < 6; ++$i) {
$heap->push($i);
}
self::assertEquals(5, $heap->size());
}
public function testClear(): void
{
$heap = new Heap();
for ($i = 1; $i < 6; ++$i) {
$heap->push($i);
}
$heap->clear();
self::assertEquals(0, $heap->size());
}
public function testEmpty(): void
{
$heap = new Heap();
self::assertTrue($heap->empty());
for ($i = 1; $i < 6; ++$i) {
$heap->push($i);
}
self::assertFalse($heap->empty());
}
}