fix bugs and start impl. additional functions

This commit is contained in:
Dennis Eichhorn 2021-07-17 21:12:01 +02:00
parent e2f6061fa7
commit 522db1d462
9 changed files with 196 additions and 56 deletions

View File

@ -416,11 +416,11 @@ class DataMapperAbstract implements DataMapperInterface
}
if ($condValue['value'] !== null) {
$where1->andWhere(static::$table . '_' . $searchDepth . '.' . $column, $condValue['comparison'], $condValue['value']);
$where1->andWhere(static::$table . '_d' . $searchDepth . '.' . $column, $condValue['comparison'], $condValue['value']);
}
if ($condValue['orderBy'] !== null) {
$where1->orderBy(static::$table . '_' . $searchDepth . '.' . static::getColumnByMember($condValue['orderBy']), $condValue['sortOrder']);
$where1->orderBy(static::$table . '_d' . $searchDepth . '.' . static::getColumnByMember($condValue['orderBy']), $condValue['sortOrder']);
}
if ($condValue['limit'] !== null) {
@ -433,7 +433,7 @@ class DataMapperAbstract implements DataMapperInterface
$hasAutocompletes = false;
foreach (static::$columns as $col) {
if (isset($col['autocomplete']) && $col['autocomplete']) {
$where2->where(static::$table . '_' . $searchDepth . '.' . $col['name'], 'LIKE', '%' . $search . '%', 'OR');
$where2->where(static::$table . '_d' . $searchDepth . '.' . $col['name'], 'LIKE', '%' . $search . '%', 'OR');
$hasAutocompletes = true;
}
}

View File

@ -14,6 +14,8 @@ declare(strict_types=1);
namespace phpOMS\Message;
use phpOMS\Localization\ISO639x1Enum;
/**
* Response abstract class.
*
@ -100,6 +102,10 @@ abstract class ResponseAbstract implements \JsonSerializable, MessageInterface
*/
public function getLanguage() : string
{
if (!isset($this->header)) {
return ISO639x1Enum::_EN;
}
return $this->header->l11n->getLanguage();
}

View File

@ -58,7 +58,7 @@ class Edge
* @var float
* @since 1.0.0
*/
private float $weight = 0.0;
private float $weight = 1.0;
/**
* Constructor.
@ -70,7 +70,7 @@ class Edge
*
* @since 1.0.0
*/
public function __construct(Node $node1, Node $node2, float $weight = 0.0, bool $isDirected = false)
public function __construct(Node $node1, Node $node2, float $weight = 1.0, bool $isDirected = false)
{
$this->node1 = $node1;
$this->node2 = $node2;

View File

@ -21,29 +21,6 @@ namespace phpOMS\Stdlib\Graph;
* @license OMS License 1.0
* @link https://orange-management.org
* @since 1.0.0
*
* @todo Orange-Management/phpOMS#10
* * Count all paths between 2 nodes
* * Return all paths between 2 nodes
* * Find cycles using graph coloring
* * Find a negative cycle
* * Find cycles with n length
* * Find cycles with odd length
* * Find shortest path between 2 nodes
* * Find longest path between 2 nodes
* * Find islands
* * Find all unreachable nodes
* * Check if strongly connected
* * Find longest path between 2 nodes
* * Find longest path
* * Get the girth
* * Get the circuit rank
* * Get the node connectivity
* * Get the edge connectivity
* * Is the graph connected
* * Get the unconnected nodes as their own graph
* * Check if bipartite
* * Check if triangle free
*/
class Graph
{
@ -511,15 +488,79 @@ class Graph
}
/**
* Perform depth first traversal.
* Perform depth first traversal
*
* @param int|string|Node $node1 Graph node
* @param int|string|Node $node2 Graph node
*
* @return array
*
* @since 1.0.0
*/
public function depthFirstTraversal() : array
private function depthFirstTraversal(
int | string | Node $node1,
int | string | Node $node2 = null,
array &$visited,
array &$path,
array &$paths
) : array
{
return [];
$visited[$node1->getId()] = true;
if ($node1->isEquals($node2)) {
$paths[] = $path;
}
$neighbors = $node1->getNeighbors();
foreach ($neighbors as $neighbor) {
if (!isset($visited[$neighbor->getId()]) || !$visited[$neighbor->getId()]) {
$path[] = $neighbor;
$this->depthFirstTraversal($neighbor, $node2, $visited, $path);
\array_pop($path);
}
}
$visited[$node1->getId()] = false;
}
/**
* Find all reachable nodes with depth first traversal (iterative)
*
* @param int|string|Node $node1 Graph node
*
* @return Node[]
*
* @since 1.0.0
*/
public function findAllReachableNodesDFS(int | string | Node $node1) : array
{
$nodes = [];
if (!($node1 instanceof Node)) {
$node1 = $this->getNode($node1);
}
$visited = [];
$stack = [];
$stack[] = $node1;
while (!empty($stack)) {
$cNode = \array_pop($stack);
$nodes[] = $cNode;
if (!isset($visited[$cNode->getId()]) || !$visited[$cNode->getId()]) {
$visited[$cNode->getId()] = true;
}
$neighbors = $cNode->getNeighbors();
foreach ($neighbors as $neighbor) {
if (!isset($visited[$cNode->getId()]) || !$visited[$cNode->getId()]) {
$stack[] = $neighbor;
}
}
}
return $nodes;
}
/**
@ -546,6 +587,54 @@ class Graph
return [];
}
/**
* Get all paths between two nodes
*
* @param int|string|Node $node1 Graph node
* @param int|string|Node $node2 Graph node
*
* @return array<int, Node[]>
*
* @since 1.0.0
*/
public function getAllPathsBetweenNodes(int | string | Node $node1, int | string | Node $node2) : array
{
if (!($node1 instanceof Node)) {
$node1 = $this->getNode($node1);
}
if (!($node2 instanceof Node)) {
$node2 = $this->getNode($node2);
}
if ($node1 === null || $node2 === null) {
return [];
}
$path = [];
$paths = [];
$visited = [];
$this->depthFirstTraversal($node1, $node2, $visited, $path, $paths);
return $paths;
}
/**
* Count possible paths between two nodes.
*
* @param int|string|Node $node1 Graph node
* @param int|string|Node $node2 Graph node
*
* @return int
*
* @since 1.0.0
*/
public function countAllPathsBetweenNodes(int | string | Node $node1, int | string | Node $node2) : int
{
return \count($this->getAllPathsBetweenNodes($node1, $node2));
}
/**
* Get longest path between two nodes.
*
@ -558,15 +647,48 @@ class Graph
*/
public function longestPathBetweenNodes(int | string | Node $node1, int | string | Node $node2) : array
{
if (!($node1 instanceof Node)) {
$node1 = $this->getNode($node1);
$paths = $this->getAllPathsBetweenNodes($node1, $node2);
if (empty($paths)) {
return [];
}
if (!($node2 instanceof Node)) {
$node2 = $this->getNode($node2);
foreach ($paths as $key => $path) {
$edges[$key] = 0;
foreach ($path as $node) {
$edges[$key] += $node->getEdgeByNeighbor()->getWeight();
}
}
return [];
\arsort($edges);
return $paths[\array_key_first($edges)];
}
/**
* Get shortest path between two nodes.
*
* @param int|string|Node $node1 Graph node
* @param int|string|Node $node2 Graph node
*
* @return Node[]
*
* @since 1.0.0
*/
public function shortestPathBetweenNodes(int | string | Node $node1, int | string | Node $node2) : array
{
$paths = $this->getAllPathsBetweenNodes($node1, $node2);
foreach ($paths as $key => $path) {
$edges[$key] = 0;
foreach ($path as $node) {
$edges[$key] += $node->getEdgeByNeighbor()->getWeight();
}
}
\asort($edges);
return $paths[\array_key_first($edges)];
}
/**
@ -684,21 +806,13 @@ class Graph
*/
public function isConnected() : bool
{
return true;
}
if (empty($this->nodes)) {
return true;
}
/**
* Get unconnected sub graphs
*
* @return Graph[]
*
* @since 1.0.0
*/
public function getUnconnected() : array
{
// get all unconnected sub graphs
$nodes = $this->findAllReachableNodesDFS(\reset($this->nodes));
return [];
return \count($nodes) === \count($this->nodes);
}
/**

View File

@ -172,6 +172,26 @@ class Node
return $this->edges[$key] ?? null;
}
/**
* Get graph edge by neighbor.
*
* @param Node $node Neighbor node
*
* @return null|Edge
*
* @since 1.0.0
*/
public function getEdgeByNeighbor(self $node) : ?Edge
{
foreach ($this->edges as $edge) {
if ($edge->getNode1()->isEqual($node) || $edge->getNode2()->isEqual($node)) {
return $edge;
}
}
return null;
}
/**
* Get graph edges
*

View File

@ -17,6 +17,7 @@ namespace phpOMS\tests\Message;
require_once __DIR__ . '/../Autoloader.php';
use phpOMS\Message\ResponseAbstract;
use phpOMS\Localization\ISO639x1Enum;
/**
* @testdox phpOMS\tests\Message\ResponseAbstractTest: Abstract response
@ -52,6 +53,7 @@ class ResponseAbstractTest extends \PHPUnit\Framework\TestCase
{
self::assertNull($this->response->get('asdf'));
self::assertEquals('', $this->response->getBody());
self::assertTrue(ISO639x1Enum::isValidValue($this->response->getLanguage()));
}
/**

View File

@ -35,7 +35,7 @@ class EdgeTest extends \PHPUnit\Framework\TestCase
self::assertEquals([new Node('1'), new Node('2')], $edge->getNodes());
self::assertTrue($edge->getNode1()->isEqual(new Node('1')));
self::assertTrue($edge->getNode2()->isEqual(new Node('2')));
self::assertEquals(0.0, $edge->getWeight());
self::assertEquals(1.0, $edge->getWeight());
self::assertFalse($edge->isDirected());
}
@ -57,8 +57,8 @@ class EdgeTest extends \PHPUnit\Framework\TestCase
*/
public function testWeightInputOutput() : void
{
$edge = new Edge(new Node('7'), new Node('8'), 1.0, true);
self::assertEquals(1.0, $edge->getWeight());
$edge = new Edge(new Node('7'), new Node('8'), 2.0, true);
self::assertEquals(2.0, $edge->getWeight());
$edge = new Edge(new Node('7'), new Node('8'), 1.0);
$edge->setWeight(3.0);

View File

@ -58,11 +58,8 @@ class GraphTest extends \PHPUnit\Framework\TestCase
self::assertEquals([], $this->graph->getBridges());
self::assertEquals([], $this->graph->getFloydWarshallShortestPath());
self::assertEquals([], $this->graph->getDijkstraShortestPath());
self::assertEquals([], $this->graph->depthFirstTraversal());
self::assertEquals([], $this->graph->breadthFirstTraversal());
self::assertEquals([], $this->graph->longestPath());
self::assertEquals([], $this->graph->longestPathBetweenNodes('invalid1', 'invalid2'));
self::assertEquals([], $this->graph->getUnconnected());
self::assertEquals(0, $this->graph->getCost());
self::assertEquals($this->graph, $this->graph->getKruskalMinimalSpanningTree());

View File

@ -101,6 +101,7 @@ class FileUtilsTest extends \PHPUnit\Framework\TestCase
self::assertEquals(\basename(__DIR__ . '/FileUtilsTest.php'), FileUtils::mb_pathinfo(__DIR__ . '/FileUtilsTest.php', \PATHINFO_BASENAME));
self::assertEquals('php', FileUtils::mb_pathinfo(__DIR__ . '/FileUtilsTest.php', \PATHINFO_EXTENSION));
self::assertEquals('FileUtilsTest', FileUtils::mb_pathinfo(__DIR__ . '/FileUtilsTest.php', \PATHINFO_FILENAME));
self::assertEquals(
[
'dirname' => __DIR__,
@ -108,7 +109,7 @@ class FileUtilsTest extends \PHPUnit\Framework\TestCase
'extension' => 'php',
'filename' => 'FileUtilsTest',
],
FileUtils::mb_pathinfo(__DIR__ . '/FileUtilsTest.php', \PATHINFO_FILENAME)
FileUtils::mb_pathinfo(__DIR__ . '/FileUtilsTest.php')
);
}
}