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) { 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) { 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) { if ($condValue['limit'] !== null) {
@ -433,7 +433,7 @@ class DataMapperAbstract implements DataMapperInterface
$hasAutocompletes = false; $hasAutocompletes = false;
foreach (static::$columns as $col) { foreach (static::$columns as $col) {
if (isset($col['autocomplete']) && $col['autocomplete']) { 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; $hasAutocompletes = true;
} }
} }

View File

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

View File

@ -58,7 +58,7 @@ class Edge
* @var float * @var float
* @since 1.0.0 * @since 1.0.0
*/ */
private float $weight = 0.0; private float $weight = 1.0;
/** /**
* Constructor. * Constructor.
@ -70,7 +70,7 @@ class Edge
* *
* @since 1.0.0 * @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->node1 = $node1;
$this->node2 = $node2; $this->node2 = $node2;

View File

@ -21,29 +21,6 @@ namespace phpOMS\Stdlib\Graph;
* @license OMS License 1.0 * @license OMS License 1.0
* @link https://orange-management.org * @link https://orange-management.org
* @since 1.0.0 * @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 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 * @return array
* *
* @since 1.0.0 * @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 []; 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. * Get longest path between two nodes.
* *
@ -558,15 +647,48 @@ class Graph
*/ */
public function longestPathBetweenNodes(int | string | Node $node1, int | string | Node $node2) : array public function longestPathBetweenNodes(int | string | Node $node1, int | string | Node $node2) : array
{ {
if (!($node1 instanceof Node)) { $paths = $this->getAllPathsBetweenNodes($node1, $node2);
$node1 = $this->getNode($node1);
if (empty($paths)) {
return [];
} }
if (!($node2 instanceof Node)) { foreach ($paths as $key => $path) {
$node2 = $this->getNode($node2); $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 public function isConnected() : bool
{ {
return true; if (empty($this->nodes)) {
} return true;
}
/** $nodes = $this->findAllReachableNodesDFS(\reset($this->nodes));
* Get unconnected sub graphs
*
* @return Graph[]
*
* @since 1.0.0
*/
public function getUnconnected() : array
{
// get all unconnected sub graphs
return []; return \count($nodes) === \count($this->nodes);
} }
/** /**

View File

@ -172,6 +172,26 @@ class Node
return $this->edges[$key] ?? null; 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 * Get graph edges
* *

View File

@ -17,6 +17,7 @@ namespace phpOMS\tests\Message;
require_once __DIR__ . '/../Autoloader.php'; require_once __DIR__ . '/../Autoloader.php';
use phpOMS\Message\ResponseAbstract; use phpOMS\Message\ResponseAbstract;
use phpOMS\Localization\ISO639x1Enum;
/** /**
* @testdox phpOMS\tests\Message\ResponseAbstractTest: Abstract response * @testdox phpOMS\tests\Message\ResponseAbstractTest: Abstract response
@ -52,6 +53,7 @@ class ResponseAbstractTest extends \PHPUnit\Framework\TestCase
{ {
self::assertNull($this->response->get('asdf')); self::assertNull($this->response->get('asdf'));
self::assertEquals('', $this->response->getBody()); 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::assertEquals([new Node('1'), new Node('2')], $edge->getNodes());
self::assertTrue($edge->getNode1()->isEqual(new Node('1'))); self::assertTrue($edge->getNode1()->isEqual(new Node('1')));
self::assertTrue($edge->getNode2()->isEqual(new Node('2'))); self::assertTrue($edge->getNode2()->isEqual(new Node('2')));
self::assertEquals(0.0, $edge->getWeight()); self::assertEquals(1.0, $edge->getWeight());
self::assertFalse($edge->isDirected()); self::assertFalse($edge->isDirected());
} }
@ -57,8 +57,8 @@ class EdgeTest extends \PHPUnit\Framework\TestCase
*/ */
public function testWeightInputOutput() : void public function testWeightInputOutput() : void
{ {
$edge = new Edge(new Node('7'), new Node('8'), 1.0, true); $edge = new Edge(new Node('7'), new Node('8'), 2.0, true);
self::assertEquals(1.0, $edge->getWeight()); self::assertEquals(2.0, $edge->getWeight());
$edge = new Edge(new Node('7'), new Node('8'), 1.0); $edge = new Edge(new Node('7'), new Node('8'), 1.0);
$edge->setWeight(3.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->getBridges());
self::assertEquals([], $this->graph->getFloydWarshallShortestPath()); self::assertEquals([], $this->graph->getFloydWarshallShortestPath());
self::assertEquals([], $this->graph->getDijkstraShortestPath()); self::assertEquals([], $this->graph->getDijkstraShortestPath());
self::assertEquals([], $this->graph->depthFirstTraversal());
self::assertEquals([], $this->graph->breadthFirstTraversal());
self::assertEquals([], $this->graph->longestPath()); self::assertEquals([], $this->graph->longestPath());
self::assertEquals([], $this->graph->longestPathBetweenNodes('invalid1', 'invalid2')); self::assertEquals([], $this->graph->longestPathBetweenNodes('invalid1', 'invalid2'));
self::assertEquals([], $this->graph->getUnconnected());
self::assertEquals(0, $this->graph->getCost()); self::assertEquals(0, $this->graph->getCost());
self::assertEquals($this->graph, $this->graph->getKruskalMinimalSpanningTree()); 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(\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('php', FileUtils::mb_pathinfo(__DIR__ . '/FileUtilsTest.php', \PATHINFO_EXTENSION));
self::assertEquals('FileUtilsTest', FileUtils::mb_pathinfo(__DIR__ . '/FileUtilsTest.php', \PATHINFO_FILENAME)); self::assertEquals('FileUtilsTest', FileUtils::mb_pathinfo(__DIR__ . '/FileUtilsTest.php', \PATHINFO_FILENAME));
self::assertEquals( self::assertEquals(
[ [
'dirname' => __DIR__, 'dirname' => __DIR__,
@ -108,7 +109,7 @@ class FileUtilsTest extends \PHPUnit\Framework\TestCase
'extension' => 'php', 'extension' => 'php',
'filename' => 'FileUtilsTest', 'filename' => 'FileUtilsTest',
], ],
FileUtils::mb_pathinfo(__DIR__ . '/FileUtilsTest.php', \PATHINFO_FILENAME) FileUtils::mb_pathinfo(__DIR__ . '/FileUtilsTest.php')
); );
} }
} }