mirror of
https://github.com/Karaka-Management/phpOMS.git
synced 2026-01-11 17:58:41 +00:00
219 lines
5.4 KiB
PHP
219 lines
5.4 KiB
PHP
<?php
|
|
/**
|
|
* Orange Management
|
|
*
|
|
* PHP Version 7.2
|
|
*
|
|
* @package phpOMS\Stdlib\Graph
|
|
* @copyright Dennis Eichhorn
|
|
* @license OMS License 1.0
|
|
* @version 1.0.0
|
|
* @link http://website.orange-management.de
|
|
*/
|
|
declare(strict_types=1);
|
|
|
|
namespace phpOMS\Stdlib\Graph;
|
|
|
|
/**
|
|
* Tree class.
|
|
*
|
|
* @package phpOMS\Stdlib\Graph
|
|
* @license OMS License 1.0
|
|
* @link http://website.orange-management.de
|
|
* @since 1.0.0
|
|
*
|
|
* @todo : there is a bug with Hungary ibans since they have two k (checksums) in their definition
|
|
*/
|
|
class BinaryTree extends Tree
|
|
{
|
|
public static function invert($list) : BinaryTree
|
|
{
|
|
if (empty($list->getNodes())) {
|
|
return $list;
|
|
}
|
|
|
|
$left = $list->getLeft();
|
|
$list->setLeft($list->invert($list->nodes[1]));
|
|
$list->setRight($list->invert($left));
|
|
|
|
return $list;
|
|
}
|
|
|
|
/**
|
|
* Get left node of a node.
|
|
*
|
|
* @param Node $base Tree node
|
|
*
|
|
* @return Node Left node
|
|
*
|
|
* @since 1.0.0
|
|
*/
|
|
public function getLeft(Node $base)
|
|
{
|
|
$neighbors = $base->getNeighbors($base);
|
|
|
|
// todo: index can be wrong, see setLeft/setRight
|
|
return $neighbors[0] ?? null;
|
|
}
|
|
|
|
/**
|
|
* Get right node of a node.
|
|
*
|
|
* @param Node $base Tree node
|
|
*
|
|
* @return Node Right node
|
|
*
|
|
* @since 1.0.0
|
|
*/
|
|
public function getRight(Node $base)
|
|
{
|
|
$neighbors = $this->getNeighbors($base);
|
|
|
|
// todo: index can be wrong, see setLeft/setRight
|
|
return $neighbors[1] ?? null;
|
|
}
|
|
|
|
/**
|
|
* Set left node of node.
|
|
*
|
|
* @param Node $base Base node
|
|
* @param Node $left Left node
|
|
*
|
|
* @return BinaryTree
|
|
*
|
|
* @since 1.0.0
|
|
*/
|
|
public function setLeft(Node $base, Node $left) : BinaryTree
|
|
{
|
|
if ($this->getLeft($base) === null) {
|
|
$this->addNodeRelative($base, $left);
|
|
// todo: doesn't know that this is left
|
|
// todo: maybe need to add numerics to edges?
|
|
} else {
|
|
// todo: replace node
|
|
}
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Set right node of node.
|
|
*
|
|
* @param Node $base Base node
|
|
* @param Node $right Right node
|
|
*
|
|
* @return BinaryTree
|
|
*
|
|
* @since 1.0.0
|
|
*/
|
|
public function setRight(Node $base, Node $right) /* : void */
|
|
{
|
|
if ($this->getRight($base) === null) {
|
|
$this->addNodeRelative($base, $right);
|
|
// todo: doesn't know that this is right
|
|
// todo: maybe need to add numerics to edges?
|
|
} else {
|
|
// todo: replace node
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Perform action on tree in in-order.
|
|
*
|
|
* @param Node $node Tree node
|
|
* @param \Closure $callback Task to perform on node
|
|
*
|
|
* @return void
|
|
*
|
|
* @since 1.0.0
|
|
*/
|
|
public function inOrder(Node $node, \Closure $callback) : void
|
|
{
|
|
$this->inOrder($this->getLeft($node), $callback);
|
|
$callback($node);
|
|
$this->inOrder($this->getRight($node), $callback);
|
|
}
|
|
|
|
/**
|
|
* Get nodes in vertical order.
|
|
*
|
|
* @param Node $node Tree node
|
|
* @param int $horizontalDistance Horizontal distance
|
|
* @param Node[] $order Ordered nodes by horizontal distance
|
|
*
|
|
* @return void
|
|
*
|
|
* @since 1.0.0
|
|
*/
|
|
private function getVerticalOrder(Node $node, int $horizontalDistance = 0, array &$order) : void
|
|
{
|
|
if (!isset($order[$horizontalDistance])) {
|
|
$order[$horizontalDistance] = [];
|
|
}
|
|
|
|
$order[$horizontalDistance][] = $node;
|
|
$left = $this->getLeft($node);
|
|
$right = $this->getRight($node);
|
|
|
|
if ($left !== null) {
|
|
$this->getVerticalOrder($left, $horizontalDistance - 1, $order);
|
|
}
|
|
|
|
if ($right !== null) {
|
|
$this->getVerticalOrder($right, $horizontalDistance + 1, $order);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Perform action on tree in vertical-order.
|
|
*
|
|
* @param Node $node Tree node
|
|
* @param \Closure $callback Task to perform on node
|
|
*
|
|
* @return void
|
|
*
|
|
* @since 1.0.0
|
|
*/
|
|
public function verticalOrder(Node $node, \Closure $callback) : void
|
|
{
|
|
$order = [];
|
|
$this->getVerticalOrder($node, 0, $order);
|
|
|
|
foreach ($order as $level) {
|
|
foreach ($level as $node) {
|
|
$callback($node);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check if tree is symmetric.
|
|
*
|
|
* @param Node $node1 Tree node1
|
|
* @param Node $node2 Tree node2 (optional, can be different tree)
|
|
*
|
|
* @return bool True if tree is symmetric, false if tree is not symmetric
|
|
*
|
|
* @since 1.0.0
|
|
*/
|
|
public function isSymmetric(Node $node1 = null, Node $node2 = null) : bool
|
|
{
|
|
if ($node1 === null && $node2 === null) {
|
|
return true;
|
|
}
|
|
|
|
$left1 = $this->getLeft($node1);
|
|
$right1 = $this->getRight($node1);
|
|
|
|
$left2 = $node2 !== null ? $this->getLeft($node1) : $this->getLeft($node2);
|
|
$right2 = $node2 !== null ? $this->getRight($node1) : $this->getRight($node2);
|
|
|
|
// todo: compare values? true symmetry requires the values to be the same
|
|
if ($node1 !== null && $node2 !== null) {
|
|
return $this->isSymmetric($left1, $right2) && $this->isSymmetric($right1, $left2);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|