diff --git a/README.md b/README.md index 13f12a82806c24fd5fc2c3660238664e201560da..0a65b8a9a0f26db964895f4743afe7cfe043e25e 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # ILIAS FormATest -* For ILIAS versions: 6.0 - 6.999 +* For ILIAS versions: 7.0 - 7.999 ## Installation Instructions 1. Clone this repository to /Customizing/global/plugins/Services/Repository/RepositoryObject/FormATest diff --git a/classes/class.ilFatQuestionFactory.php b/classes/class.ilFatQuestionFactory.php index c130659f34f4c3000e8cc0da2432ed5b3eeac7fe..1c3ece08b1ace21b57e9ba93c938c408b908204a 100644 --- a/classes/class.ilFatQuestionFactory.php +++ b/classes/class.ilFatQuestionFactory.php @@ -13,7 +13,6 @@ class ilFatQuestionFactory { $gui = assQuestion::instantiateQuestionGUI($obj_id); if ($gui->object instanceof assImagemapQuestion) { - require_once 'Customizing/global/plugins/Services/Repository/RepositoryObject/FormATest/classes/adapter/class.ilFatImageMapQuestionAdapter.php'; $object = new ilFatImageMapQuestionAdapter(); $object->loadFromDb($obj_id); diff --git a/classes/class.ilFormATestExportGUI.php b/classes/class.ilFormATestExportGUI.php index ecf6c4fd2ed01138104792307402b67cacb34f1e..24cc566f8c10ae008cff6b978e7d6878f8122aad 100755 --- a/classes/class.ilFormATestExportGUI.php +++ b/classes/class.ilFormATestExportGUI.php @@ -12,7 +12,6 @@ class ilFormATestExportGUI extends ilExportGUI */ protected function buildExportTableGUI() { - require_once 'tables/class.ilFormATestExportTableGUI.php'; $table = new ilFormATestExportTableGUI($this, 'listExportFiles', $this->obj); return $table; } diff --git a/classes/class.ilFormATestExporter.php b/classes/class.ilFormATestExporter.php index 69c65a401d57ec6bc1c68bf1540661d7288b348a..d40920555dd639ca7f34f602d46ee7751da8d0cc 100755 --- a/classes/class.ilFormATestExporter.php +++ b/classes/class.ilFormATestExporter.php @@ -1,8 +1,6 @@ diff --git a/classes/class.ilFormATestImporter.php b/classes/class.ilFormATestImporter.php index e263f7620861cd5b4e09e97b5a13fef1879256d7..747e2792807c602fd14ec74a9db3f4e1604b2926 100644 --- a/classes/class.ilFormATestImporter.php +++ b/classes/class.ilFormATestImporter.php @@ -1,9 +1,6 @@ diff --git a/classes/class.ilFormATestPlugin.php b/classes/class.ilFormATestPlugin.php index e82ce3a235fc07c66c7a346b6d55c3c7bdc905fe..51a3d64a1673ef42072d4e5318134c46822d8eb1 100644 --- a/classes/class.ilFormATestPlugin.php +++ b/classes/class.ilFormATestPlugin.php @@ -54,7 +54,6 @@ class ilFormATestPlugin extends ilRepositoryObjectPlugin parent::__construct(); if (isset($DIC['global_screen'])) { - require_once __DIR__ . '/providers/FormATestGlobalScreenToolsProvider.php'; $this->provider_collection->setToolProvider(new FormATestGlobalScreenToolsProvider($DIC, $this)); } } diff --git a/classes/class.ilObjFormATest.php b/classes/class.ilObjFormATest.php index 722795245d620722d527823221332020f33cc6c5..8122492747d956405c85279391bacc96afeed7dd 100644 --- a/classes/class.ilObjFormATest.php +++ b/classes/class.ilObjFormATest.php @@ -1,13 +1,6 @@ read(); $original_id = $set->getOriginalId(); diff --git a/classes/class.ilObjFormATestAccess.php b/classes/class.ilObjFormATestAccess.php index 9ef7de5f5f25411f9f590bcb845fe9104f3eb0ac..06057e33c6f87110880de78cb2ec7251867b6f6e 100644 --- a/classes/class.ilObjFormATestAccess.php +++ b/classes/class.ilObjFormATestAccess.php @@ -1,8 +1,6 @@ ui()->mainTemplate(); $ilDB = $DIC->database(); - require_once "./Customizing/global/plugins/Services/Repository/RepositoryObject/FormATest/classes/dispatcher/class.ilXTSFPluginDispatcher.php"; - $this->lng->loadLanguageModule('assessment'); $tpl->addCss('./Customizing/global/plugins/Services/Repository/RepositoryObject/FormATest/templates/default/xtsf.css'); @@ -104,7 +99,6 @@ class ilObjFormATestGUI extends ilObjectPluginGUI implements ilDesktopItemHandli $ilTabs->activateTab('properties'); // Has to be called at last, because ilMarkSchemaGUI sets the active tab to `settings` break; case 'ilformatestskilladministrationgui': - require_once 'controllers/class.ilFormATestSkillAdministrationGUI.php'; $this->prepareOutput(); $this->addHeaderAction(); @@ -115,10 +109,6 @@ class ilObjFormATestGUI extends ilObjectPluginGUI implements ilDesktopItemHandli $this->prepareOutput(); $this->addHeaderAction(); - require_once 'utils/class.ilFormATestSkillLevelHelper.php'; - require_once 'models/class.ilFormATestSession.php'; - require_once __DIR__ . '/class.ilFormATestSkillEvaluationGUI.php'; - $session = new ilFormATestSession($this->object); $session->read($this->object->getSetList()->getData()); @@ -175,7 +165,6 @@ class ilObjFormATestGUI extends ilObjectPluginGUI implements ilDesktopItemHandli $this->ctrl->forwardCommand($gui); break; case 'ilobjformatestquestionsetpoolgui': - require_once 'class.ilObjFormATestQuestionSetPoolGUI.php'; $gui = new ilObjFormATestQuestionSetPoolGUI((int) $_GET['ref_id']); $this->ctrl->forwardCommand($gui); break; diff --git a/classes/class.ilObjFormATestListGUI.php b/classes/class.ilObjFormATestListGUI.php index 8a75214e4b6054ddd7d2cb42d88ccf6d60db0577..d94fb7cbc01c58e560bf4e64d60227dc2deb3155 100644 --- a/classes/class.ilObjFormATestListGUI.php +++ b/classes/class.ilObjFormATestListGUI.php @@ -66,7 +66,6 @@ class ilObjFormATestListGUI extends ilObjectPluginListGUI $lng = $DIC->language(); $props = array(); - include_once "class.ilObjFormATestAccess.php"; if (!ilObjFormATestAccess::checkOnline($this->obj_id)) { $props[] = array("alert" => true, "property" => $lng->txt("status"), diff --git a/classes/class.ilObjFormATestQuestionSetPoolGUI.php b/classes/class.ilObjFormATestQuestionSetPoolGUI.php index 499fcb388ebe3e2b1020a2aea2b70476eaf996e5..5a9afe38c76e645265f3cbac7d4f3d91319953a6 100644 --- a/classes/class.ilObjFormATestQuestionSetPoolGUI.php +++ b/classes/class.ilObjFormATestQuestionSetPoolGUI.php @@ -1,8 +1,5 @@ tool() ->context() ->current(); - require_once __DIR__ . '/../providers/FormATestGlobalScreenToolsProvider.php'; $additionalDataExists = $toolContext->getAdditionalData()->exists(FormATestGlobalScreenToolsProvider::LEFT_NAV); if (false === $additionalDataExists) { $toolContext->addAdditionalData(FormATestGlobalScreenToolsProvider::LEFT_NAV, $left_template->get()); diff --git a/classes/controllers/class.ilFormATestParticipantsGUI.php b/classes/controllers/class.ilFormATestParticipantsGUI.php index aefa175435a39f456a21e1f8f06b3af3a54df6e2..acdca6043824be90328e1aa2d17e90e642ed9814 100644 --- a/classes/controllers/class.ilFormATestParticipantsGUI.php +++ b/classes/controllers/class.ilFormATestParticipantsGUI.php @@ -1,7 +1,5 @@ loadData(array( "test_id" => $this->form_a_test->getTestId() diff --git a/classes/models/class.ilFormATestQuestionSetPoolSetList.php b/classes/models/class.ilFormATestQuestionSetPoolSetList.php index 6e6cded7d22cc16cd091acbd6e3c290f3055b788..23c533285457530d0b90bd7abf7b1c5be274a3ed 100644 --- a/classes/models/class.ilFormATestQuestionSetPoolSetList.php +++ b/classes/models/class.ilFormATestQuestionSetPoolSetList.php @@ -1,7 +1,5 @@ loadData(array( "form_a_test" => $this->form_a_test->getId() @@ -47,8 +42,6 @@ class ilFormATestSetList extends ilXTSFModelList */ public function insert($set_ids, $do_clone = true) { - require_once "./Customizing/global/plugins/Services/Repository/RepositoryObject/QuestionSetPool/classes/class.ilObjQuestionSetPool.php"; - foreach ($set_ids as $set_id) { $set = new ilQuestionSetPoolSet($set_id); $set->read(); diff --git a/classes/providers/abstract.ilXTSFTableDatabaseDataProvider.php b/classes/providers/abstract.ilXTSFTableDatabaseDataProvider.php index 02750bd01c5d5853eff4e58b00b923e9b85ff22c..eb598874e46e3ed05b7c087658c8182cc7dcbbb9 100644 --- a/classes/providers/abstract.ilXTSFTableDatabaseDataProvider.php +++ b/classes/providers/abstract.ilXTSFTableDatabaseDataProvider.php @@ -1,7 +1,5 @@ =5.3", + "graphp/graphviz": "~0.2.0" + } +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000000000000000000000000000000000000..d94dc7587f3ecbb6999fd8a1c0f6132f233ee252 --- /dev/null +++ b/composer.lock @@ -0,0 +1,122 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "ca690f14dfdf057e95d3a24b02721814", + "packages": [ + { + "name": "clue/graph", + "version": "v0.9.2", + "source": { + "type": "git", + "url": "https://github.com/graphp/graph.git", + "reference": "187902e158dead96fc5da3ae368ea1080abf267d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/graphp/graph/zipball/187902e158dead96fc5da3ae368ea1080abf267d", + "reference": "187902e158dead96fc5da3ae368ea1080abf267d", + "shasum": "" + }, + "require": { + "php": ">=5.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35" + }, + "suggest": { + "graphp/algorithms": "Common graph algorithms, such as Dijkstra and Moore-Bellman-Ford (shortest path), minimum spanning tree (MST), Kruskal, Prim and many more..", + "graphp/graphviz": "GraphViz graph drawing / DOT output" + }, + "type": "library", + "autoload": { + "psr-4": { + "Fhaculty\\Graph\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering" + } + ], + "description": "GraPHP is the mathematical graph/network library written in PHP.", + "homepage": "https://github.com/graphp/graph", + "keywords": [ + "edge", + "graph", + "mathematical", + "network", + "vertex" + ], + "support": { + "issues": "https://github.com/graphp/graph/issues", + "source": "https://github.com/graphp/graph/tree/v0.9.2" + }, + "time": "2020-12-03T18:55:21+00:00" + }, + { + "name": "graphp/graphviz", + "version": "v0.2.2", + "source": { + "type": "git", + "url": "https://github.com/graphp/graphviz.git", + "reference": "5cc4466223ca46fffa196d1e762fae164319c229" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/graphp/graphviz/zipball/5cc4466223ca46fffa196d1e762fae164319c229", + "reference": "5cc4466223ca46fffa196d1e762fae164319c229", + "shasum": "" + }, + "require": { + "clue/graph": "~0.9.0|~0.8.0", + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.4 || ^5.7 || ^4.8.35" + }, + "type": "library", + "autoload": { + "psr-4": { + "Graphp\\GraphViz\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "GraphViz graph drawing for the mathematical graph/network library GraPHP.", + "homepage": "https://github.com/graphp/graphviz", + "keywords": [ + "dot output", + "graph drawing", + "graph image", + "graphp", + "graphviz" + ], + "support": { + "issues": "https://github.com/graphp/graphviz/issues", + "source": "https://github.com/graphp/graphviz/tree/v0.2.2" + }, + "time": "2019-10-04T13:30:55+00:00" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": ">=5.3" + }, + "platform-dev": [], + "plugin-api-version": "2.0.0" +} diff --git a/plugin.php b/plugin.php index 3c46f8129ae6ed735f2fb9471fbfea688ce9b9b3..4cafa3d8a16d792f3b1f95bc62643b0bba863e48 100644 --- a/plugin.php +++ b/plugin.php @@ -7,8 +7,8 @@ $version = '7.0.2'; // ilias min and max version; must always reflect the versions that should // run with the plugin -$ilias_min_version = '6.0'; -$ilias_max_version = '6.999'; +$ilias_min_version = '7.0'; +$ilias_max_version = '7.999'; // optional, but useful: Add one or more responsible persons and a contact email $responsible = 'Thomas Joussen / Michael Jansen'; diff --git a/vendor/autoload.php b/vendor/autoload.php new file mode 100644 index 0000000000000000000000000000000000000000..9eb7d30a3a5f964ab6d1705a46f2d475f8043479 --- /dev/null +++ b/vendor/autoload.php @@ -0,0 +1,7 @@ +getVertexFirst()` | + | `Walk::getVertexSource()` | `Walk::getVertices()->getVertexFirst()` | + | `Walk::getVertexTarget()` | `Walk::getVertices()->getVertexLast()` | + +## 0.7.1 (2014-03-12) + +* Fix: Throwing an `UnexpectedValueException` if writing GraphViz Dot script + to a temporary file fails and remove its debugging output + ([#77](https://github.com/clue/graph/issues/77) and [#78](https://github.com/clue/graph/issues/78) @Metabor) + +* Fix: Improved GraphViz support for MS Windows + ([#99](https://github.com/clue/graph/issues/99)) + +## 0.7.0 (2013-09-11) + +* Feature: Add new `Set\Vertices` and `Set\Edges` classes that handle common + operations on a Set of multiple `Vertex` and `Edge` instances respectively. + ([#48](https://github.com/clue/graph/issues/48)) + +* BC break: Move operations and their corresponding constants concerning Sets + to their corresponding Sets: + + | Old name | New name | + |---|---| + | `Edge\Base::getFirst()` | `Set\Edges::getEdgeOrder()` | + | `Edge\Base::getAll()` | `Set\Edges::getEdgesOrder()` | + | `Edge\Base::ORDER_*` | `Set\Edges::ORDER_*` | + |---|---| + | `Vertex::getFirst()` | `Set\Vertices::getVertexOrder()` | + | `Vertex::getAll()` | `Set\Vertices::getVerticesOrder()` | + | `Vertex::ORDER_` | `Set\Vertices::ORDER_*` | + +* BC break: Each `getVertices*()` and `getEdges*()` method now returns a `Set` + instead of a primitive array of instances. *Most* of the time this should + work without changing your code, because each `Set` implements an `Iterator` + interface and can easily be iterated using `foreach`. However, using a `Set` + instead of a plain array differs when checking its boolean value or + comparing two Sets. I.e. if you happen to want to check if an `Set` is empty, + you now have to use the more explicit syntax `$set->isEmpty()`. + +* BC break: `Vertex::getVertices()`, `Vertex::getVerticesEdgeTo()` and + `Vertex::getVerticesEdgeFrom()` now return a `Set\Vertices` instance that + may contain duplicate vertices if parallel (multiple) edges exist. Previously + there was no easy way to detect this situation - this is now the default. If + you also want to get unique / distinct `Vertex` instances, use + `Vertex::getVertices()->getVerticesDistinct()` where applicable. + +* BC break: Remove all occurances of `getVerticesId()`, use + `getVertices()->getIds()` instead. + +* BC break: Merge `Cycle` into `Walk` ([#61](https://github.com/clue/graph/issues/61)). + As such, its static factory methods had to be renamed. Update your references if applicable: + + | Old name | New name | + |---|---| + | `Cycle::factoryFromPredecessorMap()` | `Walk::factoryCycleFromPredecessorMap()` | + | `Cycle::factoryFromVertices()` | `Walk::factoryCycleFromVertices()` | + | `Cycle::factoryFromEdges()` | `Walk::factoryCycleFromEdges()` | + +* BC break: Remove `Graph::isEmpty()` because it's not well-defined and might + be confusing. Most literature suggests it should check for existing edges, + whereas the old behavior was to check for existing vertices instead. Use either + of the new and more transparent methods + `Algorithm\Property\GraphProperty::isNull()` (old behavior) or (where applicable) + `Algorithm\Property\GraphProperty::isEdgeless()` ([#63](https://github.com/clue/graph/issues/63)). + +* BC break: Each of the above methods (`Walk::factoryCycleFromPredecessorMap()`, + `Walk::factoryCycleFromVertices()`, `Walk::factoryCycleFromEdges()`) now + actually makes sure the returned `Walk` instance is actually a valid Cycle, + i.e. the start `Vertex` is the same as the end `Vertex` ([#61](https://github.com/clue/graph/issues/61)) + +* BC break: Each `Algorithm\ShortestPath` algorithm now consistenly does not + return a zero weight for the root Vertex and now supports loop edges on the root + Vertex ([#62](https://github.com/clue/graph/issues/62)) + +* BC break: Each `Algorithm\ShortestPath` algorithm now consistently throws an + `OutOfBoundsException` for unreachable vertices + ([#62](https://github.com/clue/graph/issues/62)) + +* BC break: A null Graph (a Graph with no Vertices and thus no Edges) is not a + valid tree (because it is not connected), adjust `Algorithm\Tree\Base::isTree()` + accordingly. + ([#72](https://github.com/clue/graph/issues/72)) + +* BC break: Remove all occurances of `getNumberOfVertices()` and + `getNumberOfEdges()` ([#75](https://github.com/clue/graph/issues/75) and + [#48](https://github.com/clue/graph/issues/48)): + + | Old name | New name | + |---|---| + | `$set->getNumberOfVertices()` | `count($set->getVertices())` | + | `$set->getNumberOfEdges()` | `count($set->getEdges())` | + +* BC break: Replace base `Set` class with `Set\DualAggregate` interface. This + is unlikely to affect you, but might potentially break your custom + inheritance or polymorphism for algorithms. + ([#75](https://github.com/clue/graph/issues/75)) + +* Feature: Add `Algorithm\ShortestPath\Base::hasVertex(Vertex $vertex)` to check whether + a path to the given Vertex exists ([#62](https://github.com/clue/graph/issues/62)). + +* Feature: Support opening GraphViz images on Mac OS X in default image viewer + ([#67](https://github.com/clue/graph/issues/67) @onigoetz) + +* Feature: Add `Algorithm\MinimumSpanningTree\Base::getWeight()` to get total + weight of resulting minimum spanning tree (MST). + ([#73](https://github.com/clue/graph/issues/73)) + +* Feature: Each `Algorithm\MinimumSpanningTree` algorithm now supports + undirected and mixed Graphs, as well as null weights for Edges. + ([#73](https://github.com/clue/graph/issues/73)) + +* BC break: Each `Algorithm\MinimumSpanningTree` algorithm now throws an + `UnexpectedValueException` for unconnected Graphs (and thus also null Graphs). + ([#73](https://github.com/clue/graph/issues/73)) + +* Feature: Add `Walk::factoryFromVertices()` + ([#64](https://github.com/clue/graph/issues/64)). + +* Fix: Checking `Walk::isValid()` + ([#61](https://github.com/clue/graph/issues/61)) + +* Fix: Missing import prevented + `Algorithm\ShortestPath\MooreBellmanFord::getCycleNegative()` from actually + throwing the right `UnderflowException` if no cycle was found + ([#62](https://github.com/clue/graph/issues/62)) + +* Fix: Calling `Exporter\Image::setFormat()` had no effect due to misassignment + ([#70](https://github.com/clue/graph/issues/70) @FGM) + +## 0.6.0 (2013-07-11) + +* BC break: Move algorithm definitions in base classes to separate algorithm classes ([#27](https://github.com/clue/graph/issues/27)). + The following methods containing algorithms were now moved to separate algorithm classes. This + change encourages code-reuse, simplifies spotting algorithms, helps reducing complexity, + improves testablity and avoids tight coupling. Update your references if applicable: + + | Old name | New name | Related ticket | + |---|---|---| + | `Set::getWeight()` | `Algorithm\Weight::getWeight()` | [#33](https://github.com/clue/graph/issues/33) | + | `Set::getWeightFlow()` | `Algorithm\Weight::getWeightFlow()` | [#33](https://github.com/clue/graph/issues/33) | + | `Set::getWeightMin()` | `Algorithm\Weight::getWeightMin()` | [#33](https://github.com/clue/graph/issues/33) | + | `Set::isWeighted()` | `Algorithm\Weight::isWeighted()` | [#33](https://github.com/clue/graph/issues/33) | + |-|-|-| + | `Graph::getDegree()` | `Algorithm\Degree::getDegree()` | [#29](https://github.com/clue/graph/issues/29) | + | `Graph::getDegreeMin()` | `Algorithm\Degree::getDegreeMin()` | [#29](https://github.com/clue/graph/issues/29) | + | `Graph::getDegreeMax()` | `Algorithm\Degree::getDegreeMax()` | [#29](https://github.com/clue/graph/issues/29) | + | `Graph::isRegular()` | `Algorithm\Degree::isRegular()` | [#29](https://github.com/clue/graph/issues/29) | + | `Graph::isBalanced()` | `Algorithm\Degree::isBalanced()` | [#29](https://github.com/clue/graph/issues/29) | + | `Vertex::getDegree()` | `Algorithm\Degree:getDegreeVertex()` | [#49](https://github.com/clue/graph/issues/49) | + | `Vertex::getDegreeIn()` | `Algorithm\Degree:getDegreeInVertex()` | [#49](https://github.com/clue/graph/issues/49) | + | `Vertex::getDegreeOut()` | `Algorithm\Degree:getDegreeOutVertex()` | [#49](https://github.com/clue/graph/issues/49) | + | `Vertex::isSink()` | `Algorithm\Degree:isVertexSink()` | [#49](https://github.com/clue/graph/issues/49) | + | `Vertex::isSource()` | `Algorithm\Degree:isVertexSource()` | [#49](https://github.com/clue/graph/issues/49) | + | `Vertex::isIsolated()` | `Algorithm\Degree::isVertexIsolated()` | [#49](https://github.com/clue/graph/issues/49) | + |-|-|-| + | `Set::isDirected()` | `Algorithm\Directed::isDirected()` | [#34](https://github.com/clue/graph/issues/34) | + |-|-|-| + | `Graph::isSymmetric()` | `Algorithm\Symmetric::isSymmetric()` | [#41](https://github.com/clue/graph/issues/41) | + |-|-|-| + | `Graph::isComplete()` | `Algorithm\Complete::isComplete()` | [#43](https://github.com/clue/graph/issues/43) | + |-|-|-| + | `Set::hasFlow()` | `Algorithm\Flow::hasFlow()` | [#47](https://github.com/clue/graph/issues/47) | + | `Graph::getBalance()` | `Algorithm\Flow::getBalance()` | [#30](https://github.com/clue/graph/issues/30), [#47](https://github.com/clue/graph/issues/47) | + | `Graph::isBalancedFlow()` | `Algorithm\Flow::isBalancedFlow()` | [#30](https://github.com/clue/graph/issues/39), [#47](https://github.com/clue/graph/issues/47) | + | `Vertex::getFlow()` | `Algorithm\Flow::getFlowVertex()` | [#47](https://github.com/clue/graph/issues/47) | + |-|-|-| + | `Vertex::isLeaf()` | `Algorithm\Tree\Undirected::isVertexLeaf()` | [#44](https://github.com/clue/graph/issues/44) | + |-|-|-| + | `Set::hasLoop()` | `Algorithm\Loop::hasLoop()` | [#51](https://github.com/clue/graph/issues/51) | + | `Vertex::hasLoop()` | `Algorithm\Loop::hasLoopVertex()` | [#51](https://github.com/clue/graph/issues/51) | + |-|-|-| + | `Set::hasEdgeParallel()` | `Algorithm\Parallel::hasEdgeParallel()` | [#52](https://github.com/clue/graph/issues/52) | + | `Edge\Base::hasEdgeParallel()` | `Algorithm\Parallel::hasEdgeParallelEdge()` | [#52](https://github.com/clue/graph/issues/52) | + | `Edge\Base::getEdgesParallel()` | `Algorithm\Parallel::getEdgeParallelEdge()` | [#52](https://github.com/clue/graph/issues/52) | + |-|-|-| + | `Graph::isEdgeless()` | `Algorithm\Property\GraphProperty::isEdgeless()` | [#54](https://github.com/clue/graph/issues/54) | + | `Graph::isTrivial()` | `Algorithm\Property\GraphProperty::isTrivial()` | [#54](https://github.com/clue/graph/issues/54) | + | `Walk::isCycle()` | `Algorithm\Property\WalkProperty::isCycle()` | [#54](https://github.com/clue/graph/issues/54) | + | `Walk::isPath()` | `Algorithm\Property\WalkProperty::isPath()` | [#54](https://github.com/clue/graph/issues/54) | + | `Walk::hasCycle()` | `Algorithm\Property\WalkProperty::hasCycle()` | [#54](https://github.com/clue/graph/issues/54) | + | `Walk::isLoop()` | `Algorithm\Property\WalkProperty::isLoop()` | [#54](https://github.com/clue/graph/issues/54) | + | `Walk::isDigon()` | `Algorithm\Property\WalkProperty::isDigon()` | [#54](https://github.com/clue/graph/issues/54) | + | `Walk::isTriangle()` | `Algorithm\Property\WalkProperty::isTriangle()` | [#54](https://github.com/clue/graph/issues/54) | + | `Walk::isSimple()` | `Algorithm\Property\WalkProperty::isSimple()` | [#54](https://github.com/clue/graph/issues/54) | + | `Walk::isHamiltonian()` | `Algorithm\Property\WalkProperty::isHamiltonian()` | [#54](https://github.com/clue/graph/issues/54) | + | `Walk::isEulerian()` | `Algorithm\Property\WalkProperty::isEulerian()` | [#54](https://github.com/clue/graph/issues/54) | + +* BC break: Remove unneeded algorithm alias definitions ([#31](https://github.com/clue/graph/issues/31), [#50](https://github.com/clue/graph/issues/50)). The following *alias definitions* + have been removed, their original/actual name has already existed before and continues to work + unchanged. Update your references if applicable: + + | Old/removed alias definition | Actual name | + |---|---| + | `Graph::isConnected()` | `Algorithm\ConnectedComponents::isSingle()` | + | `Graph::hasEulerianCycle()` | `Algorithm\Eulerian::hasCycle()` | + | `Graph::getNumberOfComponents()` | `Algorithm\ConnectedComponents::getNumberOfComponents()` | + | `Graph::getNumberOfGroups()` | `Algorithm\Groups::getNumberOfGroups()` | + | `Graph::isBipartit()` | `Algorithm\Bipartit::isBipartit()` | + | `Vertex::hasPathTo()` | `Algorithm\ShortestPath\BreadthFirst::hasVertex()` | + | `Vertex::hasPathFrom()` | `Algorithm\ShortestPath\BreadthFirst::hasVertex()` | + | `Vertex::getVerticesPathTo()` | `Algorithm\ShortestPath\BreadthFirst::getVertices()` | + | `Vertex::getVerticesPathFrom()` | `Algorithm\ShortestPath\BreadthFirst::getVertices()` | + +* BC break: `Graph::createVertices()` now returns an array of vertices instead of the + chainable `Graph` ([#19](https://github.com/clue/graph/issues/19)) + +* BC break: Move `Loader\UmlClassDiagram` to separate [fhaculty/graph-uml](https://github.com/fhaculty/graph-uml) + repo ([#38](https://github.com/clue/graph/issues/38)) + +* BC break: Remove needless `Algorithm\MinimumSpanningTree\PrimWithIf` + (use `Algorithm\MinimumSpanningTree\Prim` instead) + ([#45](https://github.com/clue/graph/issues/45)) + +* BC break: `Vertex::createEdgeTo()` now returns an instance of type + `Edge\Undirected` instead of `Edge\UndirectedId` + ([#46](https://github.com/clue/graph/issues/46)) + +* BC break: `Edge\Base::setCapacity()` now consistently throws an `RangeException` + instead of `InvalidArgumentException` if the current flow exceeds the new maximum + capacity ([#53](https://github.com/clue/graph/issues/53)) + +* Feature: New `Algorithm\Tree` namespace with algorithms for undirected and directed, + rooted trees ([#44](https://github.com/clue/graph/issues/44)) + +* Feature: According to be above list of moved algorithm methods, the following algorithm + classes have been added ([#27](https://github.com/clue/graph/issues/27)): + * New `Algorithm\Weight` ([#33](https://github.com/clue/graph/issues/33)) + * New `Algorithm\Degree` ([#29](https://github.com/clue/graph/issues/29), [#49](https://github.com/clue/graph/issues/49)) + * New `Algorithm\Directed` ([#34](https://github.com/clue/graph/issues/34)) + * New `Algorithm\Symmetric` ([#41](https://github.com/clue/graph/issues/41)) + * New `Algorithm\Complete` ([#43](https://github.com/clue/graph/issues/43)) + * New `Algorithm\Flow` ([#30](https://github.com/clue/graph/issues/30), [#47](https://github.com/clue/graph/issues/47)) + * New `Algorithm\Tree` ([#44](https://github.com/clue/graph/issues/44)) + * New `Algorithm\Loop` ([#51](https://github.com/clue/graph/issues/51)) + * New `Algorithm\Parallel` ([#52](https://github.com/clue/graph/issues/52)) + * New `Algorithm\Property` ([#54](https://github.com/clue/graph/issues/54)) + +* Feature: `Graph::createVertices()` now also accepts an array of vertex IDs + ([#19](https://github.com/clue/graph/issues/19)) + +* Feature: Add `Algorithm\Property\WalkProperty::hasLoop()` alias definition for + completeness ([#54](https://github.com/clue/graph/issues/54)) + +* Feature: Add `Algorithm\Property\WalkProperty::isCircuit()` definition to distinguish + circuits from cycles ([#54](https://github.com/clue/graph/issues/54)) + +* Fix: Checking hamiltonian cycles always returned false + ([#54](https://github.com/clue/graph/issues/54)) + +* Fix: A Walk with no edges is no longer considered a valid cycle + ([#54](https://github.com/clue/graph/issues/54)) + +* Fix: Various issues with `Vertex`/`Edge` layout attributes + ([#32](https://github.com/clue/graph/issues/32)) + +* Fix: Getting multiple parallel edges for undirected edges + ([#52](https://github.com/clue/graph/issues/52)) + +## 0.5.0 (2013-05-07) + +* First tagged release (See issue [#20](https://github.com/clue/graph/issues/20) for more info on why it starts as v0.5.0) diff --git a/vendor/clue/graph/LICENSE b/vendor/clue/graph/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..358d929ad811d9dc5a36e74c3b664ed3da1c56eb --- /dev/null +++ b/vendor/clue/graph/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2012+ Christian Lück (Maintainer) +Copyright (c) 2012+ Fhaculty Core Team and our awesome contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/clue/graph/README.md b/vendor/clue/graph/README.md new file mode 100644 index 0000000000000000000000000000000000000000..7a0d3d856a35d7d9a26e2aab73355c08630b1ee5 --- /dev/null +++ b/vendor/clue/graph/README.md @@ -0,0 +1,157 @@ +# graphp/graph [![Build Status](https://travis-ci.org/graphp/graph.png?branch=master)](https://travis-ci.org/graphp/graph) + +GraPHP is the mathematical graph/network library written in PHP. + +> You're viewing the contents of the `v0.9.x` release branch, note that active + development continues on another branch, see `master` branch for more details. + +**Table of contents** + +* [Quickstart examples](#quickstart-examples) +* [Features](#features) +* [Components](#components) + * [Graph drawing](#graph-drawing) + * [Common algorithms](#common-algorithms) +* [Install](#install) +* [Tests](#tests) +* [Contributing](#contributing) +* [License](#license) + +## Quickstart examples + +Once [installed](#install), let's initialize a sample graph: + +```php +createVertex('Rome'); +$madrid = $graph->createVertex('Madrid'); +$cologne = $graph->createVertex('Cologne'); + +// build some roads +$cologne->createEdgeTo($madrid); +$madrid->createEdgeTo($rome); +// create loop +$rome->createEdgeTo($rome); +``` + +Let's see which city (Vertex) has a road (i.e. an edge pointing) to Rome: + +```php +foreach ($rome->getVerticesEdgeFrom() as $vertex) { + echo $vertex->getId().' leads to rome'.PHP_EOL; + // result: Madrid and Rome itself +} +``` + +## Features + +This library is built around the concept of [mathematical graph theory](https://en.wikipedia.org/wiki/Graph_theory) (i.e. it is **not** a [charting](http://en.wikipedia.org/wiki/Chart) library for drawing a [graph of a function](http://en.wikipedia.org/wiki/Graph_of_a_function)). In essence, a graph is a set of *nodes* with any number of *connections* in between. In graph theory, [vertices](http://en.wikipedia.org/wiki/Vertex_%28graph_theory%29) (plural of vertex) are an abstract representation of these *nodes*, while *connections* are represented as *edges*. Edges may be either undirected ("two-way") or directed ("one-way", aka di-edges, arcs). + +Depending on how the edges are constructed, the whole graph can either be undirected, can be a [directed graph](http://en.wikipedia.org/wiki/Directed_graph) (aka digraph) or be a [mixed graph](https://en.wikipedia.org/wiki/Mixed_graph). Edges are also allowed to form [loops](http://en.wikipedia.org/wiki/Loop_%28graph_theory%29) (i.e. an edge from vertex A pointing to vertex A again). Also, [multiple edges](http://en.wikipedia.org/wiki/Multiple_edges) from vertex A to vertex B are supported as well (aka parallel edges), effectively forming a [multigraph](http://en.wikipedia.org/wiki/Multigraph) (aka pseudograph). And of course, any combination thereof is supported as well. While many authors try to differentiate between these core concepts, this library tries hard to not impose any artificial limitations or assumptions on your graphs. + +## Components + +This library provides the core data structures for working with graphs, its vertices, edges and attributes. + +There are several official components built on top of these structures to provide commonly needed functionality. +This architecture allows these components to be used independently and on demand only. + +Following is a list of some highlighted components. A list of all official components can be found in the [graphp project](https://github.com/graphp). + +### Graph drawing + +This library is built to support visualizing graph images, including them into webpages, opening up images from within CLI applications and exporting them as PNG, JPEG or SVG file formats (among many others). Because [graph drawing](http://en.wikipedia.org/wiki/Graph_drawing) is a complex area on its own, the actual layouting of the graph is left up to the excellent [GraphViz](http://www.graphviz.org/) "Graph Visualization Software" and we merely provide some convenient APIs to interface with GraphViz. + +See [graphp/graphviz](https://github.com/graphp/graphviz) for more details. + +### Common algorithms + +Besides graph drawing, one of the most common things to do with graphs is running algorithms to solve common graph problems. +Therefore this library is being used as the basis for implementations for a number of commonly used graph algorithms: + +* Search + * Deep first (DFS) + * Breadth first search (BFS) +* Shortest path + * [Dijkstra](https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm) + * Moore-Bellman-Ford (MBF) + * Counting number of hops (simple BFS) +* [Minimum spanning tree (MST)](https://en.wikipedia.org/wiki/Minimum_spanning_tree) + * Kruskal + * Prim +* [Traveling salesman problem (TSP)](https://en.wikipedia.org/wiki/Travelling_salesman_problem) + * Bruteforce algorithm + * Minimum spanning tree heuristic (TSP MST heuristic) + * Nearest neighbor heuristic (NN heuristic) +* Maximum flow + * [Edmonds-Karp](https://en.wikipedia.org/wiki/Edmonds%E2%80%93Karp_algorithm) +* Minimum cost flow (MCF) + * Cycle canceling + * Successive shortest path +* Maximum matching + * Flow algorithm + +See [graphp/algorithms](https://github.com/graphp/algorithms) for more details. + +## Install + +The recommended way to install this library is [through Composer](https://getcomposer.org). +[New to Composer?](https://getcomposer.org/doc/00-intro.md) + +This will install the latest supported version: + +```bash +$ composer require clue/graph:^0.9.2 +``` + +See also the [CHANGELOG](CHANGELOG.md) for details about version upgrades. + +This project aims to run on any platform and thus does not require any PHP +extensions and supports running on legacy PHP 5.3 through current PHP 8+ and +HHVM. +It's *highly recommended to use PHP 7+* for this project. + +You may also want to install some of the [additional components](#components). +A list of all official components can be found in the [graphp project](https://github.com/graphp). + +## Tests + +This library uses PHPUnit for its extensive test suite. +To run the test suite, you first need to clone this repo and then install all +dependencies [through Composer](https://getcomposer.org): + +```bash +$ composer install +``` + +To run the test suite, go to the project root and run: + +```bash +$ php vendor/bin/phpunit +``` + +## Contributing + +This library comes with an extensive test suite and is regularly tested and used in the *real world*. +Despite this, this library is still considered beta software and its API is subject to change. +The [changelog](CHANGELOG.md) lists all relevant information for updates between releases. + +If you encounter any issues, please don't hesitate to drop us a line, file a bug report or even best provide us with a patch / pull request and/or unit test to reproduce your problem. + +Besides directly working with the code, any additional documentation, additions to our readme or even fixing simple typos are appreciated just as well. + +Any feedback and/or contribution is welcome! + +Check out #graphp on irc.freenode.net. + +## License + +Released under the terms of the permissive [MIT license](http://opensource.org/licenses/MIT). diff --git a/vendor/clue/graph/composer.json b/vendor/clue/graph/composer.json new file mode 100644 index 0000000000000000000000000000000000000000..29ac918f6dadd4f9e60f21b0926de56a106eb4cb --- /dev/null +++ b/vendor/clue/graph/composer.json @@ -0,0 +1,40 @@ +{ + "name": "clue/graph", + "type": "library", + "description": "GraPHP is the mathematical graph/network library written in PHP.", + "keywords": [ + "graph", + "network", + "mathematical", + "vertex", + "edge" + ], + "homepage": "https://github.com/graphp/graph", + "license": "MIT", + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering" + } + ], + "autoload": { + "psr-4": { + "Fhaculty\\Graph\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Fhaculty\\Graph\\Tests\\": "tests/" + } + }, + "require": { + "php": ">=5.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35" + }, + "suggest": { + "graphp/graphviz": "GraphViz graph drawing / DOT output", + "graphp/algorithms": "Common graph algorithms, such as Dijkstra and Moore-Bellman-Ford (shortest path), minimum spanning tree (MST), Kruskal, Prim and many more.." + } +} diff --git a/vendor/clue/graph/src/Attribute/AttributeAware.php b/vendor/clue/graph/src/Attribute/AttributeAware.php new file mode 100644 index 0000000000000000000000000000000000000000..8c4bd283c674681fa22c1c339b1a5a5a91800aab --- /dev/null +++ b/vendor/clue/graph/src/Attribute/AttributeAware.php @@ -0,0 +1,35 @@ +attributes[$name]) ? $this->attributes[$name] : $default; + } + + /** + * set a single attribute with the given $name to given $value + * + * @param string $name + * @param mixed $value + * @return self For a fluid interface. + */ + public function setAttribute($name, $value) + { + $this->attributes[$name] = $value; + + return $this; + } + + /** + * get an array of all attributes + * + * @return array + */ + public function getAttributes() + { + return $this->attributes; + } + + /** + * set an array of additional attributes + * + * @param array $attributes + * @return self For a fluid interface. + */ + public function setAttributes(array $attributes) + { + $this->attributes = $attributes + $this->attributes; + + return $this; + } + + /** + * get a container for all attributes + * + * @return AttributeBag + */ + public function getAttributeBag() + { + return $this; + } +} diff --git a/vendor/clue/graph/src/Attribute/AttributeBagNamespaced.php b/vendor/clue/graph/src/Attribute/AttributeBagNamespaced.php new file mode 100644 index 0000000000000000000000000000000000000000..2e2341237e27596ac5790f16a58acaef73abc3c4 --- /dev/null +++ b/vendor/clue/graph/src/Attribute/AttributeBagNamespaced.php @@ -0,0 +1,116 @@ +getAttributeBag(); + } + $this->bag = $bag; + $this->prefix = $prefix; + } + + /** + * get a single attribute with the given $name (or return $default if attribute was not found) + * + * This prefixes the attribute name before requesting from the base bag. + * + * @param string $name + * @param mixed $default to return if attribute was not found + * @return mixed + */ + public function getAttribute($name, $default = null) + { + return $this->bag->getAttribute($this->prefix . $name, $default); + } + + /** + * set a single attribute with the given $name to given $value + * + * This prefixes the attribute name before setting in the base bag. + * + * @param string $name + * @param mixed $value + * @return void + */ + public function setAttribute($name, $value) + { + $this->bag->setAttribute($this->prefix . $name, $value); + } + + /** + * get an array of all attributes + * + * The prefix will not be included in the returned attribute keys. + * + * @return array + */ + public function getAttributes() + { + $attributes = array(); + $len = strlen($this->prefix); + + foreach ($this->bag->getAttributes() as $name => $value) { + if (strpos($name, $this->prefix) === 0) { + $attributes[substr($name, $len)] = $value; + } + } + + return $attributes; + } + + /** + * set an array of additional attributes + * + * Each attribute is prefixed before setting in the base bag. + * + * @param array $attributes + * @return void + */ + public function setAttributes(array $attributes) + { + foreach ($attributes as $name => $value) { + $this->bag->setAttribute($this->prefix . $name, $value); + } + } + + /** + * get a container for all attributes + * + * @return AttributeBag + */ + public function getAttributeBag() + { + return $this; + } +} diff --git a/vendor/clue/graph/src/Attribute/AttributeBagReference.php b/vendor/clue/graph/src/Attribute/AttributeBagReference.php new file mode 100644 index 0000000000000000000000000000000000000000..2aae63769fdc17dfe24aec9ee870f1ab06f7396e --- /dev/null +++ b/vendor/clue/graph/src/Attribute/AttributeBagReference.php @@ -0,0 +1,90 @@ +attributes =& $attributes; + } + + /** + * get a single attribute with the given $name (or return $default if attribute was not found) + * + * @param string $name + * @param mixed $default to return if attribute was not found + * @return mixed + */ + public function getAttribute($name, $default = null) + { + return isset($this->attributes[$name]) ? $this->attributes[$name] : $default; + } + + /** + * set a single attribute with the given $name to given $value + * + * @param string $name + * @param mixed $value + * @return self For a fluid interface. + */ + public function setAttribute($name, $value) + { + $this->attributes[$name] = $value; + + return $this; + } + + /** + * get an array of all attributes + * + * @return array + */ + public function getAttributes() + { + return $this->attributes; + } + + /** + * set an array of additional attributes + * + * @param array $attributes + * @return self For a fluid interface. + */ + public function setAttributes(array $attributes) + { + $this->attributes = $attributes + $this->attributes; + + return $this; + } + + /** + * get a container for all attributes + * + * @return AttributeBag + */ + public function getAttributeBag() + { + return $this; + } +} diff --git a/vendor/clue/graph/src/Edge/Base.php b/vendor/clue/graph/src/Edge/Base.php new file mode 100644 index 0000000000000000000000000000000000000000..a93a17cec59698f9a288a6ff1cb294706513e004 --- /dev/null +++ b/vendor/clue/graph/src/Edge/Base.php @@ -0,0 +1,308 @@ +weight; + } + + /** + * set new weight for edge + * + * @param float|int|NULL $weight new numeric weight of edge or NULL=unset weight + * @return self $this (chainable) + * @throws InvalidArgumentException if given weight is not numeric + */ + public function setWeight($weight) + { + if ($weight !== NULL && !is_float($weight) && !is_int($weight)) { + throw new InvalidArgumentException('Invalid weight given - must be numeric or NULL'); + } + $this->weight = $weight; + + return $this; + } + + /** + * get total capacity of this edge + * + * @return float|int|NULL numeric capacity or NULL=not set + */ + public function getCapacity() + { + return $this->capacity; + } + + /** + * get the capacity remaining (total capacity - current flow) + * + * @return float|int|NULL numeric capacity remaining or NULL=no upper capacity set + */ + public function getCapacityRemaining() + { + if ($this->capacity === NULL) { + return NULL; + } + + return $this->capacity - $this->flow; + } + + /** + * set new total capacity of this edge + * + * @param float|int|NULL $capacity + * @return self $this (chainable) + * @throws InvalidArgumentException if $capacity is invalid (not numeric or negative) + * @throws RangeException if current flow exceeds new capacity + */ + public function setCapacity($capacity) + { + if ($capacity !== NULL) { + if (!is_float($capacity) && !is_int($capacity)) { + throw new InvalidArgumentException('Invalid capacity given - must be numeric'); + } + if ($capacity < 0) { + throw new InvalidArgumentException('Capacity must not be negative'); + } + if ($this->flow !== NULL && $this->flow > $capacity) { + throw new RangeException('Current flow of ' . $this->flow . ' exceeds new capacity'); + } + } + $this->capacity = $capacity; + + return $this; + } + + /** + * get current flow (capacity currently in use) + * + * @return float|int|NULL numeric flow or NULL=not set + */ + public function getFlow() + { + return $this->flow; + } + + /** + * set new total flow (capacity currently in use) + * + * @param float|int|NULL $flow + * @return self $this (chainable) + * @throws InvalidArgumentException if $flow is invalid (not numeric or negative) + * @throws RangeException if flow exceeds current maximum capacity + */ + public function setFlow($flow) + { + if ($flow !== NULL) { + if (!is_float($flow) && !is_int($flow)) { + throw new InvalidArgumentException('Invalid flow given - must be numeric'); + } + if ($flow < 0) { + throw new InvalidArgumentException('Flow must not be negative'); + } + if ($this->capacity !== NULL && $flow > $this->capacity) { + throw new RangeException('New flow exceeds maximum capacity'); + } + } + $this->flow = $flow; + + return $this; + } + + /** + * get set of all Vertices this edge connects + * + * @return Vertices + */ + //abstract public function getVertices(); + + /** + * get graph instance this edge is attached to + * + * @return Graph + * @throws LogicException + */ + public function getGraph() + { + foreach ($this->getVertices() as $vertex) { + return $vertex->getGraph(); + + // the following code can only be reached if this edge does not + // contain any vertices (invalid state), so ignore its coverage + // @codeCoverageIgnoreStart + } + + throw new LogicException('Internal error: should not be reached'); + // @codeCoverageIgnoreEnd + } + + /** + * destroy edge and remove reference from vertices and graph + * + * @uses Graph::removeEdge() + * @uses Vertex::removeEdge() + * @return void + */ + public function destroy() + { + $this->getGraph()->removeEdge($this); + foreach ($this->getVertices() as $vertex) { + $vertex->removeEdge($this); + } + } + + /** + * create new clone of this edge between adjacent vertices + * + * @return self new edge + * @uses Graph::createEdgeClone() + */ + public function createEdgeClone() + { + return $this->getGraph()->createEdgeClone($this); + } + + /** + * create new clone of this edge inverted (in opposite direction) between adjacent vertices + * + * @return self new edge + * @uses Graph::createEdgeCloneInverted() + */ + public function createEdgeCloneInverted() + { + return $this->getGraph()->createEdgeCloneInverted($this); + } + + /** + * do NOT allow cloning of objects + * + * @throws BadMethodCallException + */ + private function __clone() + { + // @codeCoverageIgnoreStart + throw new BadMethodCallException(); + // @codeCoverageIgnoreEnd + } + + public function getAttribute($name, $default = null) + { + return isset($this->attributes[$name]) ? $this->attributes[$name] : $default; + } + + public function setAttribute($name, $value) + { + $this->attributes[$name] = $value; + } + + public function getAttributeBag() + { + return new AttributeBagReference($this->attributes); + } +} diff --git a/vendor/clue/graph/src/Edge/Directed.php b/vendor/clue/graph/src/Edge/Directed.php new file mode 100644 index 0000000000000000000000000000000000000000..8b8b49f760e11ec49afdb8bcebc65ea41ebc28fe --- /dev/null +++ b/vendor/clue/graph/src/Edge/Directed.php @@ -0,0 +1,119 @@ +getGraph() !== $to->getGraph()) { + throw new InvalidArgumentException('Vertices have to be within the same graph'); + } + + $this->from = $from; + $this->to = $to; + + $from->getGraph()->addEdge($this); + $from->addEdge($this); + $to->addEdge($this); + } + + public function getVerticesTarget() + { + return new Vertices(array($this->to)); + } + + public function getVerticesStart() + { + return new Vertices(array($this->from)); + } + + public function getVertices() + { + return new Vertices(array($this->from, $this->to)); + } + + /** + * get end/target vertex + * + * @return Vertex + */ + public function getVertexEnd() + { + return $this->to; + } + + /** + * get start vertex + * + * @return Vertex + */ + public function getVertexStart() + { + return $this->from; + } + + public function isConnection(Vertex $from, Vertex $to) + { + return ($this->to === $to && $this->from === $from); + } + + public function isLoop() + { + return ($this->to === $this->from); + } + + public function getVertexToFrom(Vertex $startVertex) + { + if ($this->from !== $startVertex) { + throw new InvalidArgumentException('Invalid start vertex'); + } + + return $this->to; + } + + public function getVertexFromTo(Vertex $endVertex) + { + if ($this->to !== $endVertex) { + throw new InvalidArgumentException('Invalid end vertex'); + } + + return $this->from; + } + + public function hasVertexStart(Vertex $startVertex) + { + return ($this->from === $startVertex); + } + + public function hasVertexTarget(Vertex $targetVertex) + { + return ($this->to === $targetVertex); + } +} diff --git a/vendor/clue/graph/src/Edge/Undirected.php b/vendor/clue/graph/src/Edge/Undirected.php new file mode 100644 index 0000000000000000000000000000000000000000..667903f38abb92a1565b17176d263fd326fe4a0e --- /dev/null +++ b/vendor/clue/graph/src/Edge/Undirected.php @@ -0,0 +1,104 @@ +getGraph() !== $b->getGraph()) { + throw new InvalidArgumentException('Vertices have to be within the same graph'); + } + + $this->a = $a; + $this->b = $b; + + $a->getGraph()->addEdge($this); + $a->addEdge($this); + $b->addEdge($this); + } + + public function getVerticesTarget() + { + return new Vertices(array($this->b, $this->a)); + } + + public function getVerticesStart() + { + return new Vertices(array($this->a, $this->b)); + } + + public function getVertices() + { + return new Vertices(array($this->a, $this->b)); + } + + public function isConnection(Vertex $from, Vertex $to) + { + // one way or other way + return (($this->a === $from && $this->b === $to) || ($this->b === $from && $this->a === $to)); + } + + public function isLoop() + { + return ($this->a === $this->b); + } + + public function getVertexToFrom(Vertex $startVertex) + { + if ($this->a === $startVertex) { + return $this->b; + } elseif ($this->b === $startVertex) { + return $this->a; + } else { + throw new InvalidArgumentException('Invalid start vertex'); + } + } + + public function getVertexFromTo(Vertex $endVertex) + { + if ($this->a === $endVertex) { + return $this->b; + } elseif ($this->b === $endVertex) { + return $this->a; + } else { + throw new InvalidArgumentException('Invalid end vertex'); + } + } + + public function hasVertexStart(Vertex $startVertex) + { + return ($this->a === $startVertex || $this->b === $startVertex); + } + + public function hasVertexTarget(Vertex $targetVertex) + { + // same implementation as direction does not matter + return $this->hasVertexStart($targetVertex); + } +} diff --git a/vendor/clue/graph/src/Exception.php b/vendor/clue/graph/src/Exception.php new file mode 100644 index 0000000000000000000000000000000000000000..70cc8cca0ccd3aed0f0d391062868dff9fe87d62 --- /dev/null +++ b/vendor/clue/graph/src/Exception.php @@ -0,0 +1,7 @@ +cycle = $cycle; + } + + /** + * + * @return Walk + */ + public function getCycle() + { + return $this->cycle; + } +} diff --git a/vendor/clue/graph/src/Exception/OutOfBoundsException.php b/vendor/clue/graph/src/Exception/OutOfBoundsException.php new file mode 100644 index 0000000000000000000000000000000000000000..25a76a70a21b85db8c409c3b344df5fd2a9a0287 --- /dev/null +++ b/vendor/clue/graph/src/Exception/OutOfBoundsException.php @@ -0,0 +1,9 @@ +vertices = VerticesMap::factoryArrayReference($this->verticesStorage); + $this->edges = Edges::factoryArrayReference($this->edgesStorage); + } + + /** + * return set of Vertices added to this graph + * + * @return Vertices + */ + public function getVertices() + { + return $this->vertices; + } + + /** + * return set of ALL Edges added to this graph + * + * @return Edges + */ + public function getEdges() + { + return $this->edges; + } + + /** + * create a new Vertex in the Graph + * + * @param int|NULL $id new vertex ID to use (defaults to NULL: use next free numeric ID) + * @param bool $returnDuplicate normal operation is to throw an exception if given id already exists. pass true to return original vertex instead + * @return Vertex (chainable) + * @throws InvalidArgumentException if given vertex $id is invalid + * @throws OverflowException if given vertex $id already exists and $returnDuplicate is not set + * @uses Vertex::getId() + */ + public function createVertex($id = NULL, $returnDuplicate = false) + { + // no ID given + if ($id === NULL) { + $id = $this->getNextId(); + } + if ($returnDuplicate && $this->vertices->hasVertexId($id)) { + return $this->vertices->getVertexId($id); + } + + return new Vertex($this, $id); + } + + /** + * create a new Vertex in this Graph from the given input Vertex of another graph + * + * @param Vertex $originalVertex + * @return Vertex new vertex in this graph + * @throws RuntimeException if vertex with this ID already exists + */ + public function createVertexClone(Vertex $originalVertex) + { + $id = $originalVertex->getId(); + if ($this->vertices->hasVertexId($id)) { + throw new RuntimeException('Id of cloned vertex already exists'); + } + $newVertex = new Vertex($this, $id); + // TODO: properly set attributes of vertex + $newVertex->getAttributeBag()->setAttributes($originalVertex->getAttributeBag()->getAttributes()); + $newVertex->setBalance($originalVertex->getBalance()); + $newVertex->setGroup($originalVertex->getGroup()); + + return $newVertex; + } + + /** + * create new clone/copy of this graph - copy all attributes and vertices, but do NOT copy edges + * + * using this method is faster than creating a new graph and calling createEdgeClone() yourself + * + * @return Graph + */ + public function createGraphCloneEdgeless() + { + $graph = new Graph(); + $graph->getAttributeBag()->setAttributes($this->getAttributeBag()->getAttributes()); + // TODO: set additional graph attributes + foreach ($this->getVertices() as $originalVertex) { + $vertex = $graph->createVertexClone($originalVertex); + // $graph->vertices[$vid] = $vertex; + } + + return $graph; + } + + /** + * create new clone/copy of this graph - copy all attributes and vertices. but only copy all given edges + * + * @param Edges|Edge[] $edges set or array of edges to be cloned + * @return Graph + * @uses Graph::createGraphCloneEdgeless() + * @uses Graph::createEdgeClone() for each edge to be cloned + */ + public function createGraphCloneEdges($edges) + { + $graph = $this->createGraphCloneEdgeless(); + foreach ($edges as $edge) { + $graph->createEdgeClone($edge); + } + + return $graph; + } + + /** + * create new clone/copy of this graph - copy all attributes, vertices and edges + * + * @return Graph + * @uses Graph::createGraphCloneEdges() to clone graph with current edges + */ + public function createGraphClone() + { + return $this->createGraphCloneEdges($this->edges); + } + + /** + * create a new clone/copy of this graph - copy all attributes and given vertices and its edges + * + * @param Vertices $vertices set of vertices to keep + * @return Graph + * @uses Graph::createGraphClone() to create a complete clone + * @uses Vertex::destroy() to remove unneeded vertices again + */ + public function createGraphCloneVertices($vertices) + { + $verticesKeep = Vertices::factory($vertices); + + $graph = $this->createGraphClone(); + foreach ($graph->getVertices()->getMap() as $vid => $vertex) { + if (!$verticesKeep->hasVertexId($vid)) { + $vertex->destroy(); + } + } + + return $graph; + } + + /** + * create new clone of the given edge between adjacent vertices + * + * @param Edge $originalEdge original edge (not neccessarily from this graph) + * @return Edge new edge in this graph + * @uses Graph::createEdgeCloneInternal() + */ + public function createEdgeClone(Edge $originalEdge) + { + return $this->createEdgeCloneInternal($originalEdge, 0, 1); + } + + /** + * create new clone of the given edge inverted (in opposite direction) between adjacent vertices + * + * @param Edge $originalEdge original edge (not neccessarily from this graph) + * @return Edge new edge in this graph + * @uses Graph::createEdgeCloneInternal() + */ + public function createEdgeCloneInverted(Edge $originalEdge) + { + return $this->createEdgeCloneInternal($originalEdge, 1, 0); + } + + /** + * create new clone of the given edge between adjacent vertices + * + * @param Edge $originalEdge original edge from old graph + * @param int $ia index of start vertex + * @param int $ib index of end vertex + * @return Edge new edge in this graph + * @uses Edge::getVertices() + * @uses Graph::getVertex() + * @uses Vertex::createEdge() to create a new undirected edge if given edge was undrected + * @uses Vertex::createEdgeTo() to create a new directed edge if given edge was directed + * @uses Edge::getWeight() + * @uses Edge::setWeight() + * @uses Edge::getFlow() + * @uses Edge::setFlow() + * @uses Edge::getCapacity() + * @uses Edge::setCapacity() + */ + private function createEdgeCloneInternal(Edge $originalEdge, $ia, $ib) + { + $ends = $originalEdge->getVertices()->getIds(); + + // get start vertex from old start vertex id + $a = $this->getVertex($ends[$ia]); + // get target vertex from old target vertex id + $b = $this->getVertex($ends[$ib]); + + if ($originalEdge instanceof EdgeDirected) { + $newEdge = $a->createEdgeTo($b); + } else { + // create new edge between new a and b + $newEdge = $a->createEdge($b); + } + // TODO: copy edge attributes + $newEdge->getAttributeBag()->setAttributes($originalEdge->getAttributeBag()->getAttributes()); + $newEdge->setWeight($originalEdge->getWeight()); + $newEdge->setFlow($originalEdge->getFlow()); + $newEdge->setCapacity($originalEdge->getCapacity()); + + return $newEdge; + } + + /** + * create the given number of vertices or given array of Vertex IDs + * + * @param int|array $n number of vertices to create or array of Vertex IDs to create + * @return Vertices set of Vertices created + * @uses Graph::getNextId() + */ + public function createVertices($n) + { + $vertices = array(); + if (is_int($n) && $n >= 0) { + for ($id = $this->getNextId(), $n += $id; $id < $n; ++$id) { + $vertices[$id] = new Vertex($this, $id); + } + } elseif (is_array($n)) { + // array given => check to make sure all given IDs are available (atomic operation) + foreach ($n as $id) { + if (!is_int($id) && !is_string($id)) { + throw new InvalidArgumentException('All Vertex IDs have to be of type integer or string'); + } elseif ($this->vertices->hasVertexId($id)) { + throw new OverflowException('Given array of Vertex IDs contains an ID that already exists. Given IDs must be unique'); + } elseif (isset($vertices[$id])) { + throw new InvalidArgumentException('Given array of Vertex IDs contain duplicate IDs. Given IDs must be unique'); + } + + // temporary marker to check for duplicate IDs in the array + $vertices[$id] = false; + } + + // actually create all requested vertices + foreach ($n as $id) { + $vertices[$id] = new Vertex($this, $id); + } + } else { + throw new InvalidArgumentException('Invalid number of vertices given. Must be non-negative integer or an array of Vertex IDs'); + } + + return new Vertices($vertices); + } + + /** + * get next free/unused/available vertex ID + * + * its guaranteed there's NO other vertex with a greater ID + * + * @return int + */ + private function getNextId() + { + if (!$this->verticesStorage) { + return 0; + } + + // auto ID + return max(array_keys($this->verticesStorage))+1; + } + + /** + * returns the Vertex with identifier $id + * + * @param int|string $id identifier of Vertex + * @return Vertex + * @throws OutOfBoundsException if given vertex ID does not exist + */ + public function getVertex($id) + { + return $this->vertices->getVertexId($id); + } + + /** + * checks whether given vertex ID exists in this graph + * + * @param int|string $id identifier of Vertex + * @return bool + */ + public function hasVertex($id) + { + return $this->vertices->hasVertexId($id); + } + + /** + * adds a new Vertex to the Graph (MUST NOT be called manually!) + * + * @param Vertex $vertex instance of the new Vertex + * @return void + * @internal + * @see self::createVertex() instead! + */ + public function addVertex(Vertex $vertex) + { + if (isset($this->verticesStorage[$vertex->getId()])) { + throw new OverflowException('ID must be unique'); + } + $this->verticesStorage[$vertex->getId()] = $vertex; + } + + /** + * adds a new Edge to the Graph (MUST NOT be called manually!) + * + * @param Edge $edge instance of the new Edge + * @return void + * @internal + * @see Vertex::createEdge() instead! + */ + public function addEdge(Edge $edge) + { + $this->edgesStorage []= $edge; + } + + /** + * remove the given edge from list of connected edges (MUST NOT be called manually!) + * + * @param Edge $edge + * @return void + * @throws InvalidArgumentException if given edge does not exist (should not ever happen) + * @internal + * @see Edge::destroy() instead! + */ + public function removeEdge(Edge $edge) + { + try { + unset($this->edgesStorage[$this->edges->getIndexEdge($edge)]); + } + catch (OutOfBoundsException $e) { + throw new InvalidArgumentException('Invalid Edge does not exist in this Graph'); + } + } + + /** + * remove the given vertex from list of known vertices (MUST NOT be called manually!) + * + * @param Vertex $vertex + * @return void + * @throws InvalidArgumentException if given vertex does not exist (should not ever happen) + * @internal + * @see Vertex::destroy() instead! + */ + public function removeVertex(Vertex $vertex) + { + try { + unset($this->verticesStorage[$this->vertices->getIndexVertex($vertex)]); + } + catch (OutOfBoundsException $e) { + throw new InvalidArgumentException('Invalid Vertex does not exist in this Graph'); + } + } + + /** + * Extracts edge from this graph + * + * @param Edge $edge + * @return Edge + * @throws UnderflowException if no edge was found + * @throws OverflowException if multiple edges match + */ + public function getEdgeClone(Edge $edge) + { + // Extract endpoints from edge + $vertices = $edge->getVertices()->getVector(); + + return $this->getEdgeCloneInternal($edge, $vertices[0], $vertices[1]); + } + + /** + * Extracts inverted edge from this graph + * + * @param Edge $edge + * @return Edge + * @throws UnderflowException if no edge was found + * @throws OverflowException if multiple edges match + */ + public function getEdgeCloneInverted(Edge $edge) + { + // Extract endpoints from edge + $vertices = $edge->getVertices()->getVector(); + + return $this->getEdgeCloneInternal($edge, $vertices[1], $vertices[0]); + } + + private function getEdgeCloneInternal(Edge $edge, Vertex $startVertex, Vertex $targetVertex) + { + // Get original vertices from resultgraph + $residualGraphEdgeStartVertex = $this->getVertex($startVertex->getId()); + $residualGraphEdgeTargetVertex = $this->getVertex($targetVertex->getId()); + + // Now get the edge + $residualEdgeArray = $residualGraphEdgeStartVertex->getEdgesTo($residualGraphEdgeTargetVertex); + $residualEdgeArray = Edges::factory($residualEdgeArray)->getVector(); + + // Check for parallel edges + if (!$residualEdgeArray) { + throw new UnderflowException('No original edges for given cloned edge found'); + } elseif (count($residualEdgeArray) !== 1) { + throw new OverflowException('More than one cloned edge? Parallel edges (multigraph) not supported'); + } + + return $residualEdgeArray[0]; + } + + /** + * do NOT allow cloning of objects (MUST NOT be called!) + * + * @throws BadMethodCallException + * @see Graph::createGraphClone() instead + */ + private function __clone() + { + // @codeCoverageIgnoreStart + throw new BadMethodCallException(); + // @codeCoverageIgnoreEnd + } + + public function getAttribute($name, $default = null) + { + return isset($this->attributes[$name]) ? $this->attributes[$name] : $default; + } + + public function setAttribute($name, $value) + { + $this->attributes[$name] = $value; + } + + public function getAttributeBag() + { + return new AttributeBagReference($this->attributes); + } +} diff --git a/vendor/clue/graph/src/Set/DualAggregate.php b/vendor/clue/graph/src/Set/DualAggregate.php new file mode 100644 index 0000000000000000000000000000000000000000..fd23ac8eaab243dfca0de25962e8714932f67b4b --- /dev/null +++ b/vendor/clue/graph/src/Set/DualAggregate.php @@ -0,0 +1,27 @@ +getEdges(); + } + return new self($edges); + } + + /** + * create new Edges instance that references the given source array of Edge instances + * + * Any changes in the referenced source array will automatically be + * reflected in this Set of Edges, e.g. if you add an Edge instance to the + * array, it will automatically be included in this Set. + * + * @param array $edgesArray + * @return Edges + */ + public static function factoryArrayReference(array &$edgesArray) + { + $edges = new static(); + $edges->edges =& $edgesArray; + return $edges; + } + + /** + * instantiate new Set of Edges + * + * @param array $edges + */ + public function __construct(array $edges = array()) + { + $this->edges = $edges; + } + + /** + * get array index for given Edge + * + * @param Edge $edge + * @throws OutOfBoundsException + * @return mixed + */ + public function getIndexEdge(Edge $edge) + { + $id = array_search($edge, $this->edges, true); + if ($id === false) { + throw new OutOfBoundsException('Given edge does NOT exist'); + } + return $id; + } + + /** + * return first Edge in this set of Edges + * + * some algorithms do not need a particular edge, but merely a (random) + * starting point. this is a convenience function to just pick the first + * edge from the list of known edges. + * + * @return Edge first Edge in this set of Edges + * @throws UnderflowException if set is empty + * @see self::getEdgeOrder() if you need to apply ordering first + */ + public function getEdgeFirst() + { + if (!$this->edges) { + throw new UnderflowException('Does not contain any edges'); + } + reset($this->edges); + + return current($this->edges); + } + + /** + * return last Edge in this set of Edges + * + * @return Edge last Edge in this set of Edges + * @throws UnderflowException if set is empty + */ + public function getEdgeLast() + { + if (!$this->edges) { + throw new UnderflowException('Does not contain any edges'); + } + end($this->edges); + + return current($this->edges); + } + + /** + * return Edge at given array index + * + * @param mixed $index + * @throws OutOfBoundsException if the given index does not exist + * @return Edge + */ + public function getEdgeIndex($index) + { + if (!isset($this->edges[$index])) { + throw new OutOfBoundsException('Invalid edge index'); + } + return $this->edges[$index]; + } + + /** + * return first Edge that matches the given callback filter function + * + * @param callable $callbackCheck + * @return Edge + * @throws UnderflowException if no Edge matches the given callback filter function + * @uses self::getEdgeMatchOrNull() + * @see self::getEdgesMatch() if you want to return *all* Edges that match + */ + public function getEdgeMatch($callbackCheck) + { + $ret = $this->getEdgeMatchOrNull($callbackCheck); + if ($ret === null) { + throw new UnderflowException('No edge found'); + } + return $ret; + } + + /** + * checks whethere there's an Edge that matches the given callback filter function + * + * @param callable $callbackCheck + * @return bool + * @see self::getEdgeMatch() to return the Edge instance that matches the given callback filter function + * @uses self::getEdgeMatchOrNull() + */ + public function hasEdgeMatch($callbackCheck) + { + return ($this->getEdgeMatchOrNull($callbackCheck) !== null); + } + + /** + * get a new set of Edges that match the given callback filter function + * + * This only keeps Edge elements if the $callbackCheck returns a bool + * true and filters out everything else. + * + * Edge index positions will be left unchanged. + * + * @param callable $callbackCheck + * @return Edges a new Edges instance + * @see self::getEdgeMatch() + */ + public function getEdgesMatch($callbackCheck) + { + return new static(array_filter($this->edges, $callbackCheck)); + } + + /** + * get new set of Edges ordered by given criterium $orderBy + * + * Edge index positions will be left unchanged. + * + * @param int $orderBy criterium to sort by. see self::ORDER_WEIGHT, etc. + * @param bool $desc whether to return biggest first (true) instead of smallest first (default:false) + * @return Edges a new Edges set ordered by the given $orderBy criterium + * @throws InvalidArgumentException if criterium is unknown + */ + public function getEdgesOrder($orderBy, $desc = false) + { + if ($orderBy === self::ORDER_RANDOM) { + // shuffle the edge positions + $keys = array_keys($this->edges); + shuffle($keys); + + // re-order according to shuffled edge positions + $edges = array(); + foreach ($keys as $key) { + $edges[$key] = $this->edges[$key]; + } + + // create iterator for shuffled array (no need to check DESC flag) + return new static($edges); + } + + $callback = $this->getCallback($orderBy); + $array = $this->edges; + + uasort($array, function (Edge $va, Edge $vb) use ($callback, $desc) { + $ra = $callback($desc ? $vb : $va); + $rb = $callback($desc ? $va : $vb); + + if ($ra < $rb) { + return -1; + } elseif ($ra > $rb) { + return 1; + } else { + return 0; + } + }); + + return new static($array); + } + + /** + * get first edge ordered by given criterium $orderBy + * + * @param int $orderBy criterium to sort by. see self::ORDER_WEIGHT, etc. + * @param bool $desc whether to return biggest (true) instead of smallest (default:false) + * @return Edge + * @throws InvalidArgumentException if criterium is unknown + * @throws UnderflowException if no edges exist + */ + public function getEdgeOrder($orderBy, $desc=false) + { + if (!$this->edges) { + throw new UnderflowException('No edge found'); + } + // random order + if ($orderBy === self::ORDER_RANDOM) { + // just return by random key (no need to check for DESC flag) + return $this->edges[array_rand($this->edges)]; + } + + $callback = $this->getCallback($orderBy); + + $ret = NULL; + $best = NULL; + foreach ($this->edges as $edge) { + $now = $callback($edge); + + if ($ret === NULL || ($desc && $now > $best) || (!$desc && $now < $best)) { + $ret = $edge; + $best = $now; + } + } + + return $ret; + } + + /** + * return self reference to Set of Edges + * + * @return Edges + * @see self::factory() + */ + public function getEdges() + { + return $this; + } + + /** + * get a new set of Edges where each Edge is distinct/unique + * + * @return Edges a new Edges instance + */ + public function getEdgesDistinct() + { + $edges = array(); + foreach ($this->edges as $edge) { + // filter duplicate edges + if (!in_array($edge, $edges, true)) { + $edges []= $edge; + } + } + + return new Edges($edges); + } + + /** + * get intersection of Edges with given other Edges + * + * The intersection contains all Edge instances that are present in BOTH + * this set of Edges and the given set of other Edges. + * + * Edge index/keys will be preserved from original array. + * + * Duplicate Edge instances will be kept if the corresponding number of + * Edge instances is also found in $otherEdges. + * + * @param Edges|Edge[] $otherEdges + * @return Edges a new Edges set + */ + public function getEdgesIntersection($otherEdges) + { + $otherArray = self::factory($otherEdges)->getVector(); + + $edges = array(); + foreach ($this->edges as $eid => $edge) { + $i = array_search($edge, $otherArray, true); + + if ($i !== false) { + // remove from other array in order to check for duplicate matches + unset($otherArray[$i]); + + $edges[$eid] = $edge; + } + } + + return new static($edges); + } + + /** + * return array of Edge instances + * + * @return Edge[] + */ + public function getVector() + { + return array_values($this->edges); + } + + /** + * count number of Edges + * + * @return int + * @see self::isEmpty() + */ + public function count() + { + return count($this->edges); + } + + /** + * check whether this Set of Edges is empty + * + * A Set if empty if no single Edge instance is added. This is faster + * than calling `count() === 0`. + * + * @return bool + */ + public function isEmpty() + { + return !$this->edges; + } + + /** + * get Iterator + * + * This method implements the IteratorAggregate interface and allows this + * Set of Edges to be used in foreach loops. + * + * @return \IteratorIterator + */ + public function getIterator() + { + return new \IteratorIterator(new \ArrayIterator($this->edges)); + } + + /** + * call given $callback on each Edge and sum their results + * + * @param callable $callback + * @return number + * @throws InvalidArgumentException for invalid callbacks + * @uses self::getCallback() + */ + public function getSumCallback($callback) + { + $callback = $this->getCallback($callback); + + // return array_sum(array_map($callback, $this->edges)); + + $sum = 0; + foreach ($this->edges as $edge) { + $sum += $callback($edge); + } + return $sum; + } + + private function getEdgeMatchOrNull($callbackCheck) + { + $callbackCheck = $this->getCallback($callbackCheck); + + foreach ($this->edges as $edge) { + if ($callbackCheck($edge)) { + return $edge; + } + } + return null; + } + + /** + * get callback/Closure to be called on Edge instances for given callback identifier + * + * @param callable|int $callback + * @throws InvalidArgumentException + * @return callable + */ + private function getCallback($callback) + { + if (is_callable($callback)) { + if (is_array($callback)) { + $callback = function (Edge $edge) use ($callback) { + return call_user_func($callback, $edge); + }; + } + return $callback; + } + + static $methods = array( + self::ORDER_WEIGHT => 'getWeight', + self::ORDER_CAPACITY => 'getCapacity', + self::ORDER_CAPACITY_REMAINING => 'getCapacityRemaining', + self::ORDER_FLOW => 'getFlow' + ); + + if (!is_int($callback) || !isset($methods[$callback])) { + throw new InvalidArgumentException('Invalid callback given'); + } + + $method = $methods[$callback]; + + return function (Edge $edge) use ($method) { + return $edge->$method(); + }; + } +} diff --git a/vendor/clue/graph/src/Set/EdgesAggregate.php b/vendor/clue/graph/src/Set/EdgesAggregate.php new file mode 100644 index 0000000000000000000000000000000000000000..19e898d2fcf0c10e39d729ca5aaf629a3e26df34 --- /dev/null +++ b/vendor/clue/graph/src/Set/EdgesAggregate.php @@ -0,0 +1,14 @@ +getVertices(); + } + return new self($vertices); + } + + /** + * create new Vertices instance that references the given source array of Vertex instances + * + * Any changes in the referenced source array will automatically be + * reflected in this Set of Vertices, e.g. if you add a Vertex instance to + * the array, it will automatically be included in this Set. + * + * @param array $verticesArray + * @return Vertices + */ + public static function factoryArrayReference(array &$verticesArray) + { + $vertices = new static(); + $vertices->vertices =& $verticesArray; + return $vertices; + } + + /** + * instantiate new Set of Vertices + * + * @param array $vertices + */ + public function __construct(array $vertices = array()) + { + $this->vertices = $vertices; + } + + /** + * get Vertex with the given vertex $id + * + * @param int|string $id + * @return Vertex + * @throws OutOfBoundsException if no Vertex with the given ID exists + * @uses self::getVertexMatch() + */ + public function getVertexId($id) + { + try { + return $this->getVertexMatch($this->getCallbackId($id)); + } + catch (UnderflowException $e) { + throw new OutOfBoundsException('Vertex ' . $id . ' does not exist', 0, $e); + } + } + + /** + * checks whether given vertex ID exists in this set of vertices + * + * @param int|string $id identifier of Vertex + * @return bool + * @uses self::hasVertexMatch() + */ + public function hasVertexId($id) + { + return $this->hasVertexMatch($this->getCallbackId($id)); + } + + /** + * get array index for given Vertex + * + * not every set of Vertices represents a map, as such array index and + * Vertex ID do not necessarily have to match. + * + * @param Vertex $vertex + * @throws OutOfBoundsException + * @return mixed + */ + public function getIndexVertex(Vertex $vertex) + { + $id = array_search($vertex, $this->vertices, true); + if ($id === false) { + throw new OutOfBoundsException('Given vertex does NOT exist'); + } + return $id; + } + + /** + * return first Vertex in this set of Vertices + * + * some algorithms do not need a particular vertex, but merely a (random) + * starting point. this is a convenience function to just pick the first + * vertex from the list of known vertices. + * + * @return Vertex first Vertex in this set of Vertices + * @throws UnderflowException if set is empty + * @see self::getVertexOrder() if you need to apply ordering first + */ + public function getVertexFirst() + { + if (!$this->vertices) { + throw new UnderflowException('Does not contain any vertices'); + } + reset($this->vertices); + + return current($this->vertices); + } + + /** + * return last Vertex in this set of Vertices + * + * @return Vertex last Vertex in this set of Vertices + * @throws UnderflowException if set is empty + */ + public function getVertexLast() + { + if (!$this->vertices) { + throw new UnderflowException('Does not contain any vertices'); + } + end($this->vertices); + + return current($this->vertices); + } + + /** + * return first Vertex that matches the given callback filter function + * + * @param callable $callbackCheck + * @return Vertex + * @throws UnderflowException if no Vertex matches the given callback filter function + * @uses self::getVertexMatchOrNull() + * @see self::getVerticesMatch() if you want to return *all* Vertices that match + */ + public function getVertexMatch($callbackCheck) + { + $ret = $this->getVertexMatchOrNull($callbackCheck); + if ($ret === null) { + throw new UnderflowException('No vertex found'); + } + return $ret; + } + + /** + * checks whether there's a Vertex that matches the given callback filter function + * + * @param callable $callbackCheck + * @return bool + * @see self::getVertexMatch() to return the Vertex instance that matches the given callback filter function + * @uses self::getVertexMatchOrNull() + */ + public function hasVertexMatch($callbackCheck) + { + return ($this->getVertexMatchOrNull($callbackCheck) !== null); + } + + /** + * get a new set of Vertices that match the given callback filter function + * + * This only keeps Vertex elements if the $callbackCheck returns a bool + * true and filters out everything else. + * + * Vertex index positions will be left unchanged, so if you call this method + * on a VerticesMap, it will also return a VerticesMap. + * + * @param callable $callbackCheck + * @return Vertices a new Vertices instance + * @see self::getVertexMatch() + */ + public function getVerticesMatch($callbackCheck) + { + return new static(array_filter($this->vertices, $callbackCheck)); + } + + /** + * get new Set of Vertices ordered by given criterium $orderBy + * + * Vertex index positions will be left unchanged, so if you call this method + * on a VerticesMap, it will also return a VerticesMap. + * + * @param int $orderBy criterium to sort by. see Vertex::ORDER_ID, etc. + * @param bool $desc whether to return biggest first (true) instead of smallest first (default:false) + * @return Vertices a new Vertices set ordered by the given $orderBy criterium + * @throws InvalidArgumentException if criterium is unknown + * @see self::getVertexOrder() + */ + public function getVerticesOrder($orderBy, $desc = false) + { + if ($orderBy === self::ORDER_RANDOM) { + // shuffle the vertex positions + $keys = array_keys($this->vertices); + shuffle($keys); + + // re-order according to shuffled vertex positions + $vertices = array(); + foreach ($keys as $key) { + $vertices[$key] = $this->vertices[$key]; + } + + // create iterator for shuffled array (no need to check DESC flag) + return new static($vertices); + } + + $callback = $this->getCallback($orderBy); + $array = $this->vertices; + + uasort($array, function (Vertex $va, Vertex $vb) use ($callback, $desc) { + $ra = $callback($desc ? $vb : $va); + $rb = $callback($desc ? $va : $vb); + + if ($ra < $rb) { + return -1; + } elseif ($ra > $rb) { + return 1; + } else { + return 0; + } + }); + + return new static($array); + } + + /** + * get intersection of Vertices with given other Vertices + * + * The intersection contains all Vertex instances that are present in BOTH + * this set of Vertices and the given set of other Vertices. + * + * Vertex index/keys will be preserved from original array. + * + * Duplicate Vertex instances will be kept if the corresponding number of + * Vertex instances is also found in $otherVertices. + * + * @param Vertices|Vertex[] $otherVertices + * @return Vertices a new Vertices set + */ + public function getVerticesIntersection($otherVertices) + { + $otherArray = self::factory($otherVertices)->getVector(); + + $vertices = array(); + foreach ($this->vertices as $vid => $vertex) { + $i = array_search($vertex, $otherArray, true); + + if ($i !== false) { + // remove from other array in order to check for duplicate matches + unset($otherArray[$i]); + + $vertices[$vid] = $vertex; + } + } + + return new static($vertices); + } + + /** + * get first vertex (optionally ordered by given criterium $by) from given array of vertices + * + * @param int $orderBy criterium to sort by. see Vertex::ORDER_ID, etc. + * @param bool $desc whether to return biggest (true) instead of smallest (default:false) + * @return Vertex + * @throws InvalidArgumentException if criterium is unknown + * @throws UnderflowException if no vertices exist + * @see self::getVerticesOrder() + */ + public function getVertexOrder($orderBy, $desc=false) + { + if (!$this->vertices) { + throw new UnderflowException('No vertex found'); + } + // random order + if ($orderBy === self::ORDER_RANDOM) { + // just return by random key (no need to check for DESC flag) + return $this->vertices[array_rand($this->vertices)]; + } + + $callback = $this->getCallback($orderBy); + + $ret = NULL; + $best = NULL; + foreach ($this->vertices as $vertex) { + $now = $callback($vertex); + + if ($ret === NULL || ($desc && $now > $best) || (!$desc && $now < $best)) { + $ret = $vertex; + $best = $now; + } + } + + return $ret; + } + + /** + * return self reference to Set of Vertices + * + * @return Vertices + * @see self::factory() + */ + public function getVertices() + { + return $this; + } + + /** + * get a new set of Vertices where each Vertex is distinct/unique + * + * @return VerticesMap a new VerticesMap instance + * @uses self::getMap() + */ + public function getVerticesDistinct() + { + return new VerticesMap($this->getMap()); + } + + /** + * get a mapping array of Vertex ID => Vertex instance and thus remove duplicate vertices + * + * @return Vertex[] Vertex ID => Vertex instance + * @uses Vertex::getId() + */ + public function getMap() + { + $vertices = array(); + foreach ($this->vertices as $vertex) { + $vertices[$vertex->getId()] = $vertex; + } + return $vertices; + } + + /** + * return array of Vertex IDs + * + * @return array + */ + public function getIds() + { + $ids = array(); + foreach ($this->vertices as $vertex) { + $ids []= $vertex->getId(); + } + return $ids; + } + + /** + * return array of Vertex instances + * + * @return Vertex[] + */ + public function getVector() + { + return array_values($this->vertices); + } + + /** + * count number of vertices + * + * @return int + * @see self::isEmpty() + */ + public function count() + { + return count($this->vertices); + } + + /** + * check whether this Set of Vertices is empty + * + * A Set if empty if no single Vertex instance is added. This is faster + * than calling `count() === 0`. + * + * @return bool + */ + public function isEmpty() + { + return !$this->vertices; + } + + /** + * check whether this set contains any duplicate vertex instances + * + * @return bool + * @uses self::getMap() + */ + public function hasDuplicates() + { + return (count($this->vertices) !== count($this->getMap())); + } + + /** + * get Iterator + * + * This method implements the IteratorAggregate interface and allows this + * Set of Vertices to be used in foreach loops. + * + * @return \IteratorIterator + */ + public function getIterator() + { + return new \IteratorIterator(new \ArrayIterator($this->vertices)); + } + + /** + * call given $callback on each Vertex and sum their results + * + * @param callable $callback + * @return number + * @throws InvalidArgumentException for invalid callbacks + * @uses self::getCallback() + */ + public function getSumCallback($callback) + { + $callback = $this->getCallback($callback); + + // return array_sum(array_map($callback, $this->vertices)); + + $sum = 0; + foreach ($this->vertices as $vertex) { + $sum += $callback($vertex); + } + return $sum; + } + + private function getCallbackId($id) + { + return function (Vertex $vertex) use ($id) { + return ($vertex->getId() == $id); + }; + } + + private function getVertexMatchOrNull($callbackCheck) + { + $callbackCheck = $this->getCallback($callbackCheck); + + foreach ($this->vertices as $vertex) { + if ($callbackCheck($vertex)) { + return $vertex; + } + } + return null; + } + + /** + * get callback/Closure to be called on Vertex instances for given callback identifier + * + * @param callable|int $callback + * @throws InvalidArgumentException + * @return callable + */ + private function getCallback($callback) + { + if (is_callable($callback)) { + if (is_array($callback)) { + $callback = function (Vertex $vertex) use ($callback) { + return call_user_func($callback, $vertex); + }; + } + return $callback; + } + + static $methods = array( + self::ORDER_ID => 'getId', + self::ORDER_GROUP => 'getGroup' + ); + + if (!is_int($callback) || !isset($methods[$callback])) { + throw new InvalidArgumentException('Invalid callback given'); + } + + $method = $methods[$callback]; + + return function (Vertex $vertex) use ($method) { + return $vertex->$method(); + }; + } +} diff --git a/vendor/clue/graph/src/Set/VerticesAggregate.php b/vendor/clue/graph/src/Set/VerticesAggregate.php new file mode 100644 index 0000000000000000000000000000000000000000..47a44283f0585cc591d72b60c0350b125feab382 --- /dev/null +++ b/vendor/clue/graph/src/Set/VerticesAggregate.php @@ -0,0 +1,14 @@ + Vertex instance mapping array + * + * Among others, using a mapped array significantly speeds up accessing vertices + * by ID. However, there's no way to store multiple vertices with the same ID + * (i.e. each Vertex ID has to be unique). + */ +class VerticesMap extends Vertices +{ + public function getMap() + { + return $this->vertices; + } + + public function getVertexId($id) + { + if (!isset($this->vertices[$id])) { + throw new OutOfBoundsException('Invalid vertex ID'); + } + return $this->vertices[$id]; + } + + public function hasVertexId($id) + { + return isset($this->vertices[$id]); + } + + public function getVerticesDistinct() + { + return $this; + } + + public function getIds() + { + return array_keys($this->vertices); + } + + public function getIndexVertex(Vertex $vertex) + { + $id = $vertex->getId(); + if (!isset($this->vertices[$id]) || $this->vertices[$id] !== $vertex) { + throw new OutOfBoundsException(); + } + return $id; + } + + /** + * + * @return VerticesMap + */ + public function getVertices() + { + return $this; + } + + public function hasDuplicates() + { + return false; + } +} diff --git a/vendor/clue/graph/src/Vertex.php b/vendor/clue/graph/src/Vertex.php new file mode 100644 index 0000000000000000000000000000000000000000..baaef2dead23d3863765e3826f4b8337dfc16340 --- /dev/null +++ b/vendor/clue/graph/src/Vertex.php @@ -0,0 +1,406 @@ +id = $id; + $this->graph = $graph; + + $graph->addVertex($this); + } + + /** + * get graph this vertex is attached to + * + * @return Graph + */ + public function getGraph() + { + return $this->graph; + } + + public function getBalance() + { + return $this->balance; + } + + public function setBalance($balance) + { + if ($balance !== NULL && !is_float($balance) && !is_int($balance)) { + throw new InvalidArgumentException('Invalid balance given - must be numeric'); + } + $this->balance = $balance; + + return $this; + } + + /** + * set group number of this vertex + * + * @param int $group + * @return Vertex $this (chainable) + * @throws InvalidArgumentException if group is not numeric + */ + public function setGroup($group) + { + if (!is_int($group)) { + throw new InvalidArgumentException('Invalid group number'); + } + $this->group = $group; + + return $this; + } + + /** + * get group number + * + * @return int + */ + public function getGroup() + { + return $this->group; + } + + /** + * returns id of this Vertex + * + * @return int|string + */ + public function getId() + { + return $this->id; + } + + /** + * create new directed edge from this start vertex to given target vertex + * + * @param Vertex $vertex target vertex + * @return EdgeDirected + * @throws InvalidArgumentException + * @uses Graph::addEdge() + */ + public function createEdgeTo(Vertex $vertex) + { + return new EdgeDirected($this, $vertex); + } + + /** + * add new undirected (bidirectional) edge between this vertex and given vertex + * + * @param Vertex $vertex + * @return EdgeUndirected + * @throws InvalidArgumentException + * @uses Graph::addEdge() + */ + public function createEdge(Vertex $vertex) + { + return new EdgeUndirected($this, $vertex); + } + + /** + * add the given edge to list of connected edges (MUST NOT be called manually) + * + * @param Edge $edge + * @return void + * @internal + * @see self::createEdge() instead! + */ + public function addEdge(Edge $edge) + { + $this->edges[] = $edge; + } + + /** + * remove the given edge from list of connected edges (MUST NOT be called manually) + * + * @param Edge $edge + * @return void + * @throws InvalidArgumentException if given edge does not exist + * @internal + * @see Edge::destroy() instead! + */ + public function removeEdge(Edge $edge) + { + $id = array_search($edge, $this->edges, true); + if ($id === false) { + throw new InvalidArgumentException('Given edge does NOT exist'); + } + unset($this->edges[$id]); + } + + /** + * check whether this vertex has a direct edge to given $vertex + * + * @param Vertex $vertex + * @return bool + * @uses Edge::hasVertexTarget() + */ + public function hasEdgeTo(Vertex $vertex) + { + $that = $this; + + return $this->getEdges()->hasEdgeMatch(function (Edge $edge) use ($that, $vertex) { + return $edge->isConnection($that, $vertex); + }); + } + + /** + * check whether the given vertex has a direct edge to THIS vertex + * + * @param Vertex $vertex + * @return bool + * @uses Vertex::hasEdgeTo() + */ + public function hasEdgeFrom(Vertex $vertex) + { + return $vertex->hasEdgeTo($this); + } + + /** + * get set of ALL Edges attached to this vertex + * + * @return Edges + */ + public function getEdges() + { + return new Edges($this->edges); + } + + /** + * get set of all outgoing Edges attached to this vertex + * + * @return Edges + */ + public function getEdgesOut() + { + $that = $this; + $prev = null; + + return $this->getEdges()->getEdgesMatch(function (Edge $edge) use ($that, &$prev) { + $ret = $edge->hasVertexStart($that); + + // skip duplicate directed loop edges + if ($edge === $prev && $edge instanceof EdgeDirected) { + $ret = false; + } + $prev = $edge; + + return $ret; + }); + } + + /** + * get set of all ingoing Edges attached to this vertex + * + * @return Edges + */ + public function getEdgesIn() + { + $that = $this; + $prev = null; + + return $this->getEdges()->getEdgesMatch(function (Edge $edge) use ($that, &$prev) { + $ret = $edge->hasVertexTarget($that); + + // skip duplicate directed loop edges + if ($edge === $prev && $edge instanceof EdgeDirected) { + $ret = false; + } + $prev = $edge; + + return $ret; + }); + } + + /** + * get set of Edges FROM this vertex TO the given vertex + * + * @param Vertex $vertex + * @return Edges + * @uses Edge::hasVertexTarget() + */ + public function getEdgesTo(Vertex $vertex) + { + $that = $this; + + return $this->getEdges()->getEdgesMatch(function (Edge $edge) use ($that, $vertex) { + return $edge->isConnection($that, $vertex); + }); + } + + /** + * get set of Edges FROM the given vertex TO this vertex + * + * @param Vertex $vertex + * @return Edges + * @uses Vertex::getEdgesTo() + */ + public function getEdgesFrom(Vertex $vertex) + { + return $vertex->getEdgesTo($this); + } + + /** + * get set of adjacent Vertices of this vertex (edge FROM or TO this vertex) + * + * If there are multiple parallel edges between the same Vertex, it will be + * returned several times in the resulting Set of Vertices. If you only + * want unique Vertex instances, use `getVerticesDistinct()`. + * + * @return Vertices + * @uses Edge::hasVertexStart() + * @uses Edge::getVerticesToFrom() + * @uses Edge::getVerticesFromTo() + */ + public function getVerticesEdge() + { + $ret = array(); + foreach ($this->edges as $edge) { + if ($edge->hasVertexStart($this)) { + $ret []= $edge->getVertexToFrom($this); + } else { + $ret []= $edge->getVertexFromTo($this); + } + } + + return new Vertices($ret); + } + + /** + * get set of all Vertices this vertex has an edge to + * + * If there are multiple parallel edges to the same Vertex, it will be + * returned several times in the resulting Set of Vertices. If you only + * want unique Vertex instances, use `getVerticesDistinct()`. + * + * @return Vertices + * @uses Vertex::getEdgesOut() + * @uses Edge::getVerticesToFrom() + */ + public function getVerticesEdgeTo() + { + $ret = array(); + foreach ($this->getEdgesOut() as $edge) { + $ret []= $edge->getVertexToFrom($this); + } + + return new Vertices($ret); + } + + /** + * get set of all Vertices that have an edge TO this vertex + * + * If there are multiple parallel edges from the same Vertex, it will be + * returned several times in the resulting Set of Vertices. If you only + * want unique Vertex instances, use `getVerticesDistinct()`. + * + * @return Vertices + * @uses Vertex::getEdgesIn() + * @uses Edge::getVerticesFromTo() + */ + public function getVerticesEdgeFrom() + { + $ret = array(); + foreach ($this->getEdgesIn() as $edge) { + $ret []= $edge->getVertexFromTo($this); + } + + return new Vertices($ret); + } + + /** + * destroy vertex and all edges connected to it and remove reference from graph + * + * @uses Edge::destroy() + * @uses Graph::removeVertex() + */ + public function destroy() + { + foreach ($this->getEdges()->getEdgesDistinct() as $edge) { + $edge->destroy(); + } + $this->graph->removeVertex($this); + } + + /** + * do NOT allow cloning of objects + * + * @throws BadMethodCallException + */ + private function __clone() + { + // @codeCoverageIgnoreStart + throw new BadMethodCallException(); + // @codeCoverageIgnoreEnd + } + + public function getAttribute($name, $default = null) + { + return isset($this->attributes[$name]) ? $this->attributes[$name] : $default; + } + + public function setAttribute($name, $value) + { + $this->attributes[$name] = $value; + } + + public function getAttributeBag() + { + return new AttributeBagReference($this->attributes); + } +} diff --git a/vendor/clue/graph/src/Walk.php b/vendor/clue/graph/src/Walk.php new file mode 100644 index 0000000000000000000000000000000000000000..363a19faa3d16752ce6078fe4466de4be5ba8411 --- /dev/null +++ b/vendor/clue/graph/src/Walk.php @@ -0,0 +1,311 @@ +getVertexToFrom($vertexCurrent); + $vertices []= $vertexCurrent; + } + + return new self($vertices, $edges); + } + + /** + * create new walk instance between given set of Vertices / array of Vertex instances + * + * @param Vertices|Vertex[] $vertices + * @param int|null $by + * @param bool $desc + * @return Walk + * @throws UnderflowException if no vertices were given + * @see Edges::getEdgeOrder() for parameters $by and $desc + */ + public static function factoryFromVertices($vertices, $by = null, $desc = false) + { + $edges = array(); + $last = NULL; + foreach ($vertices as $vertex) { + // skip first vertex as last is unknown + if ($last !== NULL) { + // pick edge between last vertex and this vertex + /* @var $last Vertex */ + if ($by === null) { + $edges []= $last->getEdgesTo($vertex)->getEdgeFirst(); + } else { + $edges []= $last->getEdgesTo($vertex)->getEdgeOrder($by, $desc); + } + } + $last = $vertex; + } + if ($last === NULL) { + throw new UnderflowException('No vertices given'); + } + + return new self($vertices, $edges); + } + + /** + * create new cycle instance from given predecessor map + * + * @param Vertex[] $predecessors map of vid => predecessor vertex instance + * @param Vertex $vertex start vertex to search predecessors from + * @param int|null $by + * @param bool $desc + * @return Walk + * @throws UnderflowException + * @see Edges::getEdgeOrder() for parameters $by and $desc + * @uses self::factoryFromVertices() + */ + public static function factoryCycleFromPredecessorMap(array $predecessors, Vertex $vertex, $by = null, $desc = false) + { + // find a vertex in the cycle + $vid = $vertex->getId(); + $startVertices = array(); + do { + if (!isset($predecessors[$vid])) { + throw new InvalidArgumentException('Predecessor map is incomplete and does not form a cycle'); + } + + $startVertices[$vid] = $vertex; + + $vertex = $predecessors[$vid]; + $vid = $vertex->getId(); + } while (!isset($startVertices[$vid])); + + // find negative cycle + $vid = $vertex->getId(); + // build array of vertices in cycle + $vertices = array(); + do { + // add new vertex to cycle + $vertices[$vid] = $vertex; + + // get predecessor of vertex + $vertex = $predecessors[$vid]; + $vid = $vertex->getId(); + // continue until we find a vertex that's already in the circle (i.e. circle is closed) + } while (!isset($vertices[$vid])); + + // reverse cycle, because cycle is actually built in opposite direction due to checking predecessors + $vertices = array_reverse($vertices, true); + + // additional edge from last vertex to first vertex + $vertices[] = reset($vertices); + + return self::factoryCycleFromVertices($vertices, $by, $desc); + } + + /** + * create new cycle instance with edges between given vertices + * + * @param Vertex[]|Vertices $vertices + * @param int|null $by + * @param bool $desc + * @return Walk + * @throws UnderflowException if no vertices were given + * @see Edges::getEdgeOrder() for parameters $by and $desc + * @uses self::factoryFromVertices() + */ + public static function factoryCycleFromVertices($vertices, $by = null, $desc = false) + { + $cycle = self::factoryFromVertices($vertices, $by, $desc); + + if ($cycle->getEdges()->isEmpty()) { + throw new InvalidArgumentException('Cycle with no edges can not exist'); + } + + if ($cycle->getVertices()->getVertexFirst() !== $cycle->getVertices()->getVertexLast()) { + throw new InvalidArgumentException('Cycle has to start and end at the same vertex'); + } + + return $cycle; + } + + /** + * create new cycle instance with vertices connected by given edges + * + * @param Edges|Edge[] $edges + * @param Vertex $startVertex + * @return Walk + * @throws InvalidArgumentException if the given array of edges does not represent a valid cycle + * @uses self::factoryFromEdges() + */ + public static function factoryCycleFromEdges($edges, Vertex $startVertex) + { + $cycle = self::factoryFromEdges($edges, $startVertex); + + // ensure this walk is actually a cycle by checking start = end + if ($cycle->getVertices()->getVertexLast() !== $startVertex) { + throw new InvalidArgumentException('The given array of edges does not represent a cycle'); + } + + return $cycle; + } + + /** + * + * @var Vertices + */ + protected $vertices; + + /** + * + * @var Edges + */ + protected $edges; + + protected function __construct($vertices, $edges) + { + $this->vertices = Vertices::factory($vertices); + $this->edges = Edges::factory($edges); + } + + /** + * return original graph + * + * @return Graph + * @uses self::getVertices() + * @uses Vertices::getVertexFirst() + * @uses Vertex::getGraph() + */ + public function getGraph() + { + return $this->getVertices()->getVertexFirst()->getGraph(); + } + + /** + * create new graph clone with only vertices and edges actually in the walk + * + * do not add duplicate vertices and edges for loops and intersections, etc. + * + * @return Graph + * @uses Walk::getEdges() + * @uses Graph::createGraphCloneEdges() + */ + public function createGraph() + { + // create new graph clone with only edges of walk + $graph = $this->getGraph()->createGraphCloneEdges($this->getEdges()); + $vertices = $this->getVertices()->getMap(); + // get all vertices + foreach ($graph->getVertices()->getMap() as $vid => $vertex) { + if (!isset($vertices[$vid])) { + // remove those not present in the walk (isolated vertices, etc.) + $vertex->destroy(); + } + } + + return $graph; + } + + /** + * return set of all Edges of walk (in sequence visited in walk, may contain duplicates) + * + * If you need to return set a of all unique Edges of walk, use + * `Walk::getEdges()->getEdgesDistinct()` instead. + * + * @return Edges + */ + public function getEdges() + { + return $this->edges; + } + + /** + * return set of all Vertices of walk (in sequence visited in walk, may contain duplicates) + * + * If you need to return set a of all unique Vertices of walk, use + * `Walk::getVertices()->getVerticesDistinct()` instead. + * + * If you need to return the source vertex (first vertex of walk), use + * `Walk::getVertices()->getVertexFirst()` instead. + * + * If you need to return the target/destination vertex (last vertex of walk), use + * `Walk::getVertices()->getVertexLast()` instead. + * + * @return Vertices + */ + public function getVertices() + { + return $this->vertices; + } + + /** + * get alternating sequence of vertex, edge, vertex, edge, ..., vertex + * + * @return array + */ + public function getAlternatingSequence() + { + $edges = $this->edges->getVector(); + $vertices = $this->vertices->getVector(); + + $ret = array(); + for ($i = 0, $l = count($this->edges); $i < $l; ++$i) { + $ret []= $vertices[$i]; + $ret []= $edges[$i]; + } + $ret[] = $vertices[$i]; + + return $ret; + } + + /** + * check to make sure this walk is still valid (i.e. source graph still contains all vertices and edges) + * + * @return bool + * @uses Walk::getGraph() + * @uses Graph::getVertices() + * @uses Graph::getEdges() + */ + public function isValid() + { + $vertices = $this->getGraph()->getVertices()->getMap(); + // check source graph contains all vertices + foreach ($this->getVertices()->getMap() as $vid => $vertex) { + // make sure vertex ID exists and has not been replaced + if (!isset($vertices[$vid]) || $vertices[$vid] !== $vertex) { + return false; + } + } + $edges = $this->getGraph()->getEdges()->getVector(); + // check source graph contains all edges + foreach ($this->edges as $edge) { + if (!in_array($edge, $edges, true)) { + return false; + } + } + + return true; + } +} diff --git a/vendor/composer/ClassLoader.php b/vendor/composer/ClassLoader.php new file mode 100644 index 0000000000000000000000000000000000000000..247294d66ee04633486c9da28b94241e1f7c4c31 --- /dev/null +++ b/vendor/composer/ClassLoader.php @@ -0,0 +1,479 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Autoload; + +/** + * ClassLoader implements a PSR-0, PSR-4 and classmap class loader. + * + * $loader = new \Composer\Autoload\ClassLoader(); + * + * // register classes with namespaces + * $loader->add('Symfony\Component', __DIR__.'/component'); + * $loader->add('Symfony', __DIR__.'/framework'); + * + * // activate the autoloader + * $loader->register(); + * + * // to enable searching the include path (eg. for PEAR packages) + * $loader->setUseIncludePath(true); + * + * In this example, if you try to use a class in the Symfony\Component + * namespace or one of its children (Symfony\Component\Console for instance), + * the autoloader will first look for the class under the component/ + * directory, and it will then fallback to the framework/ directory if not + * found before giving up. + * + * This class is loosely based on the Symfony UniversalClassLoader. + * + * @author Fabien Potencier + * @author Jordi Boggiano + * @see https://www.php-fig.org/psr/psr-0/ + * @see https://www.php-fig.org/psr/psr-4/ + */ +class ClassLoader +{ + private $vendorDir; + + // PSR-4 + private $prefixLengthsPsr4 = array(); + private $prefixDirsPsr4 = array(); + private $fallbackDirsPsr4 = array(); + + // PSR-0 + private $prefixesPsr0 = array(); + private $fallbackDirsPsr0 = array(); + + private $useIncludePath = false; + private $classMap = array(); + private $classMapAuthoritative = false; + private $missingClasses = array(); + private $apcuPrefix; + + private static $registeredLoaders = array(); + + public function __construct($vendorDir = null) + { + $this->vendorDir = $vendorDir; + } + + public function getPrefixes() + { + if (!empty($this->prefixesPsr0)) { + return call_user_func_array('array_merge', array_values($this->prefixesPsr0)); + } + + return array(); + } + + public function getPrefixesPsr4() + { + return $this->prefixDirsPsr4; + } + + public function getFallbackDirs() + { + return $this->fallbackDirsPsr0; + } + + public function getFallbackDirsPsr4() + { + return $this->fallbackDirsPsr4; + } + + public function getClassMap() + { + return $this->classMap; + } + + /** + * @param array $classMap Class to filename map + */ + public function addClassMap(array $classMap) + { + if ($this->classMap) { + $this->classMap = array_merge($this->classMap, $classMap); + } else { + $this->classMap = $classMap; + } + } + + /** + * Registers a set of PSR-0 directories for a given prefix, either + * appending or prepending to the ones previously set for this prefix. + * + * @param string $prefix The prefix + * @param array|string $paths The PSR-0 root directories + * @param bool $prepend Whether to prepend the directories + */ + public function add($prefix, $paths, $prepend = false) + { + if (!$prefix) { + if ($prepend) { + $this->fallbackDirsPsr0 = array_merge( + (array) $paths, + $this->fallbackDirsPsr0 + ); + } else { + $this->fallbackDirsPsr0 = array_merge( + $this->fallbackDirsPsr0, + (array) $paths + ); + } + + return; + } + + $first = $prefix[0]; + if (!isset($this->prefixesPsr0[$first][$prefix])) { + $this->prefixesPsr0[$first][$prefix] = (array) $paths; + + return; + } + if ($prepend) { + $this->prefixesPsr0[$first][$prefix] = array_merge( + (array) $paths, + $this->prefixesPsr0[$first][$prefix] + ); + } else { + $this->prefixesPsr0[$first][$prefix] = array_merge( + $this->prefixesPsr0[$first][$prefix], + (array) $paths + ); + } + } + + /** + * Registers a set of PSR-4 directories for a given namespace, either + * appending or prepending to the ones previously set for this namespace. + * + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param array|string $paths The PSR-4 base directories + * @param bool $prepend Whether to prepend the directories + * + * @throws \InvalidArgumentException + */ + public function addPsr4($prefix, $paths, $prepend = false) + { + if (!$prefix) { + // Register directories for the root namespace. + if ($prepend) { + $this->fallbackDirsPsr4 = array_merge( + (array) $paths, + $this->fallbackDirsPsr4 + ); + } else { + $this->fallbackDirsPsr4 = array_merge( + $this->fallbackDirsPsr4, + (array) $paths + ); + } + } elseif (!isset($this->prefixDirsPsr4[$prefix])) { + // Register directories for a new namespace. + $length = strlen($prefix); + if ('\\' !== $prefix[$length - 1]) { + throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); + } + $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; + $this->prefixDirsPsr4[$prefix] = (array) $paths; + } elseif ($prepend) { + // Prepend directories for an already registered namespace. + $this->prefixDirsPsr4[$prefix] = array_merge( + (array) $paths, + $this->prefixDirsPsr4[$prefix] + ); + } else { + // Append directories for an already registered namespace. + $this->prefixDirsPsr4[$prefix] = array_merge( + $this->prefixDirsPsr4[$prefix], + (array) $paths + ); + } + } + + /** + * Registers a set of PSR-0 directories for a given prefix, + * replacing any others previously set for this prefix. + * + * @param string $prefix The prefix + * @param array|string $paths The PSR-0 base directories + */ + public function set($prefix, $paths) + { + if (!$prefix) { + $this->fallbackDirsPsr0 = (array) $paths; + } else { + $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths; + } + } + + /** + * Registers a set of PSR-4 directories for a given namespace, + * replacing any others previously set for this namespace. + * + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param array|string $paths The PSR-4 base directories + * + * @throws \InvalidArgumentException + */ + public function setPsr4($prefix, $paths) + { + if (!$prefix) { + $this->fallbackDirsPsr4 = (array) $paths; + } else { + $length = strlen($prefix); + if ('\\' !== $prefix[$length - 1]) { + throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); + } + $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; + $this->prefixDirsPsr4[$prefix] = (array) $paths; + } + } + + /** + * Turns on searching the include path for class files. + * + * @param bool $useIncludePath + */ + public function setUseIncludePath($useIncludePath) + { + $this->useIncludePath = $useIncludePath; + } + + /** + * Can be used to check if the autoloader uses the include path to check + * for classes. + * + * @return bool + */ + public function getUseIncludePath() + { + return $this->useIncludePath; + } + + /** + * Turns off searching the prefix and fallback directories for classes + * that have not been registered with the class map. + * + * @param bool $classMapAuthoritative + */ + public function setClassMapAuthoritative($classMapAuthoritative) + { + $this->classMapAuthoritative = $classMapAuthoritative; + } + + /** + * Should class lookup fail if not found in the current class map? + * + * @return bool + */ + public function isClassMapAuthoritative() + { + return $this->classMapAuthoritative; + } + + /** + * APCu prefix to use to cache found/not-found classes, if the extension is enabled. + * + * @param string|null $apcuPrefix + */ + public function setApcuPrefix($apcuPrefix) + { + $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null; + } + + /** + * The APCu prefix in use, or null if APCu caching is not enabled. + * + * @return string|null + */ + public function getApcuPrefix() + { + return $this->apcuPrefix; + } + + /** + * Registers this instance as an autoloader. + * + * @param bool $prepend Whether to prepend the autoloader or not + */ + public function register($prepend = false) + { + spl_autoload_register(array($this, 'loadClass'), true, $prepend); + + if (null === $this->vendorDir) { + return; + } + + if ($prepend) { + self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders; + } else { + unset(self::$registeredLoaders[$this->vendorDir]); + self::$registeredLoaders[$this->vendorDir] = $this; + } + } + + /** + * Unregisters this instance as an autoloader. + */ + public function unregister() + { + spl_autoload_unregister(array($this, 'loadClass')); + + if (null !== $this->vendorDir) { + unset(self::$registeredLoaders[$this->vendorDir]); + } + } + + /** + * Loads the given class or interface. + * + * @param string $class The name of the class + * @return bool|null True if loaded, null otherwise + */ + public function loadClass($class) + { + if ($file = $this->findFile($class)) { + includeFile($file); + + return true; + } + } + + /** + * Finds the path to the file where the class is defined. + * + * @param string $class The name of the class + * + * @return string|false The path if found, false otherwise + */ + public function findFile($class) + { + // class map lookup + if (isset($this->classMap[$class])) { + return $this->classMap[$class]; + } + if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) { + return false; + } + if (null !== $this->apcuPrefix) { + $file = apcu_fetch($this->apcuPrefix.$class, $hit); + if ($hit) { + return $file; + } + } + + $file = $this->findFileWithExtension($class, '.php'); + + // Search for Hack files if we are running on HHVM + if (false === $file && defined('HHVM_VERSION')) { + $file = $this->findFileWithExtension($class, '.hh'); + } + + if (null !== $this->apcuPrefix) { + apcu_add($this->apcuPrefix.$class, $file); + } + + if (false === $file) { + // Remember that this class does not exist. + $this->missingClasses[$class] = true; + } + + return $file; + } + + /** + * Returns the currently registered loaders indexed by their corresponding vendor directories. + * + * @return self[] + */ + public static function getRegisteredLoaders() + { + return self::$registeredLoaders; + } + + private function findFileWithExtension($class, $ext) + { + // PSR-4 lookup + $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; + + $first = $class[0]; + if (isset($this->prefixLengthsPsr4[$first])) { + $subPath = $class; + while (false !== $lastPos = strrpos($subPath, '\\')) { + $subPath = substr($subPath, 0, $lastPos); + $search = $subPath . '\\'; + if (isset($this->prefixDirsPsr4[$search])) { + $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1); + foreach ($this->prefixDirsPsr4[$search] as $dir) { + if (file_exists($file = $dir . $pathEnd)) { + return $file; + } + } + } + } + } + + // PSR-4 fallback dirs + foreach ($this->fallbackDirsPsr4 as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { + return $file; + } + } + + // PSR-0 lookup + if (false !== $pos = strrpos($class, '\\')) { + // namespaced class name + $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) + . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); + } else { + // PEAR-like class name + $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext; + } + + if (isset($this->prefixesPsr0[$first])) { + foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) { + if (0 === strpos($class, $prefix)) { + foreach ($dirs as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { + return $file; + } + } + } + } + } + + // PSR-0 fallback dirs + foreach ($this->fallbackDirsPsr0 as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { + return $file; + } + } + + // PSR-0 include paths. + if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { + return $file; + } + + return false; + } +} + +/** + * Scope isolated include. + * + * Prevents access to $this/self from included files. + */ +function includeFile($file) +{ + include $file; +} diff --git a/vendor/composer/InstalledVersions.php b/vendor/composer/InstalledVersions.php new file mode 100644 index 0000000000000000000000000000000000000000..8038c0e8abd5aa3cece9ca7e0443f1c91b5cdbc7 --- /dev/null +++ b/vendor/composer/InstalledVersions.php @@ -0,0 +1,303 @@ + + array ( + 'pretty_version' => 'dev-master', + 'version' => 'dev-master', + 'aliases' => + array ( + ), + 'reference' => '7f614721f22588a8dccdc3f32a8132f2d870deb0', + 'name' => 'ilias-plugins/question-set-pool', + ), + 'versions' => + array ( + 'clue/graph' => + array ( + 'pretty_version' => 'v0.9.2', + 'version' => '0.9.2.0', + 'aliases' => + array ( + ), + 'reference' => '187902e158dead96fc5da3ae368ea1080abf267d', + ), + 'graphp/graphviz' => + array ( + 'pretty_version' => 'v0.2.2', + 'version' => '0.2.2.0', + 'aliases' => + array ( + ), + 'reference' => '5cc4466223ca46fffa196d1e762fae164319c229', + ), + 'ilias-plugins/question-set-pool' => + array ( + 'pretty_version' => 'dev-master', + 'version' => 'dev-master', + 'aliases' => + array ( + ), + 'reference' => '7f614721f22588a8dccdc3f32a8132f2d870deb0', + ), + ), +); +private static $canGetVendors; +private static $installedByVendor = array(); + + + + + + + +public static function getInstalledPackages() +{ +$packages = array(); +foreach (self::getInstalled() as $installed) { +$packages[] = array_keys($installed['versions']); +} + +if (1 === \count($packages)) { +return $packages[0]; +} + +return array_keys(array_flip(\call_user_func_array('array_merge', $packages))); +} + + + + + + + + + +public static function isInstalled($packageName) +{ +foreach (self::getInstalled() as $installed) { +if (isset($installed['versions'][$packageName])) { +return true; +} +} + +return false; +} + + + + + + + + + + + + + + +public static function satisfies(VersionParser $parser, $packageName, $constraint) +{ +$constraint = $parser->parseConstraints($constraint); +$provided = $parser->parseConstraints(self::getVersionRanges($packageName)); + +return $provided->matches($constraint); +} + + + + + + + + + + +public static function getVersionRanges($packageName) +{ +foreach (self::getInstalled() as $installed) { +if (!isset($installed['versions'][$packageName])) { +continue; +} + +$ranges = array(); +if (isset($installed['versions'][$packageName]['pretty_version'])) { +$ranges[] = $installed['versions'][$packageName]['pretty_version']; +} +if (array_key_exists('aliases', $installed['versions'][$packageName])) { +$ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']); +} +if (array_key_exists('replaced', $installed['versions'][$packageName])) { +$ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']); +} +if (array_key_exists('provided', $installed['versions'][$packageName])) { +$ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']); +} + +return implode(' || ', $ranges); +} + +throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); +} + + + + + +public static function getVersion($packageName) +{ +foreach (self::getInstalled() as $installed) { +if (!isset($installed['versions'][$packageName])) { +continue; +} + +if (!isset($installed['versions'][$packageName]['version'])) { +return null; +} + +return $installed['versions'][$packageName]['version']; +} + +throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); +} + + + + + +public static function getPrettyVersion($packageName) +{ +foreach (self::getInstalled() as $installed) { +if (!isset($installed['versions'][$packageName])) { +continue; +} + +if (!isset($installed['versions'][$packageName]['pretty_version'])) { +return null; +} + +return $installed['versions'][$packageName]['pretty_version']; +} + +throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); +} + + + + + +public static function getReference($packageName) +{ +foreach (self::getInstalled() as $installed) { +if (!isset($installed['versions'][$packageName])) { +continue; +} + +if (!isset($installed['versions'][$packageName]['reference'])) { +return null; +} + +return $installed['versions'][$packageName]['reference']; +} + +throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); +} + + + + + +public static function getRootPackage() +{ +$installed = self::getInstalled(); + +return $installed[0]['root']; +} + + + + + + + +public static function getRawData() +{ +return self::$installed; +} + + + + + + + + + + + + + + + + + + + +public static function reload($data) +{ +self::$installed = $data; +self::$installedByVendor = array(); +} + + + + + +private static function getInstalled() +{ +if (null === self::$canGetVendors) { +self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders'); +} + +$installed = array(); + +if (self::$canGetVendors) { +foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) { +if (isset(self::$installedByVendor[$vendorDir])) { +$installed[] = self::$installedByVendor[$vendorDir]; +} elseif (is_file($vendorDir.'/composer/installed.php')) { +$installed[] = self::$installedByVendor[$vendorDir] = require $vendorDir.'/composer/installed.php'; +} +} +} + +$installed[] = self::$installed; + +return $installed; +} +} diff --git a/vendor/composer/LICENSE b/vendor/composer/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..f27399a042d95c4708af3a8c74d35d338763cf8f --- /dev/null +++ b/vendor/composer/LICENSE @@ -0,0 +1,21 @@ + +Copyright (c) Nils Adermann, Jordi Boggiano + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + diff --git a/vendor/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php new file mode 100644 index 0000000000000000000000000000000000000000..bf57ee740c8cfe738574faa9295bc705bd091aa5 --- /dev/null +++ b/vendor/composer/autoload_classmap.php @@ -0,0 +1,112 @@ + $vendorDir . '/composer/InstalledVersions.php', + 'Fhaculty\\Graph\\Attribute\\AttributeAware' => $vendorDir . '/clue/graph/src/Attribute/AttributeAware.php', + 'Fhaculty\\Graph\\Attribute\\AttributeBag' => $vendorDir . '/clue/graph/src/Attribute/AttributeBag.php', + 'Fhaculty\\Graph\\Attribute\\AttributeBagContainer' => $vendorDir . '/clue/graph/src/Attribute/AttributeBagContainer.php', + 'Fhaculty\\Graph\\Attribute\\AttributeBagNamespaced' => $vendorDir . '/clue/graph/src/Attribute/AttributeBagNamespaced.php', + 'Fhaculty\\Graph\\Attribute\\AttributeBagReference' => $vendorDir . '/clue/graph/src/Attribute/AttributeBagReference.php', + 'Fhaculty\\Graph\\Edge\\Base' => $vendorDir . '/clue/graph/src/Edge/Base.php', + 'Fhaculty\\Graph\\Edge\\Directed' => $vendorDir . '/clue/graph/src/Edge/Directed.php', + 'Fhaculty\\Graph\\Edge\\Undirected' => $vendorDir . '/clue/graph/src/Edge/Undirected.php', + 'Fhaculty\\Graph\\Exception' => $vendorDir . '/clue/graph/src/Exception.php', + 'Fhaculty\\Graph\\Exception\\BadMethodCallException' => $vendorDir . '/clue/graph/src/Exception/BadMethodCallException.php', + 'Fhaculty\\Graph\\Exception\\DomainException' => $vendorDir . '/clue/graph/src/Exception/DomainException.php', + 'Fhaculty\\Graph\\Exception\\InvalidArgumentException' => $vendorDir . '/clue/graph/src/Exception/InvalidArgumentException.php', + 'Fhaculty\\Graph\\Exception\\LogicException' => $vendorDir . '/clue/graph/src/Exception/LogicException.php', + 'Fhaculty\\Graph\\Exception\\NegativeCycleException' => $vendorDir . '/clue/graph/src/Exception/NegativeCycleException.php', + 'Fhaculty\\Graph\\Exception\\OutOfBoundsException' => $vendorDir . '/clue/graph/src/Exception/OutOfBoundsException.php', + 'Fhaculty\\Graph\\Exception\\OverflowException' => $vendorDir . '/clue/graph/src/Exception/OverflowException.php', + 'Fhaculty\\Graph\\Exception\\RangeException' => $vendorDir . '/clue/graph/src/Exception/RangeException.php', + 'Fhaculty\\Graph\\Exception\\RuntimeException' => $vendorDir . '/clue/graph/src/Exception/RuntimeException.php', + 'Fhaculty\\Graph\\Exception\\UnderflowException' => $vendorDir . '/clue/graph/src/Exception/UnderflowException.php', + 'Fhaculty\\Graph\\Exception\\UnexpectedValueException' => $vendorDir . '/clue/graph/src/Exception/UnexpectedValueException.php', + 'Fhaculty\\Graph\\Exporter\\ExporterInterface' => $vendorDir . '/clue/graph/src/Exporter/ExporterInterface.php', + 'Fhaculty\\Graph\\Graph' => $vendorDir . '/clue/graph/src/Graph.php', + 'Fhaculty\\Graph\\Set\\DualAggregate' => $vendorDir . '/clue/graph/src/Set/DualAggregate.php', + 'Fhaculty\\Graph\\Set\\Edges' => $vendorDir . '/clue/graph/src/Set/Edges.php', + 'Fhaculty\\Graph\\Set\\EdgesAggregate' => $vendorDir . '/clue/graph/src/Set/EdgesAggregate.php', + 'Fhaculty\\Graph\\Set\\Vertices' => $vendorDir . '/clue/graph/src/Set/Vertices.php', + 'Fhaculty\\Graph\\Set\\VerticesAggregate' => $vendorDir . '/clue/graph/src/Set/VerticesAggregate.php', + 'Fhaculty\\Graph\\Set\\VerticesMap' => $vendorDir . '/clue/graph/src/Set/VerticesMap.php', + 'Fhaculty\\Graph\\Vertex' => $vendorDir . '/clue/graph/src/Vertex.php', + 'Fhaculty\\Graph\\Walk' => $vendorDir . '/clue/graph/src/Walk.php', + 'FormATestGlobalScreenToolsProvider' => $baseDir . '/classes/providers/FormATestGlobalScreenToolsProvider.php', + 'Graphp\\GraphViz\\Dot' => $vendorDir . '/graphp/graphviz/src/Dot.php', + 'Graphp\\GraphViz\\GraphViz' => $vendorDir . '/graphp/graphviz/src/GraphViz.php', + 'Graphp\\GraphViz\\Image' => $vendorDir . '/graphp/graphviz/src/Image.php', + 'ilFatImageMapQuestionAdapter' => $baseDir . '/classes/adapter/class.ilFatImageMapQuestionAdapter.php', + 'ilFatQuestionFactory' => $baseDir . '/classes/class.ilFatQuestionFactory.php', + 'ilFormATestAccordionGUI' => $baseDir . '/classes/utils/class.ilFormATestAccordionGUI.php', + 'ilFormATestAssQuestionSkillAssignmentsGUI' => $baseDir . '/classes/utils/class.ilFormATestAssQuestionSkillAssignmentsGUI.php', + 'ilFormATestConditionEvaluation' => $baseDir . '/classes/utils/abstract.ilFormATestConditionEvaluation.php', + 'ilFormATestDominantScoringReworker' => $baseDir . '/classes/utils/class.ilFormATestDominantScoringReworker.php', + 'ilFormATestExportGUI' => $baseDir . '/classes/class.ilFormATestExportGUI.php', + 'ilFormATestExportTableGUI' => $baseDir . '/classes/tables/class.ilFormATestExportTableGUI.php', + 'ilFormATestExporter' => $baseDir . '/classes/class.ilFormATestExporter.php', + 'ilFormATestHistoryGUI' => $baseDir . '/classes/controllers/class.ilFormATestHistoryGUI.php', + 'ilFormATestImporter' => $baseDir . '/classes/class.ilFormATestImporter.php', + 'ilFormATestInfoScreenControllerGUI' => $baseDir . '/classes/controllers/class.ilFormATestInfoScreenControllerGUI.php', + 'ilFormATestInfoScreenGUI' => $baseDir . '/classes/utils/class.ilFormATestInfoScreenGUI.php', + 'ilFormATestLPSummaryTableGUI' => $baseDir . '/classes/class.ilFormATestLPSummaryTableGUI.php', + 'ilFormATestLPUsersTableGUI' => $baseDir . '/classes/class.ilFormATestLPUsersTableGUI.php', + 'ilFormATestLearningProgressGUI' => $baseDir . '/classes/controllers/class.ilFormATestLearningProgressGUI.php', + 'ilFormATestOutputGUI' => $baseDir . '/classes/controllers/class.ilFormATestOutputGUI.php', + 'ilFormATestParticipantQuestionHeaderBuilder' => $baseDir . '/classes/utils/class.ilFormATestParticipantQuestionHeaderBuilder.php', + 'ilFormATestParticipantsGUI' => $baseDir . '/classes/controllers/class.ilFormATestParticipantsGUI.php', + 'ilFormATestParticipantsList' => $baseDir . '/classes/models/class.ilFormATestParticipantsList.php', + 'ilFormATestParticipantsProvider' => $baseDir . '/classes/providers/class.ilFormATestParticipantsProvider.php', + 'ilFormATestParticipantsTableGUI' => $baseDir . '/classes/tables/class.ilFormATestParticipantsTableGUI.php', + 'ilFormATestPlayerAccessHelper' => $baseDir . '/classes/utils/abstract.ilFormATestPlayerAccessHelper.php', + 'ilFormATestPlayerControllerGUI' => $baseDir . '/classes/controllers/class.ilFormATestPlayerControllerGUI.php', + 'ilFormATestPlugin' => $baseDir . '/classes/class.ilFormATestPlugin.php', + 'ilFormATestPropertiesGUI' => $baseDir . '/classes/controllers/class.ilFormATestPropertiesGUI.php', + 'ilFormATestQuestionHeaderBuilder' => $baseDir . '/classes/utils/class.ilFormATestQuestionHeaderBuilder.php', + 'ilFormATestQuestionListProvider' => $baseDir . '/classes/providers/class.ilFormATestQuestionListProvider.php', + 'ilFormATestQuestionSetPoolSetList' => $baseDir . '/classes/models/class.ilFormATestQuestionSetPoolSetList.php', + 'ilFormATestResult' => $baseDir . '/classes/models/class.ilFormATestResult.php', + 'ilFormATestResultList' => $baseDir . '/classes/models/class.ilFormATestResultList.php', + 'ilFormATestResultOuputGUI' => $baseDir . '/classes/controllers/class.ilFormATestResultOuputGUI.php', + 'ilFormATestResultsTableGUI' => $baseDir . '/classes/tables/class.ilFormATestResultsTableGUI.php', + 'ilFormATestScoringAndResultPropertiesGUI' => $baseDir . '/classes/controllers/class.ilFormATestScoringAndResultPropertiesGUI.php', + 'ilFormATestSession' => $baseDir . '/classes/models/class.ilFormATestSession.php', + 'ilFormATestSessionSet' => $baseDir . '/classes/models/class.ilFormATestSessionSet.php', + 'ilFormATestSetAssignment' => $baseDir . '/classes/models/class.ilFormATestSetAssignment.php', + 'ilFormATestSetBrowserProvider' => $baseDir . '/classes/providers/class.ilFormATestSetBrowserProvider.php', + 'ilFormATestSetList' => $baseDir . '/classes/models/class.ilFormATestSetList.php', + 'ilFormATestSetProvider' => $baseDir . '/classes/providers/class.ilFormATestSetProvider.php', + 'ilFormATestSetResultList' => $baseDir . '/classes/models/class.ilFormATestSetResultList.php', + 'ilFormATestSetResultTableGUI' => $baseDir . '/classes/tables/class.ilFormATestSetResultTableGUI.php', + 'ilFormATestSetStepList' => $baseDir . '/classes/models/class.ilFormATestSetStepList.php', + 'ilFormATestSetsGUI' => $baseDir . '/classes/controllers/class.ilFormATestSetsGUI.php', + 'ilFormATestSetsTableGUI' => $baseDir . '/classes/tables/class.ilFormATestSetsTableGUI.php', + 'ilFormATestSkillAdministrationGUI' => $baseDir . '/classes/controllers/class.ilFormATestSkillAdministrationGUI.php', + 'ilFormATestSkillAssQuestionList' => $baseDir . '/classes/utils/class.ilFormATestSkillAssQuestionList.php', + 'ilFormATestSkillEvaluationGUI' => $baseDir . '/classes/class.ilFormATestSkillEvaluationGUI.php', + 'ilFormATestSkillLevelHelper' => $baseDir . '/classes/utils/class.ilFormATestSkillLevelHelper.php', + 'ilFormATestUtil' => $baseDir . '/classes/utils/abstract.ilFormATestUtil.php', + 'ilFormATestXmlValidator' => $baseDir . '/classes/class.ilFormATestXmlValidator.php', + 'ilObjFormATest' => $baseDir . '/classes/class.ilObjFormATest.php', + 'ilObjFormATestAccess' => $baseDir . '/classes/class.ilObjFormATestAccess.php', + 'ilObjFormATestGUI' => $baseDir . '/classes/class.ilObjFormATestGUI.php', + 'ilObjFormATestListGUI' => $baseDir . '/classes/class.ilObjFormATestListGUI.php', + 'ilObjFormATestQuestionSetPoolGUI' => $baseDir . '/classes/class.ilObjFormATestQuestionSetPoolGUI.php', + 'ilObjFormATestXMLParser' => $baseDir . '/classes/class.ilObjFormATestXMLParser.php', + 'ilObjTestGateway' => $baseDir . '/classes/utils/class.ilObjTestGateway.php', + 'ilXTSFControllerFormGUI' => $baseDir . '/classes/controllers/abstract.ilXTSFControllerFormGUI.php', + 'ilXTSFControllerGUI' => $baseDir . '/classes/controllers/abstract.ilXTSFControllerGUI.php', + 'ilXTSFControllerTableGUI' => $baseDir . '/classes/controllers/abstract.ilXTSFControllerTableGUI.php', + 'ilXTSFModel' => $baseDir . '/classes/models/abstract.ilXTSFModel.php', + 'ilXTSFModelInterface' => $baseDir . '/classes/models/interface.ilXTSFModelInterface.php', + 'ilXTSFModelList' => $baseDir . '/classes/models/abstract.ilXTSFModelList.php', + 'ilXTSFPluginDispatcher' => $baseDir . '/classes/dispatcher/class.ilXTSFPluginDispatcher.php', + 'ilXTSFTableDataProvider' => $baseDir . '/classes/providers/interface.ilXTSFTableDataProvider.php', + 'ilXTSFTableDatabaseDataProvider' => $baseDir . '/classes/providers/abstract.ilXTSFTableDatabaseDataProvider.php', + 'ilXTSFTableGUI' => $baseDir . '/classes/tables/abstract.ilXTSFTableGUI.php', +); diff --git a/vendor/composer/autoload_namespaces.php b/vendor/composer/autoload_namespaces.php new file mode 100644 index 0000000000000000000000000000000000000000..b7fc0125dbca56fd7565ad62097672a59473e64e --- /dev/null +++ b/vendor/composer/autoload_namespaces.php @@ -0,0 +1,9 @@ + array($vendorDir . '/graphp/graphviz/src'), + 'Fhaculty\\Graph\\' => array($vendorDir . '/clue/graph/src'), +); diff --git a/vendor/composer/autoload_real.php b/vendor/composer/autoload_real.php new file mode 100644 index 0000000000000000000000000000000000000000..d86708def00e0c67a02651b00ce022d540333389 --- /dev/null +++ b/vendor/composer/autoload_real.php @@ -0,0 +1,48 @@ += 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded()); + if ($useStaticLoader) { + require __DIR__ . '/autoload_static.php'; + + call_user_func(\Composer\Autoload\ComposerStaticInit3eea2cbdff537fb78fa9f1bf50307e6a::getInitializer($loader)); + } else { + $classMap = require __DIR__ . '/autoload_classmap.php'; + if ($classMap) { + $loader->addClassMap($classMap); + } + } + + $loader->setClassMapAuthoritative(true); + $loader->register(true); + + return $loader; + } +} diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php new file mode 100644 index 0000000000000000000000000000000000000000..e88222dd40f57b9449cb8beac6cc98e5b6bba5c6 --- /dev/null +++ b/vendor/composer/autoload_static.php @@ -0,0 +1,146 @@ + + array ( + 'Graphp\\GraphViz\\' => 16, + ), + 'F' => + array ( + 'Fhaculty\\Graph\\' => 15, + ), + ); + + public static $prefixDirsPsr4 = array ( + 'Graphp\\GraphViz\\' => + array ( + 0 => __DIR__ . '/..' . '/graphp/graphviz/src', + ), + 'Fhaculty\\Graph\\' => + array ( + 0 => __DIR__ . '/..' . '/clue/graph/src', + ), + ); + + public static $classMap = array ( + 'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php', + 'Fhaculty\\Graph\\Attribute\\AttributeAware' => __DIR__ . '/..' . '/clue/graph/src/Attribute/AttributeAware.php', + 'Fhaculty\\Graph\\Attribute\\AttributeBag' => __DIR__ . '/..' . '/clue/graph/src/Attribute/AttributeBag.php', + 'Fhaculty\\Graph\\Attribute\\AttributeBagContainer' => __DIR__ . '/..' . '/clue/graph/src/Attribute/AttributeBagContainer.php', + 'Fhaculty\\Graph\\Attribute\\AttributeBagNamespaced' => __DIR__ . '/..' . '/clue/graph/src/Attribute/AttributeBagNamespaced.php', + 'Fhaculty\\Graph\\Attribute\\AttributeBagReference' => __DIR__ . '/..' . '/clue/graph/src/Attribute/AttributeBagReference.php', + 'Fhaculty\\Graph\\Edge\\Base' => __DIR__ . '/..' . '/clue/graph/src/Edge/Base.php', + 'Fhaculty\\Graph\\Edge\\Directed' => __DIR__ . '/..' . '/clue/graph/src/Edge/Directed.php', + 'Fhaculty\\Graph\\Edge\\Undirected' => __DIR__ . '/..' . '/clue/graph/src/Edge/Undirected.php', + 'Fhaculty\\Graph\\Exception' => __DIR__ . '/..' . '/clue/graph/src/Exception.php', + 'Fhaculty\\Graph\\Exception\\BadMethodCallException' => __DIR__ . '/..' . '/clue/graph/src/Exception/BadMethodCallException.php', + 'Fhaculty\\Graph\\Exception\\DomainException' => __DIR__ . '/..' . '/clue/graph/src/Exception/DomainException.php', + 'Fhaculty\\Graph\\Exception\\InvalidArgumentException' => __DIR__ . '/..' . '/clue/graph/src/Exception/InvalidArgumentException.php', + 'Fhaculty\\Graph\\Exception\\LogicException' => __DIR__ . '/..' . '/clue/graph/src/Exception/LogicException.php', + 'Fhaculty\\Graph\\Exception\\NegativeCycleException' => __DIR__ . '/..' . '/clue/graph/src/Exception/NegativeCycleException.php', + 'Fhaculty\\Graph\\Exception\\OutOfBoundsException' => __DIR__ . '/..' . '/clue/graph/src/Exception/OutOfBoundsException.php', + 'Fhaculty\\Graph\\Exception\\OverflowException' => __DIR__ . '/..' . '/clue/graph/src/Exception/OverflowException.php', + 'Fhaculty\\Graph\\Exception\\RangeException' => __DIR__ . '/..' . '/clue/graph/src/Exception/RangeException.php', + 'Fhaculty\\Graph\\Exception\\RuntimeException' => __DIR__ . '/..' . '/clue/graph/src/Exception/RuntimeException.php', + 'Fhaculty\\Graph\\Exception\\UnderflowException' => __DIR__ . '/..' . '/clue/graph/src/Exception/UnderflowException.php', + 'Fhaculty\\Graph\\Exception\\UnexpectedValueException' => __DIR__ . '/..' . '/clue/graph/src/Exception/UnexpectedValueException.php', + 'Fhaculty\\Graph\\Exporter\\ExporterInterface' => __DIR__ . '/..' . '/clue/graph/src/Exporter/ExporterInterface.php', + 'Fhaculty\\Graph\\Graph' => __DIR__ . '/..' . '/clue/graph/src/Graph.php', + 'Fhaculty\\Graph\\Set\\DualAggregate' => __DIR__ . '/..' . '/clue/graph/src/Set/DualAggregate.php', + 'Fhaculty\\Graph\\Set\\Edges' => __DIR__ . '/..' . '/clue/graph/src/Set/Edges.php', + 'Fhaculty\\Graph\\Set\\EdgesAggregate' => __DIR__ . '/..' . '/clue/graph/src/Set/EdgesAggregate.php', + 'Fhaculty\\Graph\\Set\\Vertices' => __DIR__ . '/..' . '/clue/graph/src/Set/Vertices.php', + 'Fhaculty\\Graph\\Set\\VerticesAggregate' => __DIR__ . '/..' . '/clue/graph/src/Set/VerticesAggregate.php', + 'Fhaculty\\Graph\\Set\\VerticesMap' => __DIR__ . '/..' . '/clue/graph/src/Set/VerticesMap.php', + 'Fhaculty\\Graph\\Vertex' => __DIR__ . '/..' . '/clue/graph/src/Vertex.php', + 'Fhaculty\\Graph\\Walk' => __DIR__ . '/..' . '/clue/graph/src/Walk.php', + 'FormATestGlobalScreenToolsProvider' => __DIR__ . '/../..' . '/classes/providers/FormATestGlobalScreenToolsProvider.php', + 'Graphp\\GraphViz\\Dot' => __DIR__ . '/..' . '/graphp/graphviz/src/Dot.php', + 'Graphp\\GraphViz\\GraphViz' => __DIR__ . '/..' . '/graphp/graphviz/src/GraphViz.php', + 'Graphp\\GraphViz\\Image' => __DIR__ . '/..' . '/graphp/graphviz/src/Image.php', + 'ilFatImageMapQuestionAdapter' => __DIR__ . '/../..' . '/classes/adapter/class.ilFatImageMapQuestionAdapter.php', + 'ilFatQuestionFactory' => __DIR__ . '/../..' . '/classes/class.ilFatQuestionFactory.php', + 'ilFormATestAccordionGUI' => __DIR__ . '/../..' . '/classes/utils/class.ilFormATestAccordionGUI.php', + 'ilFormATestAssQuestionSkillAssignmentsGUI' => __DIR__ . '/../..' . '/classes/utils/class.ilFormATestAssQuestionSkillAssignmentsGUI.php', + 'ilFormATestConditionEvaluation' => __DIR__ . '/../..' . '/classes/utils/abstract.ilFormATestConditionEvaluation.php', + 'ilFormATestDominantScoringReworker' => __DIR__ . '/../..' . '/classes/utils/class.ilFormATestDominantScoringReworker.php', + 'ilFormATestExportGUI' => __DIR__ . '/../..' . '/classes/class.ilFormATestExportGUI.php', + 'ilFormATestExportTableGUI' => __DIR__ . '/../..' . '/classes/tables/class.ilFormATestExportTableGUI.php', + 'ilFormATestExporter' => __DIR__ . '/../..' . '/classes/class.ilFormATestExporter.php', + 'ilFormATestHistoryGUI' => __DIR__ . '/../..' . '/classes/controllers/class.ilFormATestHistoryGUI.php', + 'ilFormATestImporter' => __DIR__ . '/../..' . '/classes/class.ilFormATestImporter.php', + 'ilFormATestInfoScreenControllerGUI' => __DIR__ . '/../..' . '/classes/controllers/class.ilFormATestInfoScreenControllerGUI.php', + 'ilFormATestInfoScreenGUI' => __DIR__ . '/../..' . '/classes/utils/class.ilFormATestInfoScreenGUI.php', + 'ilFormATestLPSummaryTableGUI' => __DIR__ . '/../..' . '/classes/class.ilFormATestLPSummaryTableGUI.php', + 'ilFormATestLPUsersTableGUI' => __DIR__ . '/../..' . '/classes/class.ilFormATestLPUsersTableGUI.php', + 'ilFormATestLearningProgressGUI' => __DIR__ . '/../..' . '/classes/controllers/class.ilFormATestLearningProgressGUI.php', + 'ilFormATestOutputGUI' => __DIR__ . '/../..' . '/classes/controllers/class.ilFormATestOutputGUI.php', + 'ilFormATestParticipantQuestionHeaderBuilder' => __DIR__ . '/../..' . '/classes/utils/class.ilFormATestParticipantQuestionHeaderBuilder.php', + 'ilFormATestParticipantsGUI' => __DIR__ . '/../..' . '/classes/controllers/class.ilFormATestParticipantsGUI.php', + 'ilFormATestParticipantsList' => __DIR__ . '/../..' . '/classes/models/class.ilFormATestParticipantsList.php', + 'ilFormATestParticipantsProvider' => __DIR__ . '/../..' . '/classes/providers/class.ilFormATestParticipantsProvider.php', + 'ilFormATestParticipantsTableGUI' => __DIR__ . '/../..' . '/classes/tables/class.ilFormATestParticipantsTableGUI.php', + 'ilFormATestPlayerAccessHelper' => __DIR__ . '/../..' . '/classes/utils/abstract.ilFormATestPlayerAccessHelper.php', + 'ilFormATestPlayerControllerGUI' => __DIR__ . '/../..' . '/classes/controllers/class.ilFormATestPlayerControllerGUI.php', + 'ilFormATestPlugin' => __DIR__ . '/../..' . '/classes/class.ilFormATestPlugin.php', + 'ilFormATestPropertiesGUI' => __DIR__ . '/../..' . '/classes/controllers/class.ilFormATestPropertiesGUI.php', + 'ilFormATestQuestionHeaderBuilder' => __DIR__ . '/../..' . '/classes/utils/class.ilFormATestQuestionHeaderBuilder.php', + 'ilFormATestQuestionListProvider' => __DIR__ . '/../..' . '/classes/providers/class.ilFormATestQuestionListProvider.php', + 'ilFormATestQuestionSetPoolSetList' => __DIR__ . '/../..' . '/classes/models/class.ilFormATestQuestionSetPoolSetList.php', + 'ilFormATestResult' => __DIR__ . '/../..' . '/classes/models/class.ilFormATestResult.php', + 'ilFormATestResultList' => __DIR__ . '/../..' . '/classes/models/class.ilFormATestResultList.php', + 'ilFormATestResultOuputGUI' => __DIR__ . '/../..' . '/classes/controllers/class.ilFormATestResultOuputGUI.php', + 'ilFormATestResultsTableGUI' => __DIR__ . '/../..' . '/classes/tables/class.ilFormATestResultsTableGUI.php', + 'ilFormATestScoringAndResultPropertiesGUI' => __DIR__ . '/../..' . '/classes/controllers/class.ilFormATestScoringAndResultPropertiesGUI.php', + 'ilFormATestSession' => __DIR__ . '/../..' . '/classes/models/class.ilFormATestSession.php', + 'ilFormATestSessionSet' => __DIR__ . '/../..' . '/classes/models/class.ilFormATestSessionSet.php', + 'ilFormATestSetAssignment' => __DIR__ . '/../..' . '/classes/models/class.ilFormATestSetAssignment.php', + 'ilFormATestSetBrowserProvider' => __DIR__ . '/../..' . '/classes/providers/class.ilFormATestSetBrowserProvider.php', + 'ilFormATestSetList' => __DIR__ . '/../..' . '/classes/models/class.ilFormATestSetList.php', + 'ilFormATestSetProvider' => __DIR__ . '/../..' . '/classes/providers/class.ilFormATestSetProvider.php', + 'ilFormATestSetResultList' => __DIR__ . '/../..' . '/classes/models/class.ilFormATestSetResultList.php', + 'ilFormATestSetResultTableGUI' => __DIR__ . '/../..' . '/classes/tables/class.ilFormATestSetResultTableGUI.php', + 'ilFormATestSetStepList' => __DIR__ . '/../..' . '/classes/models/class.ilFormATestSetStepList.php', + 'ilFormATestSetsGUI' => __DIR__ . '/../..' . '/classes/controllers/class.ilFormATestSetsGUI.php', + 'ilFormATestSetsTableGUI' => __DIR__ . '/../..' . '/classes/tables/class.ilFormATestSetsTableGUI.php', + 'ilFormATestSkillAdministrationGUI' => __DIR__ . '/../..' . '/classes/controllers/class.ilFormATestSkillAdministrationGUI.php', + 'ilFormATestSkillAssQuestionList' => __DIR__ . '/../..' . '/classes/utils/class.ilFormATestSkillAssQuestionList.php', + 'ilFormATestSkillEvaluationGUI' => __DIR__ . '/../..' . '/classes/class.ilFormATestSkillEvaluationGUI.php', + 'ilFormATestSkillLevelHelper' => __DIR__ . '/../..' . '/classes/utils/class.ilFormATestSkillLevelHelper.php', + 'ilFormATestUtil' => __DIR__ . '/../..' . '/classes/utils/abstract.ilFormATestUtil.php', + 'ilFormATestXmlValidator' => __DIR__ . '/../..' . '/classes/class.ilFormATestXmlValidator.php', + 'ilObjFormATest' => __DIR__ . '/../..' . '/classes/class.ilObjFormATest.php', + 'ilObjFormATestAccess' => __DIR__ . '/../..' . '/classes/class.ilObjFormATestAccess.php', + 'ilObjFormATestGUI' => __DIR__ . '/../..' . '/classes/class.ilObjFormATestGUI.php', + 'ilObjFormATestListGUI' => __DIR__ . '/../..' . '/classes/class.ilObjFormATestListGUI.php', + 'ilObjFormATestQuestionSetPoolGUI' => __DIR__ . '/../..' . '/classes/class.ilObjFormATestQuestionSetPoolGUI.php', + 'ilObjFormATestXMLParser' => __DIR__ . '/../..' . '/classes/class.ilObjFormATestXMLParser.php', + 'ilObjTestGateway' => __DIR__ . '/../..' . '/classes/utils/class.ilObjTestGateway.php', + 'ilXTSFControllerFormGUI' => __DIR__ . '/../..' . '/classes/controllers/abstract.ilXTSFControllerFormGUI.php', + 'ilXTSFControllerGUI' => __DIR__ . '/../..' . '/classes/controllers/abstract.ilXTSFControllerGUI.php', + 'ilXTSFControllerTableGUI' => __DIR__ . '/../..' . '/classes/controllers/abstract.ilXTSFControllerTableGUI.php', + 'ilXTSFModel' => __DIR__ . '/../..' . '/classes/models/abstract.ilXTSFModel.php', + 'ilXTSFModelInterface' => __DIR__ . '/../..' . '/classes/models/interface.ilXTSFModelInterface.php', + 'ilXTSFModelList' => __DIR__ . '/../..' . '/classes/models/abstract.ilXTSFModelList.php', + 'ilXTSFPluginDispatcher' => __DIR__ . '/../..' . '/classes/dispatcher/class.ilXTSFPluginDispatcher.php', + 'ilXTSFTableDataProvider' => __DIR__ . '/../..' . '/classes/providers/interface.ilXTSFTableDataProvider.php', + 'ilXTSFTableDatabaseDataProvider' => __DIR__ . '/../..' . '/classes/providers/abstract.ilXTSFTableDatabaseDataProvider.php', + 'ilXTSFTableGUI' => __DIR__ . '/../..' . '/classes/tables/abstract.ilXTSFTableGUI.php', + ); + + public static function getInitializer(ClassLoader $loader) + { + return \Closure::bind(function () use ($loader) { + $loader->prefixLengthsPsr4 = ComposerStaticInit3eea2cbdff537fb78fa9f1bf50307e6a::$prefixLengthsPsr4; + $loader->prefixDirsPsr4 = ComposerStaticInit3eea2cbdff537fb78fa9f1bf50307e6a::$prefixDirsPsr4; + $loader->classMap = ComposerStaticInit3eea2cbdff537fb78fa9f1bf50307e6a::$classMap; + + }, null, ClassLoader::class); + } +} diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json new file mode 100644 index 0000000000000000000000000000000000000000..93b16ff89faf5708a1d102a93c27d60297b8c8bd --- /dev/null +++ b/vendor/composer/installed.json @@ -0,0 +1,113 @@ +{ + "packages": [ + { + "name": "clue/graph", + "version": "v0.9.2", + "version_normalized": "0.9.2.0", + "source": { + "type": "git", + "url": "https://github.com/graphp/graph.git", + "reference": "187902e158dead96fc5da3ae368ea1080abf267d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/graphp/graph/zipball/187902e158dead96fc5da3ae368ea1080abf267d", + "reference": "187902e158dead96fc5da3ae368ea1080abf267d", + "shasum": "" + }, + "require": { + "php": ">=5.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35" + }, + "suggest": { + "graphp/algorithms": "Common graph algorithms, such as Dijkstra and Moore-Bellman-Ford (shortest path), minimum spanning tree (MST), Kruskal, Prim and many more..", + "graphp/graphviz": "GraphViz graph drawing / DOT output" + }, + "time": "2020-12-03T18:55:21+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "Fhaculty\\Graph\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering" + } + ], + "description": "GraPHP is the mathematical graph/network library written in PHP.", + "homepage": "https://github.com/graphp/graph", + "keywords": [ + "edge", + "graph", + "mathematical", + "network", + "vertex" + ], + "support": { + "issues": "https://github.com/graphp/graph/issues", + "source": "https://github.com/graphp/graph/tree/v0.9.2" + }, + "install-path": "../clue/graph" + }, + { + "name": "graphp/graphviz", + "version": "v0.2.2", + "version_normalized": "0.2.2.0", + "source": { + "type": "git", + "url": "https://github.com/graphp/graphviz.git", + "reference": "5cc4466223ca46fffa196d1e762fae164319c229" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/graphp/graphviz/zipball/5cc4466223ca46fffa196d1e762fae164319c229", + "reference": "5cc4466223ca46fffa196d1e762fae164319c229", + "shasum": "" + }, + "require": { + "clue/graph": "~0.9.0|~0.8.0", + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.4 || ^5.7 || ^4.8.35" + }, + "time": "2019-10-04T13:30:55+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "Graphp\\GraphViz\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "GraphViz graph drawing for the mathematical graph/network library GraPHP.", + "homepage": "https://github.com/graphp/graphviz", + "keywords": [ + "dot output", + "graph drawing", + "graph image", + "graphp", + "graphviz" + ], + "support": { + "issues": "https://github.com/graphp/graphviz/issues", + "source": "https://github.com/graphp/graphviz/tree/v0.2.2" + }, + "install-path": "../graphp/graphviz" + } + ], + "dev": true, + "dev-package-names": [] +} diff --git a/vendor/composer/installed.php b/vendor/composer/installed.php new file mode 100644 index 0000000000000000000000000000000000000000..3402efc7a08e0a5409bd5d78601607a49570c558 --- /dev/null +++ b/vendor/composer/installed.php @@ -0,0 +1,42 @@ + + array ( + 'pretty_version' => 'dev-master', + 'version' => 'dev-master', + 'aliases' => + array ( + ), + 'reference' => '7f614721f22588a8dccdc3f32a8132f2d870deb0', + 'name' => 'ilias-plugins/question-set-pool', + ), + 'versions' => + array ( + 'clue/graph' => + array ( + 'pretty_version' => 'v0.9.2', + 'version' => '0.9.2.0', + 'aliases' => + array ( + ), + 'reference' => '187902e158dead96fc5da3ae368ea1080abf267d', + ), + 'graphp/graphviz' => + array ( + 'pretty_version' => 'v0.2.2', + 'version' => '0.2.2.0', + 'aliases' => + array ( + ), + 'reference' => '5cc4466223ca46fffa196d1e762fae164319c229', + ), + 'ilias-plugins/question-set-pool' => + array ( + 'pretty_version' => 'dev-master', + 'version' => 'dev-master', + 'aliases' => + array ( + ), + 'reference' => '7f614721f22588a8dccdc3f32a8132f2d870deb0', + ), + ), +); diff --git a/vendor/composer/platform_check.php b/vendor/composer/platform_check.php new file mode 100644 index 0000000000000000000000000000000000000000..7621d4ff97fd0e26db5da0a4844c397ddc4f36ba --- /dev/null +++ b/vendor/composer/platform_check.php @@ -0,0 +1,26 @@ += 50300)) { + $issues[] = 'Your Composer dependencies require a PHP version ">= 5.3.0". You are running ' . PHP_VERSION . '.'; +} + +if ($issues) { + if (!headers_sent()) { + header('HTTP/1.1 500 Internal Server Error'); + } + if (!ini_get('display_errors')) { + if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') { + fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL); + } elseif (!headers_sent()) { + echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL; + } + } + trigger_error( + 'Composer detected issues in your platform: ' . implode(' ', $issues), + E_USER_ERROR + ); +} diff --git a/vendor/graphp/graphviz/.gitignore b/vendor/graphp/graphviz/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..de4a392c3313c89428ffe2a939f8744aae16b81b --- /dev/null +++ b/vendor/graphp/graphviz/.gitignore @@ -0,0 +1,2 @@ +/vendor +/composer.lock diff --git a/vendor/graphp/graphviz/.travis.yml b/vendor/graphp/graphviz/.travis.yml new file mode 100644 index 0000000000000000000000000000000000000000..14eeac7303e714a84aa0c69687af60b00391a86a --- /dev/null +++ b/vendor/graphp/graphviz/.travis.yml @@ -0,0 +1,34 @@ +language: php + +php: +# - 5.3 # requires old distro, see below + - 5.4 + - 5.5 + - 5.6 + - 7.0 + - 7.1 + - 7.2 + - hhvm # ignore errors, see below + +# lock distro so future defaults will not break the build +dist: trusty + +matrix: + include: + - php: 5.3 + dist: precise + allow_failures: + - php: hhvm + +sudo: false + +addons: + apt: + packages: + - graphviz + +install: + - composer install --no-interaction + +script: + - vendor/bin/phpunit --coverage-text diff --git a/vendor/graphp/graphviz/CHANGELOG.md b/vendor/graphp/graphviz/CHANGELOG.md new file mode 100644 index 0000000000000000000000000000000000000000..ad786f30109385102691570eb8bbeb2bd3bfe5d6 --- /dev/null +++ b/vendor/graphp/graphviz/CHANGELOG.md @@ -0,0 +1,44 @@ +# Changelog + +## 0.2.2 (2019-10-04) + +* Feature: Omit root graph name unless explicitly assigned via `graphviz.name` attribute. + (#28 by @rhelms and @clue) + + ```php + $graph = new Graph(); + $graph->setAttribute('graphviz.name', 'g'); + ``` + +* Feature: Remove unneeded dependency on `graphp/algorithms`. + (#39 by @clue) + +* Feature / Fix: Use UTF-8 encoding (Unicode) by default and respect charset attribute. + (#27 by @Ithilias and @clue) + +* Fix: Fix representing directed loop edges as directed edge + (#37 by @clue) + +* Add examples and documentation for GraphViz attributes, labels and record shapes. + (#26 by @clue) + +* Update test suite to support PHPUnit 6 and PHPUnit 5 and support running on legacy PHP 5.3 through PHP 7.2 and HHVM. + (#24 by @clue) + +## 0.2.1 (2015-03-08) + +* Support graph v0.9 (while keeping BC) + ([#9](https://github.com/graphp/graphviz/pull/9)) + +## 0.2.0 (2015-01-19) + +* BC break: Refactor to inject Graph into GraphViz on demand, inject GraphViz into exporters + ([#6](https://github.com/graphp/graphviz/pull/6)) + +* BC break: Remove legacy layout helper + ([#5](https://github.com/graphp/graphviz/pull/5)) + +## 0.1.0 (2014-12-31) + +* First tagged release, split off from [clue/graph](https://github.com/clue/graph) v0.8.0 + ([#1](https://github.com/graphp/graphviz/issues/1)) diff --git a/vendor/graphp/graphviz/LICENSE b/vendor/graphp/graphviz/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..358d929ad811d9dc5a36e74c3b664ed3da1c56eb --- /dev/null +++ b/vendor/graphp/graphviz/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2012+ Christian Lück (Maintainer) +Copyright (c) 2012+ Fhaculty Core Team and our awesome contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/graphp/graphviz/README.md b/vendor/graphp/graphviz/README.md new file mode 100644 index 0000000000000000000000000000000000000000..19cae692ee71e36e7cdeb8ba58550b8cfbaa8334 --- /dev/null +++ b/vendor/graphp/graphviz/README.md @@ -0,0 +1,425 @@ +# graphp/graphviz [![Build Status](https://travis-ci.org/graphp/graphviz.svg?branch=master)](https://travis-ci.org/graphp/graphviz) + +GraphViz graph drawing for the mathematical graph/network library GraPHP. + +The library supports visualizing graph images, including them into webpages, +opening up images from within CLI applications and exporting them +as PNG, JPEG or SVG file formats (among many others). +Because [graph drawing](http://en.wikipedia.org/wiki/Graph_drawing) is a complex area on its own, +the actual layouting of the graph is left up to the excelent [GraphViz](http://www.graphviz.org/) +"Graph Visualization Software" and we merely provide some convenient APIs to interface with GraphViz. + +> Note: This project is in beta stage! Feel free to report any issues you encounter. + +**Table of contents** + +* [Quickstart examples](#quickstart-examples) +* [Attributes](#attributes) + * [Graph attributes](#graph-attributes) + * [Vertex attributes](#vertex-attributes) + * [Edge attributes](#edge-attributes) +* [Labels](#labels) + * [Vertex labels](#vertex-labels) + * [Edge labels](#edge-labels) + * [HTML-like labels](#html-like-labels) + * [Record-based nodes](#record-based-nodes) +* [Install](#install) +* [Tests](#tests) +* [License](#license) + +## Quickstart examples + +Once [installed](#install), let's build and display a sample graph: + +````php +$graph = new Fhaculty\Graph\Graph(); + +$blue = $graph->createVertex('blue'); +$blue->setAttribute('graphviz.color', 'blue'); + +$red = $graph->createVertex('red'); +$red->setAttribute('graphviz.color', 'red'); + +$edge = $blue->createEdgeTo($red); +$edge->setAttribute('graphviz.color', 'grey'); + +$graphviz = new Graphp\GraphViz\GraphViz(); +$graphviz->display($graph); +```` + +The above code will open your default image viewer with the following image: + +![red-blue](examples/01-simple.png) + +See also the [examples](examples/). + +## Attributes + +GraphViz supports a number of attributes on the graph instance itself, each +vertex instance (GraphViz calls these "nodes") and edge instance. Any of these +GraphViz attributes are supported by this library and have to be assigned using +GraPHP attributes as documented below. + +For the full list of all GraphViz attributes, please refer to the +[GraphViz documentation](https://graphviz.gitlab.io/_pages/doc/info/attrs.html). + +Note that all attributes use UTF-8 encoding (Unicode) and will be quoted and +escaped by default, so a `ö` and `>` will appear as-is and will not be +interpreted as HTML. See also [HTML-like labels](#html-like-labels) below for +more details. + +### Graph attributes + +GraphViz supports a number of attributes on the graph instance itself. Any of +these GraphViz attributes are supported by this library and have to be assigned +on the graph instance with the `graphviz.graph.` prefix like this: + +```php +$graph = new Fhaculty\Graph\Graph(); +$graph->setAttribute('graphviz.graph.bgcolor', 'transparent'); +``` + +> Note how this uses the `graphviz.graph.` prefix and not just `graphviz.`. This + is done for consistency reasons with respect to default vertex and edge + attributes as documented below. + +For example, the `rankdir` attribute can be used to change the orientation to +horizontal mode (left to right) like this: + +```php +$graph = new Fhaculty\Graph\Graph(); +$graph->setAttribute('graphviz.graph.rankdir', 'LR'); + +$hello = $graph->createVertex('hello'); +$world = $graph->createVertex('wörld'); +$hello->createEdgeTo($world); +``` + +![html graph example](examples/02-html.png) + +See also the [examples](examples/). + +Additionally, this library accepts an optional `graphviz.name` attribute that +will be used as the name (or ID) for the root graph object in the DOT output if +given. Unless explicitly assigned, this will be omitted by default. It is common +to assign a `G` here, but usually there should be no need to assign this. Among +others, this may be used as the title or tooltip in SVG output. + +```php +$graph = new Fhaculty\Graph\Graph(); +$graph->setAttribute('graphviz.name', 'G'); + +$graph->createVertex('first'); +``` + +### Vertex attributes + +GraphViz supports a number of attributes on each vertex instance (GraphViz calls +these "node" attributes). Any of these GraphViz attributes are supported by this +library and have to be assigned on the respective vertex instance with the +`graphviz.` prefix like this: + +```php +$graph = new Fhaculty\Graph\Graph(); + +$blue = $graph->createVertex('blue'); +$blue->setAttribute('graphviz.color', 'blue'); +``` + +Additionally, GraphViz also supports default attributes for all vertices. Any of +these GraphViz attributes are supported by this library and have to be assigned +on the graph instance with the `graphviz.node.` prefix like this: + +```php +$graph = new Fhaculty\Graph\Graph(); +$graph->setAttribute('graphviz.node.color', 'grey'); + +$grey = $graph->createVertex('grey'); +``` + +These default attributes can be overriden on each vertex instance by explicitly +assigning the same attribute on the respective vertex instance like this: + +```php +$graph = new Fhaculty\Graph\Graph(); +$graph->setAttribute('graphviz.node.color', 'grey'); + +$blue = $graph->createVertex('blue'); +$blue->setAttribute('graphviz.color', 'blue'); +``` + +> Note how this uses the `graphviz.node.` prefix and not `graphviz.vertex.`. This + is done for consistency reasons with respect to how GraphViz assigns these + default attributes in its DOT output. + +### Edge attributes + +GraphViz supports a number of attributes on each edge instance. Any of these +GraphViz attributes are supported by this library and have to be assigned on the +respective edge instance with the `graphviz.` prefix like this: + +```php +$graph = new Fhaculty\Graph\Graph(); + +$a = $graph->createVertex('a'); +$b = $graph->createVertex('b'); + +$blue = $a->createEdgeTo($b); +$blue->setAttribute('graphviz.color', 'blue'); +``` + +Additionally, GraphViz also supports default attributes for all edges. Any of +these GraphViz attributes are supported by this library and have to be assigned +on the graph instance with the `graphviz.edge.` prefix like this: + +```php +$graph = new Fhaculty\Graph\Graph(); +$graph->setAttribute('graphviz.edge.color', 'grey'); + +$a = $graph->createVertex('a'); +$b = $graph->createVertex('b'); + +$grey = $a->createEdgeTo($b); +``` + +These default attributes can be overriden on each edge instance by explicitly +assigning the same attribute on the respective edge instance like this: + +```php +$graph = new Fhaculty\Graph\Graph(); +$graph->setAttribute('graphviz.edge.color', 'grey'); + +$a = $graph->createVertex('a'); +$b = $graph->createVertex('b'); + +$blue = $a->createEdgeTo($b); +$blue->setAttribute('graphviz.color', 'blue'); +``` + +## Labels + +### Vertex labels + +By default, GraphViz will always render the vertex ID as the label: + +```php +$graph = new Fhaculty\Graph\Graph(); + +$blue = $graph->createVertex('blue'); +``` + +If you assign a vertex balance, this library will automatically include a +`label` attribute that includes the balance value. The following example will +automatically assign `blue (+10)` as the label: + +```php +$graph = new Fhaculty\Graph\Graph(); + +$blue = $graph->createVertex('blue'); +$blue->setBalance(10); +``` + +You can use [vertex attributes](#vertex-attributes) to explicitly assign a +custom `label` attribute. Note that any balance value will still be appended +like in the previous example. + +```php +$graph = new Fhaculty\Graph\Graph(); + +$blue = $graph->createVertex('blue'); +$blue->setAttribute('graphviz.label', 'Hello world!'); +``` + +Note that all [attributes](#attributes) will be quoted and escaped by default, +so a `>` will appear as-is and will not be interpreted as HTML. See also +[HTML-like labels](#html-like-labels) below for more details. + +### Edge labels + +By default, GraphViz will not render any label on an edge: + +```php +$graph = new Fhaculty\Graph\Graph(); + +$a = $graph->createVertex('a'); +$b = $graph->createVertex('b'); + +$edge = $a->createEdgeTo($b); +``` + +If you assign an edge flow, capacity or weight, this library will automatically +include a `label` attribute that includes these values. The following example +will automatically assign `100` as the label for the weighted edge: + +```php +$graph = new Fhaculty\Graph\Graph(); + +$a = $graph->createVertex('a'); +$b = $graph->createVertex('b'); + +$edge = $a->createEdgeTo($b); +$edge->setWeight(100); +``` + +The following example will automatically assign `4/10` as the label for an edge +with both flow and maximum capacity set: + +```php +$graph = new Fhaculty\Graph\Graph(); + +$a = $graph->createVertex('a'); +$b = $graph->createVertex('b'); + +$edge = $a->createEdgeTo($b); +$edge->setFlow(4); +$edge->setCapacity(10); +``` + +The following example will automatically assign `4/∞/100` as the label for a +weighted edge with a flow and unlimited capacity: + +```php +$graph = new Fhaculty\Graph\Graph(); + +$a = $graph->createVertex('a'); +$b = $graph->createVertex('b'); + +$edge = $a->createEdgeTo($b); +$edge->setFlow(4); +$edge->setCapacity(null); +$edge->setWeight(100); +``` + +You can use [edge attributes](#edge-attributes) to explicitly assign any +custom `label` attribute. Note that any flow, capacity or weight value will still +be appended like in the previous examples. + +```php +$graph = new Fhaculty\Graph\Graph(); + +$a = $graph->createVertex('a'); +$b = $graph->createVertex('b'); + +$edge = $a->createEdgeTo($b); +$edge->setAttribute('graphviz.label', 'important'); +``` + +### HTML-like labels + +Note that all [attributes](#attributes) will be quoted and escaped by default, +so a `>` will appear as-is and will not be interpreted as HTML. GraphViz also +supports [HTML-like labels](https://graphviz.gitlab.io/_pages/doc/info/shapes.html#html) +that support a subset of HTML features. + +GraphViz requires any HTML-like label to be wrapped in `<` and `>` and only +supports a limited subset of HTML features as documented above. In order to +prevent automatic quoting and escaping, all attribute values have to be passed +to the static `GraphViz::raw()` helper like this: + +```php +$graph = new Fhaculty\Graph\Graph(); + +$a = $graph->createVertex('Entity'); +$a->setAttribute('graphviz.shape', 'none'); +$a->setAttribute('graphviz.label', GraphViz::raw('< + + + + +
\N
+ touch()
>')); + +$b = $graph->createVertex('Block'); +$b->createEdgeTo($a); +$b->setAttribute('graphviz.shape', 'none'); +$b->setAttribute('graphviz.label', GraphViz::raw('< + + + + +
\N
- size:int
+ touch()
>')); +``` + +![UML html graph example](examples/11-uml-html.png) + +See also the [examples](examples/). + +### Record-based nodes + +Note that all [attributes](#attributes) will be quoted and escaped by default, +so a `>` will appear as-is and will not be interpreted as HTML. Similar to the +above [HTML-like labels](#html-like-labels), GraphViz also supports simple +[record-based nodes](https://graphviz.gitlab.io/_pages/doc/info/shapes.html#record) +using the `record` and `Mrecord` shape attributes and structured label attributes. + +GraphViz requires any record-based node label to be quoted, but uses special +syntax to mark record fields and optional port names. In order to prevent +automatic quoting and escaping, all attribute values have to be quoted manually +and passed to the static `GraphViz::raw()` helper like this: + +```php +$graph = new Fhaculty\Graph\Graph(); + +$a = $graph->createVertex(); +$a->setAttribute('graphviz.shape', 'Mrecord'); +$a->setAttribute('graphviz.label', GraphViz::raw('" left | middle | right"')); + +$b = $graph->createVertex(); +$b->setAttribute('graphviz.shape', 'Mrecord'); +$b->setAttribute('graphviz.label', GraphViz::raw('" left | middle | right"')); + +// a:middle -> b:right +$edge = $a->createEdgeTo($b); +$edge->setAttribute('graphviz.tailport', 'middle'); +$edge->setAttribute('graphviz.headport', 'right'); +``` + +![records with ports graph example](examples/13-record-ports.png) + +See also the [examples](examples/). + +## Install + +The recommended way to install this library is [through Composer](https://getcomposer.org). +[New to Composer?](https://getcomposer.org/doc/00-intro.md) + +This will install the latest supported version: + +```bash +$ composer require graphp/graphviz:^0.2.2 +``` + +See also the [CHANGELOG](CHANGELOG.md) for details about version upgrades. + +This project aims to run on any platform and thus does not require any PHP +extensions and supports running on legacy PHP 5.3 through current PHP 7+ and +HHVM. +It's *highly recommended to use PHP 7+* for this project. + +The graph drawing feature is powered by the excellent [GraphViz](https://www.graphviz.org) +software. This means you'll have to install GraphViz (`dot` executable). +The [Graphviz homepage](https://www.graphviz.org/download/) includes complete +installation instructions for most common platforms, users of Debian/Ubuntu-based +distributions may simply invoke: + +```bash +$ sudo apt install graphviz +``` + +## Tests + +To run the test suite, you first need to clone this repo and then install all +dependencies [through Composer](https://getcomposer.org): + +```bash +$ composer install +``` + +To run the test suite, go to the project root and run: + +```bash +$ php vendor/bin/phpunit +``` + +## License + +Released under the terms of the permissive [MIT license](http://opensource.org/licenses/MIT). diff --git a/vendor/graphp/graphviz/composer.json b/vendor/graphp/graphviz/composer.json new file mode 100644 index 0000000000000000000000000000000000000000..c31880e1de1b2cf557b3a652e71e0b11e4eb1ccf --- /dev/null +++ b/vendor/graphp/graphviz/composer.json @@ -0,0 +1,18 @@ +{ + "name": "graphp/graphviz", + "type": "library", + "description": "GraphViz graph drawing for the mathematical graph/network library GraPHP.", + "keywords": ["GraphViz", "graph drawing", "graph image", "dot output", "GraPHP"], + "homepage": "https://github.com/graphp/graphviz", + "license": "MIT", + "autoload": { + "psr-4": {"Graphp\\GraphViz\\": "src/"} + }, + "require": { + "php": ">=5.3.0", + "clue/graph": "~0.9.0|~0.8.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.4 || ^5.7 || ^4.8.35" + } +} diff --git a/vendor/graphp/graphviz/examples/01-simple.php b/vendor/graphp/graphviz/examples/01-simple.php new file mode 100644 index 0000000000000000000000000000000000000000..2aa179316cf897ff5e0068ee570656733b0e8122 --- /dev/null +++ b/vendor/graphp/graphviz/examples/01-simple.php @@ -0,0 +1,17 @@ +createVertex('blue'); +$blue->setAttribute('graphviz.color', 'blue'); + +$red = $graph->createVertex('red'); +$red->setAttribute('graphviz.color', 'red'); + +$edge = $blue->createEdgeTo($red); +$edge->setAttribute('graphviz.color', 'grey'); + +$graphviz = new Graphp\GraphViz\GraphViz(); +$graphviz->display($graph); diff --git a/vendor/graphp/graphviz/examples/01-simple.png b/vendor/graphp/graphviz/examples/01-simple.png new file mode 100644 index 0000000000000000000000000000000000000000..e0f2dc57c86754a9c7f23572825a8317024ad2db Binary files /dev/null and b/vendor/graphp/graphviz/examples/01-simple.png differ diff --git a/vendor/graphp/graphviz/examples/02-html.php b/vendor/graphp/graphviz/examples/02-html.php new file mode 100644 index 0000000000000000000000000000000000000000..0207520ed0fcbdcbf7f46b51bd3a12d2e095f662 --- /dev/null +++ b/vendor/graphp/graphviz/examples/02-html.php @@ -0,0 +1,25 @@ +setAttribute('graphviz.graph.rankdir', 'LR'); + +$hello = $graph->createVertex('hello'); +$world = $graph->createVertex('wörld'); +$hello->createEdgeTo($world); + +$graphviz = new Graphp\GraphViz\GraphViz(); +$graphviz->setFormat('svg'); + +echo ' + + +hello wörld + +' . $graphviz->createImageHtml($graph) . ' + + +'; diff --git a/vendor/graphp/graphviz/examples/02-html.png b/vendor/graphp/graphviz/examples/02-html.png new file mode 100644 index 0000000000000000000000000000000000000000..847cb2a220c62049b9c5112443e54498788c49cb Binary files /dev/null and b/vendor/graphp/graphviz/examples/02-html.png differ diff --git a/vendor/graphp/graphviz/examples/11-uml-html.php b/vendor/graphp/graphviz/examples/11-uml-html.php new file mode 100644 index 0000000000000000000000000000000000000000..c3ec0d4cf909118d25695a278a96bfe646162f8b --- /dev/null +++ b/vendor/graphp/graphviz/examples/11-uml-html.php @@ -0,0 +1,30 @@ +createVertex('Entity'); +$a->setAttribute('graphviz.shape', 'none'); +$a->setAttribute('graphviz.label', GraphViz::raw('< + + + + +
\N
+ touch()
>')); + +$b = $graph->createVertex('Block'); +$b->createEdgeTo($a); +$b->setAttribute('graphviz.shape', 'none'); +$b->setAttribute('graphviz.label', GraphViz::raw('< + + + + +
\N
- size:int
+ touch()
>')); + +$graphviz = new GraphViz(); +echo $graphviz->createScript($graph); +$graphviz->display($graph); diff --git a/vendor/graphp/graphviz/examples/11-uml-html.png b/vendor/graphp/graphviz/examples/11-uml-html.png new file mode 100644 index 0000000000000000000000000000000000000000..2d79540cbab1a6793089bfdc53d3ac0590d513d2 Binary files /dev/null and b/vendor/graphp/graphviz/examples/11-uml-html.png differ diff --git a/vendor/graphp/graphviz/examples/12-uml-records.php b/vendor/graphp/graphviz/examples/12-uml-records.php new file mode 100644 index 0000000000000000000000000000000000000000..9c60f1163730a812d9c87de7b0fc3041b9f56031 --- /dev/null +++ b/vendor/graphp/graphviz/examples/12-uml-records.php @@ -0,0 +1,20 @@ +createVertex('Entity'); +$a->setAttribute('graphviz.shape', 'record'); +$a->setAttribute('graphviz.label', GraphViz::raw('"{\N||+ touch()}"')); + +$b = $graph->createVertex('Block'); +$b->createEdgeTo($a); +$b->setAttribute('graphviz.shape', 'record'); +$b->setAttribute('graphviz.label', GraphViz::raw('"{\N|- size:int|+ touch()}"')); + +$graphviz = new GraphViz(); +echo $graphviz->createScript($graph); +$graphviz->display($graph); diff --git a/vendor/graphp/graphviz/examples/12-uml-records.png b/vendor/graphp/graphviz/examples/12-uml-records.png new file mode 100644 index 0000000000000000000000000000000000000000..67849b0062e19cfe9212ed15d5c70777cd59e65f Binary files /dev/null and b/vendor/graphp/graphviz/examples/12-uml-records.png differ diff --git a/vendor/graphp/graphviz/examples/13-record-ports.php b/vendor/graphp/graphviz/examples/13-record-ports.php new file mode 100644 index 0000000000000000000000000000000000000000..f10dfb4b4c2de2425774feccb8e9269645d1c960 --- /dev/null +++ b/vendor/graphp/graphviz/examples/13-record-ports.php @@ -0,0 +1,24 @@ +createVertex(); +$a->setAttribute('graphviz.shape', 'Mrecord'); +$a->setAttribute('graphviz.label', GraphViz::raw('" left | middle | right"')); + +$b = $graph->createVertex(); +$b->setAttribute('graphviz.shape', 'Mrecord'); +$b->setAttribute('graphviz.label', GraphViz::raw('" left | middle | right"')); + +// a:middle -> b:right +$edge = $a->createEdgeTo($b); +$edge->setAttribute('graphviz.tailport', 'middle'); +$edge->setAttribute('graphviz.headport', 'right'); + +$graphviz = new GraphViz(); +echo $graphviz->createScript($graph); +$graphviz->display($graph); diff --git a/vendor/graphp/graphviz/examples/13-record-ports.png b/vendor/graphp/graphviz/examples/13-record-ports.png new file mode 100644 index 0000000000000000000000000000000000000000..f0b7026e512ebcdea320c387e99aa0237f9a0042 Binary files /dev/null and b/vendor/graphp/graphviz/examples/13-record-ports.png differ diff --git a/vendor/graphp/graphviz/phpunit.xml.dist b/vendor/graphp/graphviz/phpunit.xml.dist new file mode 100644 index 0000000000000000000000000000000000000000..7d3929a2f1378a2ecdebffe8cdb5f69afa1ffe0a --- /dev/null +++ b/vendor/graphp/graphviz/phpunit.xml.dist @@ -0,0 +1,19 @@ + + + + + + ./tests/ + + + + + ./src + + + \ No newline at end of file diff --git a/vendor/graphp/graphviz/src/Dot.php b/vendor/graphp/graphviz/src/Dot.php new file mode 100644 index 0000000000000000000000000000000000000000..dce2b8b58c32df9e7655149248481b76e26f3329 --- /dev/null +++ b/vendor/graphp/graphviz/src/Dot.php @@ -0,0 +1,26 @@ +graphviz = $graphviz; + } + + public function getOutput(Graph $graph) + { + return $this->graphviz->createScript($graph); + } +} diff --git a/vendor/graphp/graphviz/src/GraphViz.php b/vendor/graphp/graphviz/src/GraphViz.php new file mode 100644 index 0000000000000000000000000000000000000000..53dc3e2e39d6e2765899f1cb81d26f5c0418dab8 --- /dev/null +++ b/vendor/graphp/graphviz/src/GraphViz.php @@ -0,0 +1,446 @@ +executable = 'dot.exe'; + } + } + + /** + * Change the executable to use. + * + * Usually, your graphviz executables should be located in your $PATH + * environment variable and invoking a mere `dot` is sufficient. If you + * have no access to your $PATH variable, use this method to set the path + * to your graphviz dot executable. + * + * This should contain '.exe' on windows. + * - /full/path/to/bin/dot + * - neato + * - dot.exe + * - c:\path\to\bin\dot.exe + * + * @param string $executable + * @return GraphViz $this (chainable) + */ + public function setExecutable($executable) { + $this->executable = $executable; + + return $this; + } + + /** + * return executable to use + * + * @return string + * @see GraphViz::setExecutable() + */ + public function getExecutable() { + return $this->executable; + } + + /** + * set graph image output format + * + * @param string $format png, svg, ps2, etc. (see 'man dot' for details on parameter '-T') + * @return GraphViz $this (chainable) + */ + public function setFormat($format) + { + $this->format = $format; + + return $this; + } + + /** + * create and display image for this graph + * + * @param Graph $graph graph to display + * @return void + * @uses GraphViz::createImageFile() + */ + public function display(Graph $graph) + { + // echo "Generate picture ..."; + $tmp = $this->createImageFile($graph); + + static $next = 0; + if ($next > microtime(true)) { + // wait some time between calling xdg-open because earlier calls will be ignored otherwise + //echo '[delay flooding xdg-open]' . PHP_EOL; + sleep(self::DELAY_OPEN); + } + + if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { + // open image in untitled, temporary background shell + exec('start "" ' . escapeshellarg($tmp) . ' >NUL'); + } elseif (strtoupper(PHP_OS) === 'DARWIN') { + // open image in background (redirect stdout to /dev/null, sterr to stdout and run in background) + exec('open ' . escapeshellarg($tmp) . ' > /dev/null 2>&1 &'); + } else { + // open image in background (redirect stdout to /dev/null, sterr to stdout and run in background) + exec('xdg-open ' . escapeshellarg($tmp) . ' > /dev/null 2>&1 &'); + } + + $next = microtime(true) + self::DELAY_OPEN; + // echo "... done\n"; + } + + /** + * create image file data contents for this graph + * + * @param Graph $graph graph to display + * @return string + * @uses GraphViz::createImageFile() + */ + public function createImageData(Graph $graph) + { + $file = $this->createImageFile($graph); + $data = file_get_contents($file); + unlink($file); + + return $data; + } + + /** + * create base64-encoded image src target data to be used for html images + * + * @param Graph $graph graph to display + * @return string + * @uses GraphViz::createImageData() + */ + public function createImageSrc(Graph $graph) + { + $format = $this->format; + if ($this->format === 'svg' || $this->format === 'svgz') { + $format = 'svg+xml;charset=' . $graph->getAttribute('graphviz.graph.charset', 'UTF-8'); + } + + return 'data:image/' . $format . ';base64,' . base64_encode($this->createImageData($graph)); + } + + /** + * create image html code for this graph + * + * @param Graph $graph graph to display + * @return string + * @uses GraphViz::createImageSrc() + */ + public function createImageHtml(Graph $graph) + { + if ($this->format === 'svg' || $this->format === 'svgz') { + return ''; + } + + return ''; + } + + /** + * create image file for this graph + * + * @param Graph $graph graph to display + * @return string filename + * @throws UnexpectedValueException on error + * @uses GraphViz::createScript() + */ + public function createImageFile(Graph $graph) + { + $script = $this->createScript($graph); + // var_dump($script); + + $tmp = tempnam(sys_get_temp_dir(), 'graphviz'); + if ($tmp === false) { + throw new UnexpectedValueException('Unable to get temporary file name for graphviz script'); + } + + $ret = file_put_contents($tmp, $script, LOCK_EX); + if ($ret === false) { + throw new UnexpectedValueException('Unable to write graphviz script to temporary file'); + } + + $ret = 0; + + $executable = $this->getExecutable(); + system(escapeshellarg($executable) . ' -T ' . escapeshellarg($this->format) . ' ' . escapeshellarg($tmp) . ' -o ' . escapeshellarg($tmp . '.' . $this->format), $ret); + if ($ret !== 0) { + throw new UnexpectedValueException('Unable to invoke "' . $executable .'" to create image file (code ' . $ret . ')'); + } + + unlink($tmp); + + return $tmp . '.' . $this->format; + } + + /** + * create graphviz script representing this graph + * + * @param Graph $graph graph to display + * @return string + * @uses Directed::hasDirected() + * @uses Graph::getVertices() + * @uses Graph::getEdges() + */ + public function createScript(Graph $graph) + { + $directed = false; + foreach ($graph->getEdges() as $edge) { + if ($edge instanceof EdgeDirected) { + $directed = true; + break; + } + } + + /* + * The website [http://www.graphviz.org/content/dot-language] uses the term `ID` when displaying + * the abstract grammar for the DOT language. + * But the man pages for dot use the term `name` when describing the graph file language. + */ + $name = $graph->getAttribute('graphviz.name'); + if ($name !== null) { + $name = $this->escapeId($name) . ' '; + } + + $script = ($directed ? 'di':'') . 'graph ' . $name . '{' . self::EOL; + + // add global attributes + $globals = array( + 'graph' => 'graphviz.graph.', + 'node' => 'graphviz.node.', + 'edge' => 'graphviz.edge.', + ); + + foreach ($globals as $key => $prefix) { + $bag = new AttributeBagNamespaced($graph, $prefix); + + if ($layout = $bag->getAttributes()) { + $script .= $this->formatIndent . $key . ' ' . $this->escapeAttributes($layout) . self::EOL; + } + } + + $groups = array(); + foreach ($graph->getVertices()->getMap() as $vid => $vertex) { + $groups[$vertex->getGroup()][$vid] = $vertex; + } + + // only cluster vertices into groups if there are at least 2 different groups + if (count($groups) > 1) { + $indent = str_repeat($this->formatIndent, 2); + // put each group of vertices in a separate subgraph cluster + foreach ($groups as $group => $vertices) { + $script .= $this->formatIndent . 'subgraph cluster_' . $group . ' {' . self::EOL . + $indent . 'label = ' . $this->escape($group) . self::EOL; + foreach ($vertices as $vid => $vertex) { + $layout = $this->getLayoutVertex($vertex); + + $script .= $indent . $this->escapeId($vid); + if ($layout) { + $script .= ' ' . $this->escapeAttributes($layout); + } + $script .= self::EOL; + } + $script .= ' }' . self::EOL; + } + } else { + // explicitly add all isolated vertices (vertices with no edges) and vertices with special layout set + // other vertices wil be added automatically due to below edge definitions + foreach ($graph->getVertices()->getMap() as $vid => $vertex){ + $layout = $this->getLayoutVertex($vertex); + + if ($layout || $vertex->getEdges()->isEmpty()) { + $script .= $this->formatIndent . $this->escapeId($vid); + if ($layout) { + $script .= ' ' . $this->escapeAttributes($layout); + } + $script .= self::EOL; + } + } + } + + $edgeop = $directed ? ' -> ' : ' -- '; + + // add all edges as directed edges + foreach ($graph->getEdges() as $currentEdge) { + $both = $currentEdge->getVertices()->getVector(); + $currentStartVertex = $both[0]; + $currentTargetVertex = $both[1]; + + $script .= $this->formatIndent . $this->escapeId($currentStartVertex->getId()) . $edgeop . $this->escapeId($currentTargetVertex->getId()); + + $layout = $this->getLayoutEdge($currentEdge); + + // this edge is not a loop and also points to the opposite direction => this is actually an undirected edge + if ($directed && $currentStartVertex !== $currentTargetVertex && $currentEdge->isConnection($currentTargetVertex, $currentStartVertex)) { + $layout['dir'] = 'none'; + } + if ($layout) { + $script .= ' ' . $this->escapeAttributes($layout); + } + + $script .= self::EOL; + } + $script .= '}' . self::EOL; + + return $script; + } + + /** + * escape given id string and wrap in quotes if needed + * + * @param string $id + * @return string + * @link http://graphviz.org/content/dot-language + */ + private function escapeId($id) + { + return self::escape($id); + } + + public static function escape($id) + { + // see raw() + if ($id instanceof \stdClass && isset($id->string)) { + return $id->string; + } + // see @link: There is no semantic difference between abc_2 and "abc_2" + // numeric or simple string, no need to quote (only for simplicity) + if (preg_match('/^(?:\-?(?:\.\d+|\d+(?:\.\d+)?))$/i', $id)) { + return $id; + } + + return '"' . str_replace(array('&', '<', '>', '"', "'", '\\', "\n"), array('&', '<', '>', '"', ''', '\\\\', '\\l'), $id) . '"'; + } + + /** + * get escaped attribute string for given array of (unescaped) attributes + * + * @param array $attrs + * @return string + * @uses GraphViz::escapeId() + */ + private function escapeAttributes($attrs) + { + $script = '['; + $first = true; + foreach ($attrs as $name => $value) { + if ($first) { + $first = false; + } else { + $script .= ' '; + } + $script .= $name . '=' . self::escape($value); + } + $script .= ']'; + + return $script; + } + + /** + * create a raw string representation, i.e. do NOT escape the given string when used in graphviz output + * + * @param string $string + * @return \stdClass + * @see GraphViz::escape() + */ + public static function raw($string) + { + return (object) array('string' => $string); + } + + protected function getLayoutVertex(Vertex $vertex) + { + $bag = new AttributeBagNamespaced($vertex, 'graphviz.'); + $layout = $bag->getAttributes(); + + $balance = $vertex->getBalance(); + if($balance !== NULL){ + if($balance > 0){ + $balance = '+' . $balance; + } + if(!isset($layout['label'])){ + $layout['label'] = $vertex->getId(); + } + $layout['label'] .= ' (' . $balance . ')'; + } + + return $layout; + } + + protected function getLayoutEdge(Edge $edge) + { + $bag = new AttributeBagNamespaced($edge, 'graphviz.'); + $layout = $bag->getAttributes(); + + // use flow/capacity/weight as edge label + $label = NULL; + + $flow = $edge->getFlow(); + $capacity = $edge->getCapacity(); + // flow is set + if ($flow !== NULL) { + // NULL capacity = infinite capacity + $label = $flow . '/' . ($capacity === NULL ? '∞' : $capacity); + // capacity set, but not flow (assume zero flow) + } elseif ($capacity !== NULL) { + $label = '0/' . $capacity; + } + + $weight = $edge->getWeight(); + // weight is set + if ($weight !== NULL) { + if ($label === NULL) { + $label = $weight; + } else { + $label .= '/' . $weight; + } + } + + if ($label !== NULL) { + if (isset($layout['label'])) { + $layout['label'] .= ' ' . $label; + } else { + $layout['label'] = $label; + } + } + return $layout; + } +} diff --git a/vendor/graphp/graphviz/src/Image.php b/vendor/graphp/graphviz/src/Image.php new file mode 100644 index 0000000000000000000000000000000000000000..9b1aaca7d533bdea102377183cacee90b45dca45 --- /dev/null +++ b/vendor/graphp/graphviz/src/Image.php @@ -0,0 +1,40 @@ +setFormat('png'); + } + + $this->graphviz = $graphviz; + } + + public function getOutput(Graph $graph) + { + return $this->graphviz->createImageData($graph); + } + + /** + * set the image output format to use + * + * @param string $type png, svg + * @return self $this (chainable) + * @uses GraphViz::setFormat() + */ + public function setFormat($type) + { + $this->graphviz->setFormat($type); + return $this; + } +} diff --git a/vendor/graphp/graphviz/tests/GraphVizTest.php b/vendor/graphp/graphviz/tests/GraphVizTest.php new file mode 100644 index 0000000000000000000000000000000000000000..f1af322c789973c181e20c586f6451e201a7a803 --- /dev/null +++ b/vendor/graphp/graphviz/tests/GraphVizTest.php @@ -0,0 +1,386 @@ +graphViz = new GraphViz(); + } + + public function testGraphEmpty() + { + $graph = new Graph(); + + $expected = <<assertEquals($expected, $this->graphViz->createScript($graph)); + } + + public function testGraphWithName() + { + $graph = new Graph(); + $graph->setAttribute('graphviz.name', 'G'); + + $expected = <<assertEquals($expected, $this->graphViz->createScript($graph)); + } + + public function testGraphWithNameWithSpaces() + { + $graph = new Graph(); + $graph->setAttribute('graphviz.name', 'My Graph Name'); + + $expected = <<assertEquals($expected, $this->graphViz->createScript($graph)); + } + + public function testGraphIsolatedVertices() + { + $graph = new Graph(); + $graph->createVertex('a'); + $graph->createVertex('b'); + + $expected = <<assertEquals($expected, $this->graphViz->createScript($graph)); + } + + public function testGraphIsolatedVerticesWithGroupsWillBeAddedToClusters() + { + $graph = new Graph(); + $graph->createVertex('a')->setGroup(0); + $graph->createVertex('b')->setGroup(1)->setAttribute('graphviz.label', 'second'); + + $expected = <<assertEquals($expected, $this->graphViz->createScript($graph)); + } + + public function testGraphDefaultAttributes() + { + $graph = new Graph(); + $graph->setAttribute('graphviz.graph.bgcolor', 'transparent'); + $graph->setAttribute('graphviz.node.color', 'blue'); + $graph->setAttribute('graphviz.edge.color', 'grey'); + + $expected = <<assertEquals($expected, $this->graphViz->createScript($graph)); + } + + public function testUnknownGraphAttributesWillBeDiscarded() + { + $graph = new Graph(); + $graph->setAttribute('graphviz.vertex.color', 'blue'); + $graph->setAttribute('graphviz.unknown.color', 'red'); + + $expected = <<assertEquals($expected, $this->graphViz->createScript($graph)); + } + + public function testEscaping() + { + $graph = new Graph(); + $graph->createVertex('a'); + $graph->createVertex('b¹²³ is; ok\\ay, "right"?'); + $graph->createVertex(3); + $graph->createVertex(4)->setAttribute('graphviz.label', 'normal'); + $graph->createVertex(5)->setAttribute('graphviz.label', GraphViz::raw('')); + + + $expected = <<] +} + +VIZ; + + $this->assertEquals($expected, $this->graphViz->createScript($graph)); + } + + public function testGraphWithSimpleEdgeUsesGraphWithSimpleEdgeDefinition() + { + // a -- b + $graph = new Graph(); + $graph->createVertex('a')->createEdge($graph->createVertex('b')); + + $expected = <<assertEquals($expected, $this->graphViz->createScript($graph)); + } + + public function testGraphWithLoopUsesGraphWithSimpleLoopDefinition() + { + // a -- b -\ + // | | + // \--/ + $graph = new Graph(); + $graph->createVertex('a')->createEdge($graph->createVertex('b')); + $graph->getVertex('b')->createEdge($graph->getVertex('b')); + + $expected = <<assertEquals($expected, $this->graphViz->createScript($graph)); + } + + public function testGraphDirectedUsesDigraph() + { + $graph = new Graph(); + $graph->createVertex('a')->createEdgeTo($graph->createVertex('b')); + + $expected = << "b" +} + +VIZ; + + $this->assertEquals($expected, $this->graphViz->createScript($graph)); + } + + public function testGraphDirectedWithLoopUsesDigraphWithSimpleLoopDefinition() + { + // a -> b -\ + // ^ | + // \--/ + $graph = new Graph(); + $graph->createVertex('a')->createEdgeTo($graph->createVertex('b')); + $graph->getVertex('b')->createEdgeTo($graph->getVertex('b')); + + $expected = << "b" + "b" -> "b" +} + +VIZ; + + $this->assertEquals($expected, $this->graphViz->createScript($graph)); + } + + public function testGraphMixedUsesDigraphWithExplicitDirectionNoneForUndirectedEdges() + { + // a -> b -- c + $graph = new Graph(); + $graph->createVertex('a')->createEdgeTo($graph->createVertex('b')); + $graph->createVertex('c')->createEdge($graph->getVertex('b')); + + $expected = << "b" + "c" -> "b" [dir="none"] +} + +VIZ; + + $this->assertEquals($expected, $this->graphViz->createScript($graph)); + } + + public function testGraphMixedWithDirectedLoopUsesDigraphWithoutDirectionForDirectedLoop() + { + // a -- b -\ + // ^ | + // \--/ + $graph = new Graph(); + $graph->createVertex('a')->createEdge($graph->createVertex('b')); + $graph->getVertex('b')->createEdgeTo($graph->getVertex('b')); + + $expected = << "b" [dir="none"] + "b" -> "b" +} + +VIZ; + + $this->assertEquals($expected, $this->graphViz->createScript($graph)); + } + + public function testGraphUndirectedWithIsolatedVerticesFirst() + { + // a -- b -- c d + $graph = new Graph(); + $graph->createVertices(array('a', 'b', 'c', 'd')); + $graph->getVertex('a')->createEdge($graph->getVertex('b')); + $graph->getVertex('b')->createEdge($graph->getVertex('c')); + + $expected = <<assertEquals($expected, $this->graphViz->createScript($graph)); + } + + public function testVertexLabels() + { + $graph = new Graph(); + $graph->createVertex('a')->setBalance(1); + $graph->createVertex('b')->setBalance(0); + $graph->createVertex('c')->setBalance(-1); + $graph->createVertex('d')->setAttribute('graphviz.label', 'test'); + $graph->createVertex('e')->setBalance(2)->setAttribute('graphviz.label', 'unnamed'); + + $expected = <<assertEquals($expected, $this->graphViz->createScript($graph)); + } + + public function testEdgeLayoutAtributes() + { + $graph = new Graph(); + $graph->createVertex('1a')->createEdge($graph->createVertex('1b')); + $graph->createVertex('2a')->createEdge($graph->createVertex('2b'))->setAttribute('graphviz.numeric', 20); + $graph->createVertex('3a')->createEdge($graph->createVertex('3b'))->setAttribute('graphviz.textual', "forty"); + $graph->createVertex('4a')->createEdge($graph->createVertex('4b'))->getAttributeBag()->setAttributes(array('graphviz.1' => 1, 'graphviz.2' => 2)); + $graph->createVertex('5a')->createEdge($graph->createVertex('5b'))->getAttributeBag()->setAttributes(array('graphviz.a' => 'b', 'graphviz.c' => 'd')); + + $expected = <<assertEquals($expected, $this->graphViz->createScript($graph)); + } + + public function testEdgeLabels() + { + $graph = new Graph(); + $graph->createVertex('1a')->createEdge($graph->createVertex('1b')); + $graph->createVertex('2a')->createEdge($graph->createVertex('2b'))->setWeight(20); + $graph->createVertex('3a')->createEdge($graph->createVertex('3b'))->setCapacity(30); + $graph->createVertex('4a')->createEdge($graph->createVertex('4b'))->setFlow(40); + $graph->createVertex('5a')->createEdge($graph->createVertex('5b'))->setFlow(50)->setCapacity(60); + $graph->createVertex('6a')->createEdge($graph->createVertex('6b'))->setFlow(60)->setCapacity(70)->setWeight(80); + $graph->createVertex('7a')->createEdge($graph->createVertex('7b'))->setFlow(70)->setAttribute('graphviz.label', 'prefixed'); + + $expected = <<assertEquals($expected, $this->graphViz->createScript($graph)); + } + + public function testCreateImageSrcWillExportPngDefaultFormat() + { + $graph = new Graph(); + + $src = $this->graphViz->createImageSrc($graph); + + $this->assertStringStartsWith('data:image/png;base64,', $src); + } + + public function testCreateImageSrcAsSvgWithUtf8DefaultCharset() + { + $graph = new Graph(); + + $this->graphViz->setFormat('svg'); + $src = $this->graphViz->createImageSrc($graph); + + $this->assertStringStartsWith('data:image/svg+xml;charset=UTF-8;base64,', $src); + } + + public function testCreateImageSrcAsSvgzWithExplicitIsoCharsetLatin1() + { + $graph = new Graph(); + $graph->setAttribute('graphviz.graph.charset', 'iso-8859-1'); + + $this->graphViz->setFormat('svgz'); + $src = $this->graphViz->createImageSrc($graph); + + $this->assertStringStartsWith('data:image/svg+xml;charset=iso-8859-1;base64,', $src); + } +}