diff --git a/Stdlib/Graph/BinaryTree.php b/Stdlib/Graph/BinaryTree.php index bad8940e0..152c2fad3 100644 --- a/Stdlib/Graph/BinaryTree.php +++ b/Stdlib/Graph/BinaryTree.php @@ -45,73 +45,78 @@ class BinaryTree extends Tree return $list; } - public function getLeft() { - return $this->nodes[0] ?? null; + public function getLeft(Node $base) + { + $neighbors = $base->getNeighbors($base); + + // todo: index can be wrong, see setLeft/setRight + return $neighbors[0] ?? null; } - public function getRight() { - return $this->nodes[1] ?? null; + public function getRight(Node $base) + { + $neighbors = $base->getNeighbors($base); + + // todo: index can be wrong, see setLeft/setRight + return $neighbors[1] ?? null; } - public function setLeft(BinaryTree $left) { - $this->nodes[0] = $left; + public function setLeft(Node $base, Node $left) + { + if($this->getLeft($base) === null) { + $this->addNode($base, $left); + // todo: doesn't know that this is left + // todo: maybe need to add numerics to edges? + } else { + // todo: replace node + } } - public function setRight(BinaryTree $right) { - $this->nodes[1] = $right; + public function setRight(Node $base, Node $right) + { + if($this->getRight($base) === null) { + $this->addNode($base, $right); + // todo: doesn't know that this is right + // todo: maybe need to add numerics to edges? + } else { + // todo: replace node + } } - public function preOrder(\Closure $callback) { + public function inOrder(Node $node, \Closure $callback) + { if(count($this->nodes) === 0) { return; } - $callback($this); - $this->nodes[0]->inOrder($callback); - $this->nodes[1]->inOrder($callback); + $this->inOrder($this->getLeft($node), $callback); + $callback($node); + $this->inOrder($this->getRight($node), $callback); } - public function inOrder(\Closure $callback) { - if(count($this->nodes) === 0) { - return; - } - - $this->nodes[0]->inOrder($callback); - $callback($this); - $this->nodes[1]->inOrder($callback); - } - - public function postOrder(\Closure $callback) { - if(count($this->nodes) === 0) { - return; - } - - $this->nodes[0]->inOrder($callback); - $this->nodes[1]->inOrder($callback); - $callback($this); - } - - private function getVerticalOrder(int $horizontalDistance = 0, array &$order) + private function getVerticalOrder(Node $node, int $horizontalDistance = 0, array &$order) { if(!isset($order[$horizontalDistance])) { $order[$horizontalDistance] = []; } - $order[$horizontalDistance][] = $this; + $order[$horizontalDistance][] = $node; + $left = $this->getLeft($node); + $right = $this->getRight($node); - if(isset($this->nodes[0])) { - $this->nodes[0]->getVerticalOrder($horizontalDistance-1, $order); + if(isset($left)) { + $this->getVerticalOrder($left, $horizontalDistance-1, $order); } - if(isset($this->nodes[1])) { - $this->nodes[1]->getVerticalOrder($horizontalDistance+1, $order); + if(isset($right)) { + $this->getVerticalOrder($right, $horizontalDistance+1, $order); } } - public function verticalOrder(\Closure $callback) + public function verticalOrder(Node $node, \Closure $callback) { $order = []; - $this->getVerticalOrder(0, $order); + $this->getVerticalOrder($node, 0, $order); foreach($order as $level) { foreach($level as $node) { @@ -120,19 +125,21 @@ class BinaryTree extends Tree } } - public function isSymmetric() : bool { - // todo: compare values? true symmetry requires the values to be the same - if(isset($this->nodes[0]) && isset($this->nodes[1])) { - return isSymmetric($this->nodes[0], $this->nodes[1]); + public function isSymmetric(Node $node1 = null, Node $node2 = null) : bool + { + if(!isset($node1) && !isset($node2)) { + return true; } - return false; - } + $left1 = $this->getLeft($node1); + $right1 = $this->getRight($node1); + + $left2 = isset($node2) ? $this->getLeft($node1) : $this->getLeft($node2); + $right2 = isset($node2) ? $this->getRight($node1) : $this->getRight($node2); - public function symmetric(BinaryTree $tree1, BinaryTree $tree2) : bool { // todo: compare values? true symmetry requires the values to be the same - if(($tree1 !== null && $tree2 !== null) || $tree1 === $tree2) { - return isSymmetric($tree1->getLeft(), $tree1->getRight()) && isSymmetric($tree2->getRight(), $tree2->getLeft()); + if(isset($node1) && isset($node2)) { + return $this->isSymmetric($left1, $right2) && $this->isSymmetric($right1, $left2); } return false; diff --git a/Stdlib/Graph/Edge.php b/Stdlib/Graph/Edge.php new file mode 100644 index 000000000..b4bd7ce33 --- /dev/null +++ b/Stdlib/Graph/Edge.php @@ -0,0 +1,53 @@ + + * @author Dennis Eichhorn + * @copyright 2013 Dennis Eichhorn + * @license OMS License 1.0 + * @version 1.0.0 + * @link http://orange-management.com + */ +namespace phpOMS\Datatypes; + +/** + * Tree class. + * + * @category Framework + * @package phpOMS\Datatypes + * @author OMS Development Team + * @author Dennis Eichhorn + * @license OMS License 1.0 + * @link http://orange-management.com + * @since 1.0.0 + */ +class Edge +{ + private $node1 = null; + + private $node2 = null; + + private $directed = false; + + public function __construct(Node $node1, Node $node2, bool $directed = false) + { + $this->node1 = $node1; + $this->node2 = $node2; + $this->directed = $directed; + } + + public function getNodes() : array + { + return [$this->node1, $this->node2]; + } + + public function isDirected() : bool + { + return $this->directed; + } +} \ No newline at end of file diff --git a/Stdlib/Graph/Graph.php b/Stdlib/Graph/Graph.php index e69de29bb..63a67fdb4 100644 --- a/Stdlib/Graph/Graph.php +++ b/Stdlib/Graph/Graph.php @@ -0,0 +1,239 @@ + + * @author Dennis Eichhorn + * @copyright 2013 Dennis Eichhorn + * @license OMS License 1.0 + * @version 1.0.0 + * @link http://orange-management.com + */ +namespace phpOMS\Datatypes; + +/** + * Tree class. + * + * @category Framework + * @package phpOMS\Datatypes + * @author OMS Development Team + * @author Dennis Eichhorn + * @license OMS License 1.0 + * @link http://orange-management.com + * @since 1.0.0 + */ +class Graph +{ + protected $nodes = []; + + protected $edges = []; + + public function addNode(Node $node) + { + $this->nodes[] = $node; + + return $this; + } + + public function setNode($key, Node $node) + { + $this->nodes[$key] = $node; + + return $this; + } + + public function addEdge(Edge $edge) + { + $this->edges[] = $edge; + + return $this; + } + + public function setEdge($key, Edge $edge) + { + $this->edges[$key] = $edge; + + return $this; + } + + public function getNode($key) : Node + { + return $this->nodes[$key]; + } + + public function getEdge($key) : Edge + { + return $this->edges[$key]; + } + + public function getEdgesOfNode($node) : array + { + if(!($node instanceof Node)) { + $node = $this->getNode($node); + } + + $edges = []; + foreach($this->edges as $edge) { + $nodes = $edge->getNodes(); + + if($nodes[0] === $node || $nodes[1] === $node) { + $edges[] = $edge; + } + } + + return $edges; + } + + public function getNeighbors($node) : array + { + if(!($node instanceof Node)) { + $node = $this->getNode($node); + } + + $edges = $this->getEdgesOfNode($node); + $neighbors = []; + + foreach($edges as $edge) { + $nodes = $edge->getNodes(); + + if($nodes[0] !== $node && $nodes[0] !== null) { + $neighbors[] = $nodes[0]; + } elseif($nodes[1] !== $node && $nodes[0] !== null) { + $neighbors[] = $nodes[1]; + } + } + + return $neighbors; + } + + public function getDimension() : int + { + return 0; + } + + public function getBridges() : array + { + return []; + } + + public function getKruskalMinimalSpanningTree() : Tree + { + return new Tree(); + } + + public function getPrimMinimalSpanningTree() : Tree + { + return new Tree(); + } + + public function getCircle() : array + { + + } + + public function getFloydWarshallShortestPath() : array + { + + } + + public function getDijkstraShortestPath() : array + { + + } + + public function depthFirstTraversal() : array + { + + } + + public function breadthFirstTraversal() : array + { + + } + + public function longestPath() : array + { + + } + + public function longestPathBetweenNodes() : array + { + + } + + public function getOrder() : int + { + return count($this->nodes); + } + + public function getSize() : int + { + return count($this->edges); + } + + public function getDiameter() : int + { + $diameter = 0; + + foreach($this->nodes as $node1) { + foreach($this->nodes as $node2) { + if($node1 === $node2) { + continue; + } + + $diameter = max($diameter, $this->getFloydWarshallShortestPath($node1, $node2)); + } + } + + return $diameter; + } + + public function getGirth() : int + { + + } + + public function getCircuitRank() : int + { + + } + + public function getNodeConnectivity() : int + { + + } + + public function getEdgeConnectivity() : int + { + + } + + public function isConnected() : bool + { + return true; + } + + public function getUnconnected() : array + { + // get all unconnected sub graphs + } + + public function isBipartite() : bool + { + return true; + } + + public function isTriangleFree() : bool + { + return true; + } + + public function isCircleFree() : bool + { + return true; + } +} \ No newline at end of file diff --git a/Stdlib/Graph/Node.php b/Stdlib/Graph/Node.php new file mode 100644 index 000000000..e556d3be6 --- /dev/null +++ b/Stdlib/Graph/Node.php @@ -0,0 +1,33 @@ + + * @author Dennis Eichhorn + * @copyright 2013 Dennis Eichhorn + * @license OMS License 1.0 + * @version 1.0.0 + * @link http://orange-management.com + */ +namespace phpOMS\Datatypes; + +/** + * Tree class. + * + * @category Framework + * @package phpOMS\Datatypes + * @author OMS Development Team + * @author Dennis Eichhorn + * @license OMS License 1.0 + * @link http://orange-management.com + * @since 1.0.0 + * + * @todo : there is a bug with Hungary ibans since they have two k (checksums) in their definition + */ +class Node +{ +} \ No newline at end of file diff --git a/Stdlib/Graph/Tree.php b/Stdlib/Graph/Tree.php index 82514b0cc..7a6213502 100644 --- a/Stdlib/Graph/Tree.php +++ b/Stdlib/Graph/Tree.php @@ -25,44 +25,58 @@ namespace phpOMS\Datatypes; * @license OMS License 1.0 * @link http://orange-management.com * @since 1.0.0 - * - * @todo : there is a bug with Hungary ibans since they have two k (checksums) in their definition */ class Tree extends Graph { - protected $nodes = []; + private $root = null; - public function add(Tree $node) { - $this->nodes[] = $node; - - return $this; - } - - public function set($key, Tree $node) { - $this->nodes[$key] = $node; - - return $this; - } - - public function getMaxDepth() : int + public function __construct() { - $depth = [0]; + $root = new Node(); + $this->addNode($root); + } - foreach($this->nodes as $node) { - $depth[] = $node->getMaxDepth(); + public function addNode(Node $base, Node $node) + { + parent::addNode($node); + parent::addEdge(new Edge($base, $node)); + } + + public function getMaxDepth(Node $node = null) : int + { + $currentNode = $node ?? $this->root; + + if(!isset($currentNode)) { + return 0; } - return max($depth) + 1; + $depth = 1; + $neighbors = $this->getNeighbors($currentNode); + + foreach($neighbors as $neighbor) { + $depth = max($depth, $depth + $this->getMaxDepth($neighbor)); + } + + return $depth; } - public function getMinDepth() : int + public function getMinDepth(Node $node = null) : int { - $depth = [0]; + $currentNode = $node ?? $this->root; - foreach($this->nodes as $node) { - $depth[] = $node->getMinDepth(); + if(!isset($currentNode)) { + return 0; } + $depth = []; + $neighbors = $this->getNeighbors($currentNode); + + foreach($neighbors as $neighbor) { + $depth[] = $this->getMaxDepth($neighbor); + } + + $depth = empty($depth) ? 0 : $depth; + return min($depth) + 1; } @@ -76,34 +90,70 @@ class Tree extends Graph } } - public function isLeaf() : bool + public function isLeaf(Node $node) : bool { - return count($this->nodes) === 0; + return count($this->getEdgesOfNode($node)) === 1; } - public function getDimension() : int - { - $size = 1; - - foreach($this->nodes as $node) { - $size += $node->getDimension() + 1; - } - - return $size; - } - - public function getLevelNodes(int $level, array &$nodes) + public function getLevelNodes(int $level, Node $node) : array { --$level; + $neighbors = $this->getNeighbors($node); + $nodes = []; + + if($level === 1) { + return $neighbors; + } + + foreach($neighbors as $neighbor) { + array_merge($nodes, $this->getLevelNodes($level, $neighbor)); + } + + return $nodes; + } + + public function isFull(int $type) : bool { + if(count($this->edges) % $type !== 0) { + return false; + } foreach($this->nodes as $node) { - if($level === 0) { - $nodes[] = $this; + $neighbors = count($this->getNeighbors($node)); - return $nodes; - } else { - $this->getLevelNodes($level, $nodes); + if($neighbors !== $type && $neighbors !== 0) { + return false; } } + + return true; + } + + public function preOrder(Node $node, \Closure $callback) { + if(count($this->nodes) === 0) { + return; + } + + $callback($node); + $neighbors = $this->getNeighbors(); + + foreach($neighbors as $neighbor) { + // todo: get neighbors needs to return in ordered way + $this->preOrder($neighbor, $callback); + } + } + + public function postOrder(Node $node, \Closure $callback) { + if(count($this->nodes) === 0) { + return; + } + + $neighbors = $this->getNeighbors(); + + foreach($neighbors as $neighbor) { + // todo: get neighbors needs to return in ordered way + $this->postOrder($neighbor, $callback); + } + + $callback($node); } } \ No newline at end of file