Full Code of graphp/algorithms for AI

0.9.x 269389305c94 cached
92 files
238.7 KB
63.8k tokens
451 symbols
1 requests
Download .txt
Showing preview only (261K chars total). Download the full file or copy to clipboard to get everything.
Repository: graphp/algorithms
Branch: 0.9.x
Commit: 269389305c94
Files: 92
Total size: 238.7 KB

Directory structure:
gitextract_dqkgq7nx/

├── .gitattributes
├── .github/
│   └── workflows/
│       └── ci.yml
├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── README.md
├── composer.json
├── phpunit.xml.dist
├── phpunit.xml.legacy
├── src/
│   ├── Base.php
│   ├── BaseDual.php
│   ├── BaseGraph.php
│   ├── BaseVertex.php
│   ├── Bipartit.php
│   ├── Complete.php
│   ├── ConnectedComponents.php
│   ├── Degree.php
│   ├── DetectNegativeCycle.php
│   ├── Directed.php
│   ├── Eulerian.php
│   ├── Flow.php
│   ├── Groups.php
│   ├── Loop.php
│   ├── MaxFlow/
│   │   └── EdmondsKarp.php
│   ├── MaximumMatching/
│   │   ├── Base.php
│   │   └── Flow.php
│   ├── MinimumCostFlow/
│   │   ├── Base.php
│   │   ├── CycleCanceling.php
│   │   └── SuccessiveShortestPath.php
│   ├── MinimumSpanningTree/
│   │   ├── Base.php
│   │   ├── Kruskal.php
│   │   └── Prim.php
│   ├── Parallel.php
│   ├── Property/
│   │   ├── GraphProperty.php
│   │   └── WalkProperty.php
│   ├── ResidualGraph.php
│   ├── Search/
│   │   ├── Base.php
│   │   ├── BreadthFirst.php
│   │   └── DepthFirst.php
│   ├── ShortestPath/
│   │   ├── Base.php
│   │   ├── BreadthFirst.php
│   │   ├── Dijkstra.php
│   │   └── MooreBellmanFord.php
│   ├── Symmetric.php
│   ├── TopologicalSort.php
│   ├── TransposeGraph.php
│   ├── TravelingSalesmanProblem/
│   │   ├── Base.php
│   │   ├── Bruteforce.php
│   │   ├── MinimumSpanningTree.php
│   │   └── NearestNeighbor.php
│   ├── Tree/
│   │   ├── Base.php
│   │   ├── BaseDirected.php
│   │   ├── InTree.php
│   │   ├── OutTree.php
│   │   └── Undirected.php
│   └── Weight.php
└── tests/
    ├── BipartitTest.php
    ├── CompleteTest.php
    ├── ConnectedComponentsTest.php
    ├── DegreeTest.php
    ├── DetectNegativeCycleTest.php
    ├── DirectedTest.php
    ├── EulerianTest.php
    ├── FlowTest.php
    ├── GroupsTest.php
    ├── LoopTest.php
    ├── MaxFlow/
    │   └── EdmondsKarpTest.php
    ├── MaximumMatching/
    │   └── FlowTest.php
    ├── MinimumCostFlow/
    │   ├── BaseMcfTest.php
    │   ├── CycleCancellingTest.php
    │   └── SuccessiveShortestPathTest.php
    ├── MinimumSpanningTree/
    │   ├── BaseMstTest.php
    │   ├── KruskalTest.php
    │   └── PrimTest.php
    ├── ParallelTest.php
    ├── Property/
    │   ├── PropertyGraphTest.php
    │   └── WalkPropertyTest.php
    ├── ResidualGraphTest.php
    ├── Search/
    │   └── BreadthFirstTest.php
    ├── ShortestPath/
    │   ├── BaseShortestPathTest.php
    │   ├── BreadthFirstTest.php
    │   ├── DijkstraTest.php
    │   └── MooreBellmanFordTest.php
    ├── SymmetricTest.php
    ├── TestCase.php
    ├── TopologicalSortTest.php
    ├── TravelingSalesmanProblem/
    │   └── BruteforceTest.php
    ├── Tree/
    │   ├── BaseDirectedTest.php
    │   ├── InTreeTest.php
    │   ├── OutTreeTest.php
    │   └── UndirectedTest.php
    └── WeightTest.php

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitattributes
================================================
/.gitattributes export-ignore
/.github/workflows/ export-ignore
/.gitignore export-ignore
/phpunit.xml.dist export-ignore
/phpunit.xml.legacy export-ignore
/tests export-ignore


================================================
FILE: .github/workflows/ci.yml
================================================
name: CI

on:
  push:
  pull_request:

jobs:
  PHPUnit:
    runs-on: ubuntu-20.04
    strategy:
      matrix:
        php:
          - 7.4
          - 7.3
          - 7.2
          - 7.1
          - 7.0
          - 5.6
          - 5.5
          - 5.4
          - 5.3
    steps:
      - uses: actions/checkout@v2
      - name: Setup PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: ${{ matrix.php }}
      - run: composer install
      - run: vendor/bin/phpunit --coverage-text
        if: ${{ matrix.php >= 7.3 }}
      - run: vendor/bin/phpunit --coverage-text -c phpunit.xml.legacy
        if: ${{ matrix.php < 7.3 }}


================================================
FILE: .gitignore
================================================
/vendor
/composer.lock


================================================
FILE: CHANGELOG.md
================================================
# Changelog

## 0.8.2 (2020-02-20)

*   Feature: Add max depth parameter to breadth first search.
    (#27 by @phyrwork)

*   Feature: Replace recursive topological sort with iterative algorithm.
    (#25 by @phyrwork)

*   Fix: Fix option to merge parallel edges when creating residual graph.
    (#39 by @clue)

*   Fix: Fix setting upper limit for TSP bruteforce via MST algorithm.
    (#36 by @clue)

*   Minor code style improvements to make PHPStan happy,
    clean up dead code for depth first search and
    automated native_function_invocation fixes.
    (#35 and #40 by @clue and #37 by @draco2003)

*   Improve test suite to support PHPUnit 6 and PHPUnit 5 and
    support running on legacy PHP 5.3 through PHP 7.2 and HHVM.
    (#32 by @clue)

## 0.8.1 (2015-03-08)

*   Support graph v0.9 (while keeping BC)
    ([#16](https://github.com/graphp/algorithms/pull/16))

*   Deprecate internal algorithm base classes
    ([#15](https://github.com/graphp/algorithms/pull/15))

## 0.8.0 (2015-02-25)

*   First tagged release, split off from [clue/graph](https://github.com/clue/graph) v0.8.0
    ([#1](https://github.com/graphp/algorithms/issues/1))


================================================
FILE: LICENSE
================================================
The MIT License (MIT)

Copyright (c) 2015 Christian Lück

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.


================================================
FILE: README.md
================================================
# graphp/algorithms

[![CI status](https://github.com/graphp/algorithms/workflows/CI/badge.svg)](https://github.com/graphp/algorithms/actions)

Common mathematical graph algorithms implemented in PHP

> **Development version:** This branch contains the code for the upcoming
> version 0.9. For the code of the current version 0.8, check out the
> [`0.8.x` branch](https://github.com/graphp/algorithms/tree/0.8.x).
>
> The upcoming version 0.9 will be the way forward for this package. However,
> we will still actively support version 0.8 for those not yet on the latest
> version. See also [installation instructions](#install) 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)

Once released, this project will follow [SemVer](https://semver.org/).
At the moment, this will install the latest development version:

```bash
composer require graphp/algorithms:^0.9@dev
```

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+.
It's *highly recommended to use PHP 7+* for this project.

## 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
vendor/bin/phpunit
```

## License

This project is released under the permissive [MIT license](LICENSE).


================================================
FILE: composer.json
================================================
{
    "name": "graphp/algorithms",
    "description": "Common mathematical graph algorithms implemented in PHP",
    "keywords": ["Graph algorithms", "shortest path", "dijkstra", "moore-bellman-ford", "minimum spanning tree", "kruskal", "prim"],
    "homepage": "https://github.com/graphp/algorithms",
    "license": "MIT",
    "authors": [
        {
            "name": "Christian Lück",
            "email": "christian@clue.engineering"
        }
    ],
    "require": {
        "php": ">=5.3",
        "graphp/graph": "1.x-dev#fb198e4 as 1.0.0"
    },
    "require-dev": {
        "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35"
    },
    "autoload": {
        "psr-4": {"Graphp\\Algorithms\\": "src/"}
    },
    "autoload-dev": {
        "psr-4": { "Graphp\\Tests\\Algorithms\\": "tests/" }
    }
}


================================================
FILE: phpunit.xml.dist
================================================
<?xml version="1.0" encoding="UTF-8"?>

<!-- PHPUnit configuration file with new format for PHPUnit 9.3+ -->
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
         xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd"
         bootstrap="vendor/autoload.php" 
         colors="true" 
         cacheResult="false">
    <testsuites>
        <testsuite name="Algorithm Test Suite">
            <directory>./tests/</directory>
        </testsuite>
    </testsuites>
    <coverage>
        <include>
            <directory>./src/</directory>
        </include>
    </coverage>
</phpunit>


================================================
FILE: phpunit.xml.legacy
================================================
<?xml version="1.0" encoding="UTF-8"?>

<!-- PHPUnit configuration file with old format for PHPUnit 9.2 or older-->
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/4.8/phpunit.xsd"
         bootstrap="vendor/autoload.php" 
         colors="true">
    <testsuites>
        <testsuite name="Algorithm Test Suite">
            <directory>./tests/</directory>
        </testsuite>
    </testsuites>
    <filter>
        <whitelist>
            <directory>./src/</directory>
        </whitelist>
    </filter>
</phpunit>


================================================
FILE: src/Base.php
================================================
<?php

namespace Graphp\Algorithms;

/**
 * @deprecated
 */
abstract class Base{ }


================================================
FILE: src/BaseDual.php
================================================
<?php

namespace Graphp\Algorithms;

use Graphp\Graph\Graph;
use Graphp\Graph\Set\DualAggregate;
use Graphp\Graph\Walk;

/**
 * Abstract base class for algorithms that operate on a given Set instance
 *
 * @see Set
 * @deprecated
 */
abstract class BaseDual extends Base
{
    /**
     * Set to operate on
     *
     * @var DualAggregate
     */
    protected $set;

    /**
     * instantiate new algorithm
     *
     * @param Graph|Walk|DualAggregate $graphOrWalk either the Graph or Walk to operate on (or the common base class Set)
     */
    public function __construct(DualAggregate $graphOrWalk)
    {
        $this->set = $graphOrWalk;
    }
}


================================================
FILE: src/BaseGraph.php
================================================
<?php

namespace Graphp\Algorithms;

use Graphp\Graph\Graph;

/**
 * Abstract base class for algorithms that operate on a given Graph instance
 *
 * @deprecated
 */
abstract class BaseGraph extends Base
{
    /**
     * Graph to operate on
     *
     * @var Graph
     */
    protected $graph;

    /**
     * instantiate new algorithm
     *
     * @param Graph $graph Graph to operate on
     */
    public function __construct(Graph $graph)
    {
        $this->graph = $graph;
    }
}


================================================
FILE: src/BaseVertex.php
================================================
<?php

namespace Graphp\Algorithms;

use Graphp\Graph\Vertex;

/**
 * Abstract base class for algorithms that operate on a given Vertex instance
 *
 * @deprecated
 */
abstract class BaseVertex extends Base
{
    /**
     * Vertex to operate on
     *
     * @var Vertex
     */
    protected $vertex;

    /**
     * instantiate new algorithm
     *
     * @param Vertex $vertex Vertex to operate on
     */
    public function __construct(Vertex $vertex)
    {
        $this->vertex = $vertex;
    }
}


================================================
FILE: src/Bipartit.php
================================================
<?php

namespace Graphp\Algorithms;

use Graphp\Graph\Exception\UnexpectedValueException;
use Graphp\Graph\Graph;

class Bipartit extends BaseGraph
{
    /**
     * check whether this graph is bipartit
     *
     * @return bool
     * @uses AlgorithmBipartit::getColors()
     */
    public function isBipartit()
    {
        try {
            $this->getColors();

            return true;
        } catch (UnexpectedValueException $ignore) { }

        return false;
    }

    /**
     * checks whether the input graph's vertex groups are a valid bipartition
     *
     * @return bool
     * @uses AlgorithmGroups::isBipartit()
     */
    public function isBipartitGroups()
    {
        $alg = new Groups($this->graph);

        return $alg->isBipartit();
    }

    /**
     * get map of vertex ID to vertex color
     *
     * @return int[]
     * @throws UnexpectedValueException if graph is not bipartit
     * @uses AlgorithmBipartit::checkVertex() for every vertex not already colored
     */
    public function getColors()
    {
        $colors = array();

        // get color for each vertex
        foreach ($this->graph->getVertices()->getMap() as $vid => $startVertex) {
            if (!isset($colors[$vid])) {
                $queue = array($startVertex);
                // initialize each components color
                $colors[$vid] = 0;

                // breadth search all vertices in same component
                do {
                    // next vertex in color
                    $vertex = \array_shift($queue);
                    $color = $colors[$vertex->getId()];
                    $nextColor = 1-$color;

                    // scan all vertices connected to this vertex
                    foreach ($vertex->getVerticesEdge()->getMap() as $vid => $nextVertex) {
                        // color unknown, so expect next color for this vertex
                        if (!isset($colors[$vid])) {
                            $colors[$vid] = $nextColor;
                            $queue[] = $nextVertex;
                        // color is known but differs => can not be bipartit
                        } elseif ($colors[$vid] !== $nextColor) {
                            throw new UnexpectedValueException('Graph is not bipartit');
                        }
                    }
                } while ($queue);
            }
        }

        return $colors;
    }

    /**
     * get groups of vertices per color
     *
     * @return array[] array of arrays of vertices
     */
    public function getColorVertices()
    {
        $colors = $this->getColors();
        $ret = array(0 => array(), 1 => array());

        foreach ($this->graph->getVertices()->getMap() as $vid => $vertex) {
            $ret[$colors[$vid]][$vid] = $vertex;
        }

        return $ret;
    }

    /**
     * create new graph with valid groups set according to bipartition colors
     *
     * @return Graph
     * @throws UnexpectedValueException if graph is not bipartit
     * @uses AlgorithmBipartit::getColors()
     * @uses Graph::createGraphClone()
     * @uses Vertex::setGroup()
     */
    public function createGraphGroups()
    {
        $colors = $this->getColors();

        $graph = $this->graph->createGraphClone();
        foreach ($graph->getVertices()->getMap() as $vid => $vertex) {
            $vertex->setGroup($colors[$vid]);
        }

        return $graph;
    }
}


================================================
FILE: src/Complete.php
================================================
<?php

namespace Graphp\Algorithms;

/**
 * Basic algorithms for working with complete graphs
 *
 * A complete graph is a graph in which every pair of vertices is connected
 * by an edge.
 *
 * @link http://en.wikipedia.org/wiki/Complete_graph
 * @link http://mathworld.wolfram.com/CompleteGraph.html
 */
class Complete extends BaseGraph
{
    /**
     * checks whether this graph is complete (every vertex has an edge to any other vertex)
     *
     * @return bool
     * @uses Graph::getVertices()
     * @uses Vertex::hasEdgeTo()
     */
    public function isComplete()
    {
        // copy of array (separate iterator but same vertices)
        $c = $vertices = $this->graph->getVertices()->getVector();
        // from each vertex
        foreach ($vertices as $vertex) {
            // to each vertex
            foreach ($c as $other) {
                // missing edge => fail
                if ($other !== $vertex && !$vertex->hasEdgeTo($other)) {
                    return false;
                }
            }
        }

        return true;
    }
}


================================================
FILE: src/ConnectedComponents.php
================================================
<?php

namespace Graphp\Algorithms;

use Graphp\Algorithms\Search\BreadthFirst as SearchBreadthFirst;
use Graphp\Graph\Exception\InvalidArgumentException;
use Graphp\Graph\Exception\UnderflowException;
use Graphp\Graph\Graph;
use Graphp\Graph\Vertex;

/**
 * Algorithm for working with connected components
 *
 * @link http://en.wikipedia.org/wiki/Connected_component_%28graph_theory%29
 * @link http://mathworld.wolfram.com/ConnectedGraph.html
 * @link http://math.stackexchange.com/questions/50551/is-the-empty-graph-connected
 */
class ConnectedComponents extends BaseGraph
{
    /**
     * create subgraph with all vertices connected to given vertex (i.e. the connected component of ths given vertex)
     *
     * @param  Vertex                   $vertex
     * @return Graph
     * @throws InvalidArgumentException if given vertex is not from same graph
     * @uses AlgorithmSearchBreadthFirst::getVertices()
     * @uses Graph::createGraphCloneVertices()
     */
    public function createGraphComponentVertex(Vertex $vertex)
    {
        if ($vertex->getGraph() !== $this->graph) {
            throw new InvalidArgumentException('This graph does not contain the given vertex');
        }

        return $this->graph->createGraphCloneVertices($this->createSearch($vertex)->getVertices());
    }

    /**
     *
     * @param Vertex $vertex
     * @return SearchBreadthFirst
     */
    private function createSearch(Vertex $vertex)
    {
        $alg = new SearchBreadthFirst($vertex);

        // follow into both directions (loosely connected)
        return $alg->setDirection(SearchBreadthFirst::DIRECTION_BOTH);
    }

    /**
     * check whether this graph consists of only a single component
     *
     * If a Graph consists of only a single component, it is said to be a
     * connected Graph, otherwise it's called a disconnected Graph.
     *
     * This method returns exactly the same result as checking
     * <pre>($this->getNumberOfComponents() === 1)</pre>. However, using this
     * method is faster than calling getNumberOfComponents(), as it only has to
     * count all vertices in one component to see if the graph consists of only
     * a single component.
     *
     * As such, a null Graph (a Graph with no vertices) is not considered
     * connected here.
     *
     * @return bool
     * @see self::getNumberOfComponents()
     */
    public function isSingle()
    {
        try {
            $vertex = $this->graph->getVertices()->getVertexFirst();
        } catch (UnderflowException $e) {
            // no first vertex => empty graph => has zero components
            return false;
        }
        $alg = $this->createSearch($vertex);

        return (\count($this->graph->getVertices()) === \count($alg->getVertices()));
    }

    /**
     * count number of connected components
     *
     * A null Graph (a Graph with no vertices) will return 0 components.
     *
     * @return int number of components
     * @uses Graph::getVertices()
     * @uses AlgorithmSearchBreadthFirst::getVertices()
     */
    public function getNumberOfComponents()
    {
        $visitedVertices = array();
        $components = 0;

        // for each vertices
        foreach ($this->graph->getVertices()->getMap() as $vid => $vertex) {
            // did I visit this vertex before?
            if (!isset($visitedVertices[$vid])) {

                // get all vertices of this component
                $newVertices = $this->createSearch($vertex)->getVertices()->getIds();

                ++$components;

                // mark the vertices of this component as visited
                foreach ($newVertices as $vid) {
                    $visitedVertices[$vid] = true;
                }
            }
        }

        // return number of components
        return $components;
    }

    /**
     * separate input graph into separate independant and unconnected graphs
     *
     * @return Graph[]
     * @uses Graph::getVertices()
     * @uses AlgorithmSearchBreadthFirst::getVertices()
     */
    public function createGraphsComponents()
    {
        $visitedVertices = array();
        $graphs = array();

        // for each vertices
        foreach ($this->graph->getVertices()->getMap() as $vid => $vertex) {
            // did I visit this vertex before?
            if (!isset($visitedVertices[$vid])) {

                $alg = $this->createSearch($vertex);
                // get all vertices of this component
                $newVertices = $alg->getVertices();

                // mark the vertices of this component as visited
                foreach ($newVertices->getIds() as $vid) {
                    $visitedVertices[$vid] = true;
                }

                $graphs[] = $this->graph->createGraphCloneVertices($newVertices);
            }
        }

        return $graphs;
    }
}


================================================
FILE: src/Degree.php
================================================
<?php

namespace Graphp\Algorithms;

use Graphp\Graph\Exception\UnderflowException;
use Graphp\Graph\Exception\UnexpectedValueException;
use Graphp\Graph\Vertex;

/**
 * Basic algorithms for working with the degrees of Graphs.
 *
 * The degree (or valency) of a Vertex of a Graph is the number of Edges
 * incident to the Vertex, with Loops counted twice.
 *
 * @link http://en.wikipedia.org/wiki/Degree_%28graph_theory%29
 * @link http://en.wikipedia.org/wiki/Regular_graph
 */
class Degree extends BaseGraph
{
    /**
     * get degree for k-regular-graph (only if each vertex has the same degree)
     *
     * @return int
     * @throws UnderflowException       if graph is empty
     * @throws UnexpectedValueException if graph is not regular (i.e. vertex degrees are not equal)
     * @uses self::getDegreeVertex()
     * @see self::isRegular()
     */
    public function getDegree()
    {
        // get initial degree of any start vertex to compare others to
        $degree = $this->getDegreeVertex($this->graph->getVertices()->getVertexFirst());

        foreach ($this->graph->getVertices() as $vertex) {
            assert($vertex instanceof Vertex);
            $i = $this->getDegreeVertex($vertex);

            if ($i !== $degree) {
                throw new UnexpectedValueException('Graph is not k-regular (vertex degrees differ)');
            }
        }

        return $degree;
    }

    /**
     * get minimum degree of vertices
     *
     * @return int
     * @throws UnderflowException       if graph is empty
     * @uses Vertices::getVertexOrder()
     * @uses self::getDegreeVertex()
     */
    public function getDegreeMin()
    {
        return $this->getDegreeVertex($this->graph->getVertices()->getVertexOrder(array($this, 'getDegreeVertex')));
    }

    /**
     * get maximum degree of vertices
     *
     * @return int
     * @throws UnderflowException       if graph is empty
     * @uses Vertices::getVertexOrder()
     * @uses self::getDegreeVertex()
     */
    public function getDegreeMax()
    {
        return $this->getDegreeVertex($this->graph->getVertices()->getVertexOrder(array($this, 'getDegreeVertex'), true));
    }

    /**
     * checks whether this graph is regular, i.e. each vertex has the same indegree/outdegree
     *
     * @return bool
     * @uses self::getDegree()
     */
    public function isRegular()
    {
        // an empty graph is considered regular
        if ($this->graph->getVertices()->isEmpty()) {
            return true;
        }
        try {
            $this->getDegree();

            return true;
        } catch (UnexpectedValueException $ignore) { }

        return false;
    }

    /**
     * checks whether the indegree of every vertex equals its outdegree
     *
     * @return bool
     * @uses self::getDegreeInVertex()
     * @uses self::getDegreeOutVertex()
     */
    public function isBalanced()
    {
        foreach ($this->graph->getVertices() as $vertex) {
            if ($this->getDegreeInVertex($vertex) !== $this->getDegreeOutVertex($vertex)) {
                return false;
            }
        }

        return true;
    }

    /**
     * checks whether this vertex is a source, i.e. its indegree is zero
     *
     * @param Vertex $vertex
     * @return bool
     * @uses Edge::hasVertexTarget()
     * @see self::getDegreeInVertex()
     */
    public function isVertexSource(Vertex $vertex)
    {
        foreach ($vertex->getEdges() as $edge) {
            if ($edge->hasVertexTarget($vertex)) {
                return false;
            }
        }

        // reach this point: no edge to this vertex
        return true;
    }

    /**
     * checks whether this vertex is a sink, i.e. its outdegree is zero
     *
     * @param Vertex $vertex
     * @return bool
     * @uses Edge::hasVertexStart()
     * @see self::getDegreeOutVertex()
     */
    public function isVertexSink(Vertex $vertex)
    {
        foreach ($vertex->getEdges() as $edge) {
            if ($edge->hasVertexStart($vertex)) {
                return false;
            }
        }

        // reach this point: no edge away from this vertex
        return true;
    }

    /**
     * get degree of this vertex (total number of edges)
     *
     * vertex degree counts the total number of edges attached to this vertex
     * regardless of whether they're directed or not. loop edges are counted
     * twice as both start and end form a 'line' to the same vertex.
     *
     * @param Vertex $vertex
     * @return int
     * @see self::getDegreeInVertex()
     * @see self::getDegreeOutVertex()
     */
    public function getDegreeVertex(Vertex $vertex)
    {
        return \count($vertex->getEdges());
    }

    /**
     * check whether this vertex is isolated (i.e. has no edges attached)
     *
     * @param Vertex $vertex
     * @return bool
     */
    public function isVertexIsolated(Vertex $vertex)
    {
        return $vertex->getEdges()->isEmpty();
    }

    /**
     * get indegree of this vertex (number of edges TO this vertex)
     *
     * @param Vertex $vertex
     * @return int
     * @uses Edge::hasVertexTarget()
     * @see self::getDegreeVertex()
     */
    public function getDegreeInVertex($vertex)
    {
        $n = 0;
        foreach ($vertex->getEdges() as $edge) {
            if ($edge->hasVertexTarget($vertex)) {
                ++$n;
            }
        }

        return $n;
    }

    /**
     * get outdegree of this vertex (number of edges FROM this vertex TO other vertices)
     *
     * @param Vertex $vertex
     * @return int
     * @uses Edge::hasVertexStart()
     * @see self::getDegreeVertex()
     */
    public function getDegreeOutVertex(Vertex $vertex)
    {
        $n = 0;
        foreach ($vertex->getEdges() as $edge) {
            if ($edge->hasVertexStart($vertex)) {
                ++$n;
            }
        }

        return $n;
    }
}


================================================
FILE: src/DetectNegativeCycle.php
================================================
<?php

namespace Graphp\Algorithms;

use Graphp\Algorithms\ShortestPath\MooreBellmanFord as SpMooreBellmanFord;
use Graphp\Graph\Exception\NegativeCycleException;
use Graphp\Graph\Exception\UnderflowException;
use Graphp\Graph\Graph;
use Graphp\Graph\Walk;

class DetectNegativeCycle extends BaseGraph
{
    /**
     * check if the input graph has any negative cycles
     *
     * @return bool
     * @uses AlgorithmDetectNegativeCycle::getCycleNegative()
     */
    public function hasCycleNegative()
    {
        try {
            $this->getCycleNegative();

            // cycle was found => okay
            return true;
        // no cycle found
        } catch (UnderflowException $ignore) {}

        return false;
    }

    /**
     * Searches all vertices for the first negative cycle
     *
     * @return Walk
     * @throws UnderflowException if there's no negative cycle
     * @uses AlgorithmSpMooreBellmanFord::getVertices()
     */
    public function getCycleNegative()
    {
        // remember vertices already visited, as they can not lead to a new cycle
        $verticesVisited = array();
        // check for all vertices
        foreach ($this->graph->getVertices()->getMap() as $vid => $vertex) {
            // skip vertices already visited
            if (!isset($verticesVisited[$vid])) {
                // start MBF algorithm on current vertex
                $alg = new SpMooreBellmanFord($vertex);

                try {
                    // try to get all connected vertices (or throw new cycle)
                    foreach ($alg->getVertices()->getIds() as $vid) {
                        // getting connected vertices succeeded, so skip over all of them
                        $verticesVisited[$vid] = true;
                    // no cycle found, check next vertex...
                    }
                // yey, negative cycle encountered => return
                } catch (NegativeCycleException $e) {
                    return $e->getCycle();
                }
            }
        // no more vertices to check => abort
        }
        throw new UnderflowException('No negative cycle found');
    }

    /**
     * create new graph clone with only vertices and edges in negative cycle
     *
     * @return Graph
     * @throws UnderflowException if there's no negative cycle
     * @uses AlgorithmDetectNegativeCycle::getCycleNegative()
     * @uses Walk::createGraph()
     */
    public function createGraph()
    {
        return $this->getCycleNegative()->createGraph();
    }
}


================================================
FILE: src/Directed.php
================================================
<?php

namespace Graphp\Algorithms;

use Graphp\Graph\EdgeDirected;
use Graphp\Graph\EdgeUndirected;

/**
 * Basic algorithms for working with the undirected or directed Graphs (digraphs) / Walks.
 *
 * @link http://en.wikipedia.org/wiki/Glossary_of_graph_theory#Direction
 * @link http://en.wikipedia.org/wiki/Digraph_%28mathematics%29
 */
class Directed extends BaseDual
{
    /**
     * checks whether the graph has any directed edges
     *
     * This method is intentionally not named "isDirected()" (aka digraph),
     * because that might be misleading in regards to empty and/or mixed graphs.
     *
     * @return bool
     */
    public function hasDirected()
    {
        foreach ($this->set->getEdges() as $edge) {
            if ($edge instanceof EdgeDirected) {
                return true;
            }
        }

        return false;
    }

    /**
     * checks whether the graph has any undirected edges
     *
     * This method is intentionally not named "isUndirected()",
     * because that might be misleading in regards to empty and/or mixed graphs.
     *
     * @return bool
     */
    public function hasUndirected()
    {
        foreach ($this->set->getEdges() as $edge) {
            if ($edge instanceof EdgeUndirected) {
                return true;
            }
        }

        return false;
    }

    /**
     * checks whether this is a mixed graph (contains both directed and undirected edges)
     *
     * @return bool
     * @uses self::hasDirected()
     * @uses self::hasUndirected()
     */
    public function isMixed()
    {
        return ($this->hasDirected() && $this->hasUndirected());
    }
}


================================================
FILE: src/Eulerian.php
================================================
<?php

namespace Graphp\Algorithms;

class Eulerian extends BaseGraph
{
    /**
     * check whether this graph has an eulerian cycle
     *
     * @return bool
     * @uses ConnectedComponents::isSingle()
     * @uses Degree::getDegreeVertex()
     * @todo isolated vertices should be ignored
     * @todo definition is only valid for undirected graphs
     */
    public function hasCycle()
    {
        $components = new ConnectedComponents($this->graph);
        if ($components->isSingle()) {
            $alg = new Degree($this->graph);

            foreach ($this->graph->getVertices() as $vertex) {
                // uneven degree => fail
                if ($alg->getDegreeVertex($vertex) & 1) {
                    return false;
                }
            }

            return true;
        }

        return false;
    }
}


================================================
FILE: src/Flow.php
================================================
<?php

namespace Graphp\Algorithms;

use Graphp\Graph\EdgeDirected;
use Graphp\Graph\Exception\UnexpectedValueException;
use Graphp\Graph\Vertex;

/**
 * Basic algorithms for working with flow graphs
 *
 * A flow network (also known as a transportation network) is a directed graph
 * where each edge has a capacity and each edge receives a flow.
 *
 * @link http://en.wikipedia.org/wiki/Flow_network
 * @see Algorithm\Balance
 */
class Flow extends BaseDual
{
    /**
     * check if this graph has any flow set (any edge has a non-NULL flow)
     *
     * @return bool
     * @uses Edge::getFlow()
     */
    public function hasFlow()
    {
        foreach ($this->set->getEdges() as $edge) {
            if ($edge->getFlow() !== NULL) {
                return true;
            }
        }

        return false;
    }

    /**
     * Calculates the flow for this Vertex: sum(outflow) - sum(inflow)
     *
     * Usually, vertices should have a resulting flow of 0: The sum of flows
     * entering a vertex must equal the sum of flows leaving a vertex. If the
     * resulting flow is < 0, this vertex is considered a sink (i.e. there's
     * more flow into this vertex). If the resulting flow is > 0, this vertex
     * is considered a "source" (i.e. there's more flow leaving this vertex).
     *
     * @param Vertex $vertex
     * @return float
     * @throws UnexpectedValueException if they are undirected edges
     * @see Vertex::getBalance()
     * @uses Vertex::getEdges()
     * @uses Edge::getFlow()
     */
    public function getFlowVertex(Vertex $vertex)
    {
        $sumOfFlow = 0;

        foreach ($vertex->getEdges() as $edge) {
            if (!($edge instanceof EdgeDirected)) {
                throw new UnexpectedValueException("TODO: undirected edges not suported yet");
            }

            // edge is an outgoing edge of this vertex
            if ($edge->hasVertexStart($vertex)) {
                // flowing out (flow is "pointing away")
                $sumOfFlow += $edge->getFlow();
                // this is an ingoing edge
            } else {
                // flowing in
                $sumOfFlow -= $edge->getFlow();
            }
        }

        return $sumOfFlow;
    }

    public function getBalance()
    {
        $balance = 0;
        // Sum for all vertices of value
        foreach ($this->set->getVertices() as $vertex) {
            $balance += $vertex->getBalance();
        }

        return $balance;
    }

    /**
     * check if the current flow is balanced (aka "balanced flow" or "b-flow")
     *
     * a flow is considered balanced if each edge's current flow does not exceed its
     * maximum capacity (which is always guaranteed due to the implementation
     * of Edge::setFlow()) and each vertices' flow (i.e. outflow-inflow) equals
     * its balance.
     *
     * checking whether the FLOW is balanced is not to be confused with checking
     * whether the GRAPH is balanced (see Graph::isBalanced() instead)
     *
     * @return bool
     * @see Degree::isBalanced() if you merely want to check indegree=outdegree
     * @uses self::getFlowVertex()
     * @uses Vertex::getBalance()
     */
    public function isBalancedFlow()
    {
        // no need to check for each edge: flow <= capacity (setters already check that)
        // check for each vertex: outflow-inflow = balance
        foreach ($this->set->getVertices() as $vertex) {
            if ($this->getFlowVertex($vertex) !== $vertex->getBalance()) {
                return false;
            }
        }

        return true;
    }
}


================================================
FILE: src/Groups.php
================================================
<?php

namespace Graphp\Algorithms;

use Graphp\Graph\Set\Vertices;
use Graphp\Graph\Vertex;

class Groups extends BaseGraph
{
    /**
     * count total number of different groups assigned to vertices
     *
     * @return int
     * @uses AlgorithmGroups::getGroups()
     */
    public function getNumberOfGroups()
    {
        return \count($this->getGroups());
    }

    /**
     * checks whether the input graph's vertex groups are a valid bipartition
     *
     * @return bool
     * @see AlgorithmBipartit() if you do NOT want to take vertex groups into consideration
     * @uses AlgorithmGroups::getNumberOfGroups()
     * @uses Vertex::getGroup()
     */
    public function isBipartit()
    {
        // graph has to contain exactly 2 groups
        if ($this->getNumberOfGroups() !== 2) {
            return false;
        }

        // for each vertex
        foreach ($this->graph->getVertices() as $vertex) {
            // get current group
            $group = $vertex->getGroup();
            // for every neighbor vertex
            foreach ($vertex->getVerticesEdge() as $vertexNeighbor) {
                // vertex group must be other group
                if ($vertexNeighbor->getGroup() === $group) {
                    return false;
                }
            }
        }

        return true;
    }

    /**
     * get vector of all group numbers
     *
     * @return int[]
     * @uses Vertex::getGroup()
     */
    public function getGroups()
    {
        $groups = array();
        foreach ($this->graph->getVertices() as $vertex) {
            assert($vertex instanceof Vertex);
            $groups[$vertex->getGroup()] = true;
        }

        return \array_keys($groups);
    }

    /**
     * get set of all Vertices in the given group
     *
     * @param  int      $group
     * @return Vertices
     * @uses Vertex::getGroup()
     */
    public function getVerticesGroup($group)
    {
        $vertices = array();
        foreach ($this->graph->getVertices()->getMap() as $vid => $vertex) {
            if ($vertex->getGroup() === $group) {
                $vertices[$vid] = $vertex;
            }
        }

        return new Vertices($vertices);
    }
}


================================================
FILE: src/Loop.php
================================================
<?php

namespace Graphp\Algorithms;

use Graphp\Graph\Vertex;

/**
 * Basic algorithms for working with loop edges
 *
 * A loop (also called a self-loop or a "buckle") is an edge that connects a
 * Vertex to itself. A simple graph contains no loops.
 *
 * @link http://en.wikipedia.org/wiki/Loop_%28graph_theory%29
 */
class Loop extends BaseDual
{
    /**
     * checks whether this graph has any loops (edges from vertex to itself)
     *
     * @return bool
     * @uses Edge::isLoop()
     */
    public function hasLoop()
    {
        foreach ($this->set->getEdges() as $edge) {
            if ($edge->isLoop()) {
                return true;
            }
        }

        return false;
    }

    /**
     * checks whether this vertex has a loop (edge to itself)
     *
     * @return bool
     * @uses Edge::isLoop()
     */
    public function hasLoopVertex(Vertex $vertex)
    {
        foreach ($vertex->getEdges() as $edge) {
            if ($edge->isLoop()) {
                return true;
            }
        }

        return false;
    }
}


================================================
FILE: src/MaxFlow/EdmondsKarp.php
================================================
<?php

namespace Graphp\Algorithms\MaxFlow;

use Graphp\Algorithms\Base;
use Graphp\Algorithms\ResidualGraph;
use Graphp\Algorithms\ShortestPath\BreadthFirst;
use Graphp\Graph\EdgeDirected;
use Graphp\Graph\Exception\InvalidArgumentException;
use Graphp\Graph\Exception\OutOfBoundsException;
use Graphp\Graph\Exception\UnderflowException;
use Graphp\Graph\Exception\UnexpectedValueException;
use Graphp\Graph\Graph;
use Graphp\Graph\Set\Edges;
use Graphp\Graph\Vertex;

class EdmondsKarp extends Base
{
    /**
     * @var Vertex
     */
    private $startVertex;

    /**
     * @var Vertex
     */
    private $destinationVertex;

    /**
     * @param Vertex $startVertex       the vertex where the flow search starts
     * @param Vertex $destinationVertex the vertex where the flow search ends (destination)
     */
    public function __construct(Vertex $startVertex, Vertex $destinationVertex)
    {
        if ($startVertex === $destinationVertex) {
            throw new InvalidArgumentException('Start and destination must not be the same vertex');
        }
        if ($startVertex->getGraph() !== $destinationVertex->getGraph()) {
            throw new InvalidArgumentException('Start and target vertex have to be in the same graph instance');
        }
        $this->startVertex = $startVertex;
        $this->destinationVertex = $destinationVertex;
    }

    /**
     * Returns max flow graph
     *
     * @return Graph
     * @throws UnexpectedValueException for undirected edges
     */
    public function createGraph()
    {
        $graphResult = $this->startVertex->getGraph()->createGraphClone();

        // initialize null flow and check edges
        foreach ($graphResult->getEdges() as $edge) {
            if (!($edge instanceof EdgeDirected)) {
                throw new UnexpectedValueException('Undirected edges not supported for edmonds karp');
            }
            $edge->setFlow(0);
        }

        $idA = $this->startVertex->getId();
        $idB = $this->destinationVertex->getId();

        do {
            // Generate new residual graph and repeat
            $residualAlgorithm = new ResidualGraph($graphResult);
            $graphResidual = $residualAlgorithm->createGraph();

            // 1. Search _shortest_ (number of hops and cheapest) path from s -> t
            $alg = new BreadthFirst($graphResidual->getVertex($idA));
            try {
                $pathFlow = $alg->getWalkTo($graphResidual->getVertex($idB));
            } catch (OutOfBoundsException $e) {
                $pathFlow = NULL;
            }

            // If path exists add the new flow to graph
            if ($pathFlow) {
                // 2. get max flow from path
                $maxFlowValue = $pathFlow->getEdges()->getEdgeOrder(Edges::ORDER_CAPACITY)->getCapacity();

                // 3. add flow to path
                foreach ($pathFlow->getEdges() as $edge) {
                    // try to look for forward edge to increase flow
                    try {
                        $originalEdge = $graphResult->getEdgeClone($edge);
                        $originalEdge->setFlow($originalEdge->getFlow() + $maxFlowValue);
                    // forward edge not found, look for back edge to decrease flow
                    } catch (UnderflowException $e) {
                        $originalEdge = $graphResult->getEdgeCloneInverted($edge);
                        $originalEdge->setFlow($originalEdge->getFlow() - $maxFlowValue);
                    }
                }
            }

        // repeat while we still finds paths with residual capacity to add flow to
        } while ($pathFlow);

        return $graphResult;
    }

    /**
     * Returns max flow value
     *
     * @return float
     */
    public function getFlowMax()
    {
        $resultGraph = $this->createGraph();

        $start = $resultGraph->getVertex($this->startVertex->getId());
        $maxFlow = 0;
        foreach ($start->getEdgesOut() as $edge) {
            $maxFlow = $maxFlow + $edge->getFlow();
        }

        return $maxFlow;
    }
}


================================================
FILE: src/MaximumMatching/Base.php
================================================
<?php

namespace Graphp\Algorithms\MaximumMatching;

use Graphp\Algorithms\BaseGraph;
use Graphp\Graph\Exception\UnexpectedValueException;
use Graphp\Graph\Graph;
use Graphp\Graph\Set\Edges;

abstract class Base extends BaseGraph
{
    /**
     * Get the count of edges that are in the match
     *
     * @return int
     * @throws UnexpectedValueException if graph is directed or is not bipartit
     * @uses Base::getEdges()
     */
    public function getNumberOfMatches()
    {
        return \count($this->getEdges());
    }

    /**
     * create new resulting graph with only edges from maximum matching
     *
     * @return Graph
     * @uses Base::getEdges()
     * @uses Graph::createGraphCloneEdges()
     */
    public function createGraph()
    {
        return $this->graph->createGraphCloneEdges($this->getEdges());
    }

    /**
     * create new resulting graph with minimum-cost flow on edges
     *
     * @return Edges
     */
    abstract public function getEdges();
}


================================================
FILE: src/MaximumMatching/Flow.php
================================================
<?php

namespace Graphp\Algorithms\MaximumMatching;

use Graphp\Algorithms\Directed;
use Graphp\Algorithms\Groups;
use Graphp\Algorithms\MaxFlow\EdmondsKarp as MaxFlowEdmondsKarp;
use Graphp\Graph\Exception\UnexpectedValueException;
use Graphp\Graph\Set\Edges;
use Graphp\Graph\Vertex;

class Flow extends Base
{
    public function getEdges()
    {
        $alg = new Directed($this->graph);
        if ($alg->hasDirected()) {
            throw new UnexpectedValueException('Input graph contains directed edges');
        }

        $alg = new Groups($this->graph);
        if (!$alg->isBipartit()) {
            throw new UnexpectedValueException('Input graph does not have bipartit groups assigned to each vertex. Consider Using "AlgorithmBipartit::createGraph()" first');
        }

        // create temporary flow graph with supersource and supersink
        $graphFlow = $this->graph->createGraphCloneEdgeless();

        $superSource = $graphFlow->createVertex();
        $superSink   = $graphFlow->createVertex();

        $groups = $alg->getGroups();
        $groupA = $groups[0];

        // connect supersource s* to set A and supersink t* to set B
        foreach ($graphFlow->getVertices() as $vertex) {
            assert($vertex instanceof Vertex);
            // we want to skip over supersource & supersink as they do not have a partition assigned
            if ($vertex === $superSource || $vertex === $superSink) continue;

            $group = $vertex->getGroup();

            if ($group === $groupA) {
                // group A: source
                $graphFlow->createEdgeDirected($superSource, $vertex)->setCapacity(1)->setFlow(0);

                // temporarily create edges from A->B for flow graph
                $originalVertex = $this->graph->getVertex($vertex->getId());
                foreach ($originalVertex->getVerticesEdgeTo() as $vertexTarget) {
                    $graphFlow->createEdgeDirected($vertex, $graphFlow->getVertex($vertexTarget->getId()))->setCapacity(1)->setFlow(0);
                }
            } else {
                // group B: sink
                $graphFlow->createEdgeDirected($vertex, $superSink)->setCapacity(1)->setFlow(0);
            }
        }

        // visualize($resultGraph);

        // calculate (s*, t*)-flow
        $algMaxFlow = new MaxFlowEdmondsKarp($superSource, $superSink);
        $resultGraph = $algMaxFlow->createGraph();

        // destroy temporary supersource and supersink again
        $resultGraph->getVertex($superSink->getId())->destroy();
        $resultGraph->getVertex($superSource->getId())->destroy();

        $returnEdges = array();
        foreach ($resultGraph->getEdges() as $edge) {
            // only keep matched edges
            if ($edge->getFlow() > 0) {
                $originalEdge = $this->graph->getEdgeClone($edge);
                $returnEdges[] = $originalEdge;
            }
        }

        return new Edges($returnEdges);
    }
}


================================================
FILE: src/MinimumCostFlow/Base.php
================================================
<?php

namespace Graphp\Algorithms\MinimumCostFlow;

use Graphp\Algorithms\BaseGraph;
use Graphp\Algorithms\Weight as AlgorithmWeight;
use Graphp\Algorithms\Flow as AlgorithmFlow;
use Graphp\Graph\Exception\UnderflowException;
use Graphp\Graph\Exception\UnexpectedValueException;
use Graphp\Graph\Graph;
use Graphp\Graph\Set\Edges;

abstract class Base extends BaseGraph
{
    /**
     * check if balance is okay and throw exception otherwise
     *
     * @return $this (chainable)
     * @throws UnexpectedValueException
     */
    protected function checkBalance()
    {
        $alg = new AlgorithmFlow($this->graph);
        $balance = $alg->getBalance();

        $tolerance = 0.000001;
        if ($balance >= $tolerance || $balance <= -$tolerance) {
            throw new UnexpectedValueException('The given graph is not balanced value is: ' . $balance);
        }

        return $this;
    }

    /**
     * helper used to add $newFlow to original edges of $clonedEdges in graph $resultGraph
     *
     * @param Graph  $resultGraph graph to look for original edges
     * @param Edges  $clonedEdges set of cloned edges to be modified
     * @param number $newFlow     flow to add
     * @uses Graph::getEdgeClone()
     * @uses Graph::getEdgeCloneInverted()
     * @uses Edge::getFlow()
     * @uses Edge::setFlow()
     */
    protected function addFlow(Graph $resultGraph, Edges $clonedEdges, $newFlow)
    {
        foreach ($clonedEdges as $clonedEdge) {
            try {
                // get edge from clone
                $edge = $resultGraph->getEdgeClone($clonedEdge);
                // add flow
                $edge->setFlow($edge->getFlow() + $newFlow);
            } catch (UnderflowException $ignore) {
                // if the edge doesn't exist => use the residual edge
                $edge = $resultGraph->getEdgeCloneInverted($clonedEdge);
                // remove flow
                $edge->setFlow($edge->getFlow() - $newFlow);
            }
        }
    }

    /**
     * calculate total weight along minimum-cost flow
     *
     * @return float
     * @uses self::createGraph()
     * @uses AlgorithmWeight::getWeightFlow()
     */
    public function getWeightFlow()
    {
        $alg = new AlgorithmWeight($this->createGraph());
        return $alg->getWeightFlow();
    }

    /**
     * create new resulting graph with minimum-cost flow on edges
     *
     * @return Graph
     * @throws UnexpectedValueException for undirected edges
     * @throws UnexpectedValueException if the graph has not enough capacity for the minimum-cost flow
     */
    abstract public function createGraph();
}


================================================
FILE: src/MinimumCostFlow/CycleCanceling.php
================================================
<?php

namespace Graphp\Algorithms\MinimumCostFlow;

use Graphp\Algorithms\DetectNegativeCycle;
use Graphp\Algorithms\MaxFlow\EdmondsKarp as MaxFlowEdmondsKarp;
use Graphp\Algorithms\ResidualGraph;
use Graphp\Graph\Exception\UnderflowException;
use Graphp\Graph\Exception\UnexpectedValueException;
use Graphp\Graph\Set\Edges;

class CycleCanceling extends Base
{
    public function createGraph()
    {
        $this->checkBalance();

        // create resulting graph with supersource and supersink
        $resultGraph = $this->graph->createGraphClone();

        $superSource = $resultGraph->createVertex();
        $superSink   = $resultGraph->createVertex();

        $sumBalance = 0;

        // connect supersource s* and supersink t* with all "normal" sources and sinks
        foreach ($resultGraph->getVertices() as $vertex) {
            $balance = $vertex->getBalance();

            if ($balance > 0) {
                // positive balance => source capacity
                $resultGraph->createEdgeDirected($superSource, $vertex)->setCapacity($balance);

                $sumBalance += $balance;
            } elseif ($balance < 0) {
                // negative balance => sink capacity (positive)
                $resultGraph->createEdgeDirected($vertex, $superSink)->setCapacity(-$balance);
            }
        }

        // calculate (s*, t*)-flow
        $algMaxFlow = new MaxFlowEdmondsKarp($superSource, $superSink);
        $flowMax = $algMaxFlow->getFlowMax();

        if ($flowMax !== $sumBalance) {
            throw new UnexpectedValueException('Network does not support required flow of ' . $sumBalance . ' (maximum possible flow limited to ' . $flowMax . ')');
        }

        $resultGraph = $algMaxFlow->createGraph();

        while (true) {
            // create residual graph
            $algRG = new ResidualGraph($resultGraph);
            $residualGraph = $algRG->createGraph();

            // get negative cycle
            $alg = new DetectNegativeCycle($residualGraph);
            try {
                $clonedEdges = $alg->getCycleNegative()->getEdges();
            } catch (UnderflowException $ignore) {
                // no negative cycle found => end algorithm
                break;
            }

            // calculate maximal possible flow = minimum capacity remaining for all edges
            $newFlow = $clonedEdges->getEdgeOrder(Edges::ORDER_CAPACITY_REMAINING)->getCapacityRemaining();

            // set flow on original graph
            assert($newFlow !== null);
            $this->addFlow($resultGraph, $clonedEdges, $newFlow);
        }

        // destroy temporary supersource and supersink again
        $resultGraph->getVertex($superSink->getId())->destroy();
        $resultGraph->getVertex($superSource->getId())->destroy();

        return $resultGraph;
    }
}


================================================
FILE: src/MinimumCostFlow/SuccessiveShortestPath.php
================================================
<?php

namespace Graphp\Algorithms\MinimumCostFlow;

use Graphp\Algorithms\ResidualGraph;
use Graphp\Algorithms\ShortestPath\MooreBellmanFord as SpMooreBellmanFord;
use Graphp\Algorithms\Search\BreadthFirst as SearchBreadthFirst;
use Graphp\Graph\EdgeDirected;
use Graphp\Graph\Exception\UnderflowException;
use Graphp\Graph\Exception\UnexpectedValueException;
use Graphp\Graph\Graph;
use Graphp\Graph\Set\Edges;
use Graphp\Graph\Vertex;

class SuccessiveShortestPath extends Base
{
    /**
     * @uses Graph::createGraphClone()
     * @uses ResidualGraph::createGraph()
     * @uses SpMooreBellmanFord::getEdgesTo(Vertex $targetVertex)
     * @see Base::createGraph()
     */
    public function createGraph()
    {
        $this->checkBalance();
        $resultGraph = $this->graph->createGraphClone();

        // initial balance to 0
        $vertices = $resultGraph->getVertices();
        foreach ($vertices as $vertex) {
            $vertex->setBalance(0);
        }

        // initial flow of edges
        $edges = $resultGraph->getEdges();
        foreach ($edges as $edge) {
            if (!($edge instanceof EdgeDirected)) {
                throw new UnexpectedValueException('Undirected edges are not supported for SuccessiveShortestPath');
            }

            // 0 if weight of edge is positive
            $flow = 0;

            // maximal flow if weight of edge is negative
            if ($edge->getWeight() < 0) {
                $flow = $edge->getCapacity();

                $startVertex = $edge->getVertexStart();
                $endVertex = $edge->getVertexEnd();

                // add balance to start- and end-vertex
                $this->addBalance($startVertex, $flow);
                $this->addBalance($endVertex, - $flow);
            }

            $edge->setFlow($flow);
        }

        // return or Exception inside this while
        while (true) {
            // create residual graph
            $algRG = new ResidualGraph($resultGraph);
            $residualGraph = $algRG->createGraph();

            // search for a source
            try {
                $sourceVertex = $this->getVertexSource($residualGraph);
            } catch (UnderflowException $ignore) {
                // no source is found => minimum-cost flow is found
                break;
            }

            // search for reachable target sink from this source
            try {
                $targetVertex = $this->getVertexSink($sourceVertex);
            } catch (UnderflowException $e) {
                // no target found => network does not have enough capacity
                throw new UnexpectedValueException('The graph has not enough capacity for the minimum-cost flow', 0, $e);
            }

            // calculate shortest path between source- and target-vertex
            $algSP = new SpMooreBellmanFord($sourceVertex);
            $edgesOnFlow = $algSP->getEdgesTo($targetVertex);

            // calculate the maximal possible flow
            // new flow is the maximal possible flow for this path
            $newflow    =    $this->graph->getVertex($sourceVertex->getId())->getBalance() - $sourceVertex->getBalance();
            $targetFlow = - ($this->graph->getVertex($targetVertex->getId())->getBalance() - $targetVertex->getBalance());

            // get minimum of source and target
            if ($targetFlow < $newflow) {
                $newflow = $targetFlow;
            }

            // get minimum of capacity remaining on path
            $minCapacity = $edgesOnFlow->getEdgeOrder(Edges::ORDER_CAPACITY_REMAINING)->getCapacityRemaining();
            if ($minCapacity < $newflow) {
                $newflow = $minCapacity;
            }

            // add the new flow to the path
            assert($newflow !== null);
            $this->addFlow($resultGraph, $edgesOnFlow, $newflow);

            // add balance to source and remove for the target sink
            $oriSourceVertex = $resultGraph->getVertex($sourceVertex->getId());
            $oriTargetVertex = $resultGraph->getVertex($targetVertex->getId());

            $this->addBalance($oriSourceVertex, $newflow);
            $this->addBalance($oriTargetVertex, - $newflow);
        }

        return $resultGraph;
    }

    /**
     * @param  Graph     $graph
     * @return Vertex a source vertex in the given graph
     * @throws UnderflowException if there is no left source vertex
     */
    private function getVertexSource(Graph $graph)
    {
        foreach ($graph->getVertices()->getMap() as $vid => $vertex) {
            if ($this->graph->getVertex($vid)->getBalance() - $vertex->getBalance() > 0) {
                return $vertex;
            }
        }
        throw new UnderflowException('No source vertex found in graph');
    }

    /**
     * @param  Vertex    $source
     * @return Vertex a sink-vertex that is reachable from the source
     * @throws UnderflowException if there is no reachable sink vertex
     * @uses BreadthFirst::getVertices()
     */
    private function getVertexSink(Vertex $source)
    {
        // search for reachable Vertices
        $algBFS = new SearchBreadthFirst($source);

        foreach ($algBFS->getVertices()->getMap() as $vid => $vertex) {
            if ($this->graph->getVertex($vid)->getBalance() - $vertex->getBalance() < 0) {
                return $vertex;
            }
        }
        throw new UnderflowException('No sink vertex connected to given source vertex found');
    }

    private function addBalance(Vertex $vertex, $balance)
    {
        $vertex->setBalance($vertex->getBalance() + $balance);
    }
}


================================================
FILE: src/MinimumSpanningTree/Base.php
================================================
<?php

namespace Graphp\Algorithms\MinimumSpanningTree;

use Graphp\Algorithms\Base as AlgorithmBase;
use Graphp\Graph\Edge;
use Graphp\Graph\Graph;
use Graphp\Graph\Set\Edges;
use SplPriorityQueue;

/**
 * Abstract base class for minimum spanning tree (MST) algorithms
 *
 * A minimum spanning tree of a graph is a subgraph that is a tree and connects
 * all the vertices together while minimizing the total sum of all edges'
 * weights.
 *
 * A spanning tree thus requires a connected graph (single connected component),
 * otherwise we can span multiple trees (spanning forest) within each component.
 * Because a null graph (a Graph with no vertices) is not considered connected,
 * it also can not contain a spanning tree.
 *
 * Most authors demand that the input graph has to be undirected, whereas this
 * library supports also directed and mixed graphs. The actual direction of the
 * edge will be ignored, only its incident vertices will be checked. This is
 * done in order to be consistent to how ConnectedComponents are checked.
 *
 * @link http://en.wikipedia.org/wiki/Minimum_Spanning_Tree
 * @link http://en.wikipedia.org/wiki/Spanning_Tree
 * @link http://mathoverflow.net/questions/120536/is-the-empty-graph-a-tree
 */
abstract class Base extends AlgorithmBase
{
    /**
     * create new resulting graph with only edges on minimum spanning tree
     *
     * @return Graph
     * @uses self::getGraph()
     * @uses self::getEdges()
     * @uses Graph::createGraphCloneEdges()
     */
    public function createGraph()
    {
        return $this->getGraph()->createGraphCloneEdges($this->getEdges());
    }

    /**
     * get all edges on minimum spanning tree
     *
     * @return Edges
     */
    abstract public function getEdges();

    /**
     * return reference to current Graph
     *
     * @return Graph
     */
    abstract protected function getGraph();

    /**
     * get total weight of minimum spanning tree
     *
     * @return float
     */
    public function getWeight()
    {
        return $this->getEdges()->getSumCallback(function (Edge $edge) {
            return $edge->getWeight();
        });
    }

    /**
     * helper method to add a set of Edges to the given set of sorted edges
     *
     * @param Edges            $edges
     * @param SplPriorityQueue $sortedEdges
     */
    protected function addEdgesSorted(Edges $edges, SplPriorityQueue $sortedEdges)
    {
        // For all edges
        foreach ($edges as $edge) {
            assert($edge instanceof Edge);
            // ignore loops (a->a)
            if (!$edge->isLoop()) {
                // Add edges with negative weight because of order in stl
                $sortedEdges->insert($edge, -$edge->getWeight());
            }
        }
    }
}


================================================
FILE: src/MinimumSpanningTree/Kruskal.php
================================================
<?php

namespace Graphp\Algorithms\MinimumSpanningTree;

use Graphp\Graph\Edge;
use Graphp\Graph\Exception\UnexpectedValueException;
use Graphp\Graph\Graph;
use Graphp\Graph\Set\Edges;
use SplPriorityQueue;

class Kruskal extends Base
{
    /**
     * @var Graph
     */
    private $graph;

    public function __construct(Graph $inputGraph)
    {
        $this->graph = $inputGraph;
    }

    protected function getGraph()
    {
        return $this->graph;
    }

    /**
     * @return Edges
     */
    public function getEdges()
    {
        // Sortiere Kanten im Graphen

        $sortedEdges = new SplPriorityQueue();

        // For all edges
        $this->addEdgesSorted($this->graph->getEdges(), $sortedEdges);

        $returnEdges = array();

        // next color to assign
        $colorNext = 0;
        // array(color1 => array(vid1, vid2, ...), color2=>...)
        $colorVertices = array();
        // array(vid1 => color1, vid2 => color1, ...)
        $colorOfVertices = array();

        // Füge billigste Kanten zu neuen Graphen hinzu und verschmelze teilgragen wenn es nötig ist (keine Kreise)
        // solange ich mehr als einen Graphen habe mit weniger als n-1 kanten (bei n knoten im original)
        foreach ($sortedEdges as $edge) {
            assert($edge instanceof Edge);
            // Gucke Kante an:

            $vertices = $edge->getVertices()->getIds();

            $aId = $vertices[0];
            $bId = $vertices[1];

            $aColor = isset($colorOfVertices[$aId]) ? $colorOfVertices[$aId] : NULL;
            $bColor = isset($colorOfVertices[$bId]) ? $colorOfVertices[$bId] : NULL;

            // 1. weder start noch end gehört zu einem graphen
                // => neuer Graph mit kanten
            if ($aColor === NULL && $bColor === NULL) {
                $colorOfVertices[$aId] = $colorNext;
                $colorOfVertices[$bId] = $colorNext;

                $colorVertices[$colorNext] = array($aId, $bId);

                ++$colorNext;

                // connect both vertices
                $returnEdges[] = $edge;
            }
            // 4. start xor end gehören zu einem graphen
                // => erweitere diesesn Graphen
            // Only b has color
            else if ($aColor === NULL && $bColor !== NULL) {
                // paint a in b's color
                $colorOfVertices[$aId] = $bColor;
                $colorVertices[$bColor][]=$aId;

                $returnEdges[] = $edge;
            // Only a has color
            } elseif ($aColor !== NULL && $bColor === NULL) {
                // paint b in a's color
                $colorOfVertices[$bId] = $aColor;
                $colorVertices[$aColor][]=$bId;

                $returnEdges[] = $edge;
            }
            // 3. start und end gehören zu unterschiedlichen graphen
                // => vereinigung
            // Different color
            else if ($aColor !== $bColor) {
                $betterColor = $aColor;
                $worseColor  = $bColor;

                // more vertices with color a => paint all in b in a's color
                if (\count($colorVertices[$bColor]) > \count($colorVertices[$aColor])) {
                    $betterColor = $bColor;
                    $worseColor = $aColor;
                }

                // search all vertices with color b
                foreach ($colorVertices[$worseColor] as $vid) {
                    $colorOfVertices[$vid] = $betterColor;
                    // repaint in a's color
                    $colorVertices[$betterColor][]=$vid;
                }
                // delete old color
                unset($colorVertices[$worseColor]);

                $returnEdges[] = $edge;
            }
            // 2. start und end gehören zum gleichen graphen => zirkel
            // => nichts machen
        }

        // definition of spanning tree: number of edges = number of vertices - 1
        // above algorithm does not check isolated edges or may otherwise return multiple connected components => force check
        if (\count($returnEdges) !== (\count($this->graph->getVertices()) - 1)) {
            throw new UnexpectedValueException('Graph is not connected');
        }

        return new Edges($returnEdges);
    }
}


================================================
FILE: src/MinimumSpanningTree/Prim.php
================================================
<?php

namespace Graphp\Algorithms\MinimumSpanningTree;

use Graphp\Graph\Edge;
use Graphp\Graph\Exception\UnexpectedValueException;
use Graphp\Graph\Set\Edges;
use Graphp\Graph\Vertex;
use SplPriorityQueue;

class Prim extends Base
{
    /**
     * @var Vertex
     */
    private $startVertex;

    public function __construct(Vertex $startVertex)
    {
        $this->startVertex = $startVertex;
    }

    /**
     * @return Edges
     */
    public function getEdges()
    {
        // Initialize algorithm
        $edgeQueue = new SplPriorityQueue();
        $vertexCurrent = $this->startVertex;

        $markInserted = array();
        $returnEdges = array();

        // iterate n-1 times (per definition, resulting MST MUST have n-1 edges)
        for ($i = 0, $n = \count($this->startVertex->getGraph()->getVertices()) - 1; $i < $n; ++$i) {
            $markInserted[$vertexCurrent->getId()] = true;

            // get unvisited vertex of the edge and add edges from new vertex
            // Add all edges from $currentVertex to priority queue
            $this->addEdgesSorted($vertexCurrent->getEdges(), $edgeQueue);

            do {
                if ($edgeQueue->isEmpty()) {
                    throw new UnexpectedValueException('Graph has more than one component');
                }

                // Get next cheapest edge
                $cheapestEdge = $edgeQueue->extract();
                assert($cheapestEdge instanceof Edge);

                // Check if edge is between unmarked and marked edge
                $vertices = $cheapestEdge->getVertices();
                $vertexA  = $vertices->getVertexFirst();
                $vertexB  = $vertices->getVertexLast();
            } while (!(isset($markInserted[$vertexA->getId()]) XOR isset($markInserted[$vertexB->getId()])));

            // Cheapest Edge found, add edge to returnGraph
            $returnEdges[] = $cheapestEdge;

            // set current vertex for next iteration in order to add its edges to queue
            if (isset($markInserted[$vertexA->getId()])) {
                $vertexCurrent = $vertexB;
            } else {
                $vertexCurrent = $vertexA;
            }
        }

        return new Edges($returnEdges);
    }

    protected function getGraph()
    {
        return $this->startVertex->getGraph();
    }
}


================================================
FILE: src/Parallel.php
================================================
<?php

namespace Graphp\Algorithms;

use Graphp\Graph\Edge;
use Graphp\Graph\EdgeDirected as EdgeDirected;
use Graphp\Graph\Set\Edges;

/**
 * Basic algorithms for working with parallel edges
 *
 * Parallel edges (also called multiple edges or a multi-edge), are two or more
 * edges that are incident to the same two vertices. A simple graph has no
 * multiple edges.
 *
 * @link http://en.wikipedia.org/wiki/Multiple_edges
 */
class Parallel extends BaseGraph
{
    /**
     * checks whether this graph has any parallel edges (aka multigraph)
     *
     * @return bool
     * @uses Edge::hasEdgeParallel() for every edge
     */
    public function hasEdgeParallel()
    {
        foreach ($this->graph->getEdges() as $edge) {
            if ($this->hasEdgeParallelEdge($edge)) {
                return true;
            }
        }

        return false;
    }


    /**
     * checks whether this edge has any parallel edges
     *
     * @return bool
     * @uses Edge::getEdgesParallel()
     */
    public function hasEdgeParallelEdge(Edge $edge)
    {
        return !$this->getEdgesParallelEdge($edge)->isEmpty();
    }

    /**
     * get set of all Edges parallel to this edge (excluding self)
     *
     * @param Edge $edge
     * @return Edges
     */
    public function getEdgesParallelEdge(Edge $edge)
    {
        if ($edge instanceof EdgeDirected) {
            // get all edges between this edge's endpoints
            $edges = $edge->getVertexStart()->getEdgesTo($edge->getVertexEnd())->getVector();
        } else {
            // edge points into both directions (undirected/bidirectional edge)
            // also get all edges in other direction
            $ends  = $edge->getVertices();
            $edges = $ends->getVertexFirst()->getEdges()->getEdgesIntersection($ends->getVertexLast()->getEdges())->getVector();
        }

        $pos = \array_search($edge, $edges, true);
        assert($pos !== false);

        // exclude current edge from parallel edges
        unset($edges[$pos]);

        return new Edges(\array_values($edges));
    }
}


================================================
FILE: src/Property/GraphProperty.php
================================================
<?php

namespace Graphp\Algorithms\Property;

use Graphp\Algorithms\BaseGraph;

/**
 * Simple algorithms for working with Graph properties
 *
 * @link https://en.wikipedia.org/wiki/Graph_property
 */
class GraphProperty extends BaseGraph
{
    /**
     * checks whether this graph has no edges
     *
     * Also known as empty Graph. An empty Graph contains no edges, but can
     * possibly contain any number of isolated vertices.
     *
     * @return bool
     */
    public function isEdgeless()
    {
        return $this->graph->getEdges()->isEmpty();
    }

    /**
     * checks whether this graph is a null graph (no vertex - and thus no edges)
     *
     * Each Edge is incident to two Vertices, or in case of an loop Edge,
     * incident to the same Vertex twice. As such an Edge can not exist when
     * no Vertices exist. So if we check we have no Vertices, we can also be
     * sure that no Edges exist either.
     *
     * @return bool
     */
    public function isNull()
    {
        return $this->graph->getVertices()->isEmpty();
    }

    /**
     * checks whether this graph is trivial (one vertex and no edges)
     *
     * @return bool
     */
    public function isTrivial()
    {
        return ($this->graph->getEdges()->isEmpty() && \count($this->graph->getVertices()) === 1);
    }
}


================================================
FILE: src/Property/WalkProperty.php
================================================
<?php

namespace Graphp\Algorithms\Property;

use Graphp\Algorithms\Base as BaseAlgorithm;
use Graphp\Algorithms\Loop as AlgorithmLoop;
use Graphp\Graph\Walk;

/**
 * Simple algorithms for working with Walk properties
 *
 * @see GraphProperty
 */
class WalkProperty extends BaseAlgorithm
{
    /**
     * the Walk to operate on
     *
     * @var Walk
     */
    protected $walk;

    /**
     * instantiate new WalkProperty algorithm
     *
     * @param Walk $walk
     */
    public function __construct(Walk $walk)
    {
        $this->walk = $walk;
    }

    /**
     * checks whether walk is a cycle (i.e. source vertex = target vertex)
     *
     * A cycle is also known as a closed path, a walk that is NOT a cycle is
     * also known as an open path.
     *
     * A walk with no edges is not considered a cycle. The shortest possible
     * cycle is a single loop edge:
     *
     * 1--\
     * ^  |
     * \--/
     *
     * The following Walk is also considered a valid cycle:
     *
     *      /->3--\
     *      |     |
     * 1 -> 2 -\  |
     * ^    ^  |  |
     * |    \--/  |
     * |          |
     * \----------/
     *
     * @return bool
     * @link http://en.wikipedia.org/wiki/Cycle_%28graph_theory%29
     * @see self::isCircuit()
     * @see self::isLoop()
     */
    public function isCycle()
    {
        $vertices = $this->walk->getVertices();
        return ($vertices->getVertexFirst() === $vertices->getVertexLast() && !$this->walk->getEdges()->isEmpty());
    }

    /**
     * checks whether this walk is a circuit (i.e. a cycle with no duplicate edges)
     *
     * A circuit is also known as a closed (=cycle) trail (=path), that has at
     * least one edge.
     *
     * The following Walk is considered both a valid cycle and a valid circuit:
     *
     * 1 -> 2 -> 3 -\
     * ^            |
     * |            |
     * \------------/
     *
     * The following Walk is also considered both a valid cycle and a valid circuit:
     *
     *      /->3--\
     *      |     |
     * 1 -> 2 -\  |
     * ^    ^  |  |
     * |    \--/  |
     * |          |
     * \----------/
     *
     * The later circuit walk can be expressed by its Vertex IDs as
     * "1, 2, 2, 3, 1". If however, the inner loop would be "walked along"
     * several times, the resulting walk would be expressed as
     * "1, 2, 2, 2, 3, 1", which would still be a valid cycle, but NOT a valid
     * circuit anymore.
     *
     * @return bool
     * @link http://www.proofwiki.org/wiki/Definition:Circuit
     * @uses self::isCycle()
     * @uses self::isPath()
     */
    public function isCircuit()
    {
        return ($this->isCycle() && $this->isPath());
    }

    /**
     * checks whether walk is a path (i.e. does not contain any duplicate edges)
     *
     * A path Walk is also known as a trail.
     *
     * @return bool
     * @uses self::hasArrayDuplicates()
     * @link http://www.proofwiki.org/wiki/Definition:Trail
     */
    public function isPath()
    {
        return !$this->hasArrayDuplicates($this->walk->getEdges()->getVector());
    }

    /**
     * checks whether walk contains a cycle (i.e. contains a duplicate vertex)
     *
     * A walk that CONTAINS a cycle does not neccessarily have to BE a cycle.
     * Conversely, a Walk that *is* a cycle, automatically always *contains* a
     * cycle.
     *
     * The following Walk is NOT a cycle, but it *contains* a valid cycle:
     *
     *      /->4
     *      |
     * 1 -> 2 -> 3 -\
     *      ^       |
     *      \-------/
     *
     * @return bool
     * @uses self::hasArrayDuplicates()
     * @see self::isCycle()
     */
    public function hasCycle()
    {
        return $this->hasArrayDuplicates($this->walk->getVertices()->getVector());
    }

    /**
     * checks whether this walk IS a loop (single edge connecting vertex A with vertex A again)
     *
     * A loop is the simplest possible cycle. As such, each loop is also a
     * cycle. Accordingly, every Walk that *is* a loop, automatically also *is*
     * a cycle and automatically *contains* a loop and automatically *contains*
     * a cycle.
     *
     * The following Walk represents a simple (directed) loop:
     *
     * 1--\
     * ^  |
     * \--/
     *
     * @return bool
     * @uses self::isCycle()
     * @see self::hasLoop()
     */
    public function isLoop()
    {
        return (\count($this->walk->getEdges()) === 1 && $this->isCycle());
    }

    /**
     * checks whether this walk HAS a loop (single edge connecting vertex A with vertex A again)
     *
     * The following Walk is NOT a valid loop, but it contains a valid loop:
     *
     *      /->3
     *      |
     * 1 -> 2 -\
     *      ^  |
     *      \--/
     *
     * @return bool
     * @uses AlgorithmLoop::hasLoop()
     * @see self::isLoop()
     */
    public function hasLoop()
    {
        $alg = new AlgorithmLoop($this->walk);

        return $alg->hasLoop();
    }

    /**
     * checks whether this walk is a digon (a pair of parallel edges in a multigraph or a pair of antiparallel edges in a digraph)
     *
     * A digon is a cycle connecting exactly two distinct vertices with exactly
     * two distinct edges.
     *
     * The following Graph represents a digon in an undirected Graph:
     *
     *  /--\
     * 1    2
     *  \--/
     *
     * The following Graph represents a digon as a set of antiparallel directed
     * Edges in a directed Graph:
     *
     * 1 -> 2
     * ^    |
     * |    |
     * \----/
     *
     * @return bool
     * @uses self::hasArrayDuplicates()
     * @uses self::isCycle()
     */
    public function isDigon()
    {
        // exactly 2 edges
        return (\count($this->walk->getEdges()) === 2 &&
                // no duplicate edges
                !$this->hasArrayDuplicates($this->walk->getEdges()->getVector()) &&
                // exactly two distinct vertices
                \count($this->walk->getVertices()->getVerticesDistinct()) === 2 &&
                // this is actually a cycle
                $this->isCycle());
    }

    /**
     * checks whether this walk is a triangle (a simple cycle with exactly three distinct vertices)
     *
     * The following Graph is a valid directed triangle:
     *
     * 1->2->3
     * ^     |
     * \-----/
     *
     * @return bool
     * @uses self::isCycle()
     */
    public function isTriangle()
    {
        // exactly 3 (implicitly distinct) edges
        return (\count($this->walk->getEdges()) === 3 &&
                // exactly three distinct vertices
                \count($this->walk->getVertices()->getVerticesDistinct()) === 3 &&
                // this is actually a cycle
                $this->isCycle());
    }

    /**
     * check whether this walk is simple
     *
     * contains no duplicate/repeated vertices (and thus no duplicate edges either)
     * other than the starting and ending vertices of cycles.
     *
     * A simple Walk is also known as a chain.
     *
     * The term "simple walk" is somewhat related to a walk with no cycles. If
     * a Walk has a cycle, it is not simple - with one single exception: a Walk
     * that IS a cycle automatically also contains a cycle, but if it contains
     * no "further" additional cycles, it is considered a simple cycle.
     *
     * The following Graph represents a (very) simple Walk:
     *
     * 1 -- 2
     *
     * The following Graph IS a cycle and is simple:
     *
     * 1 -> 2
     * ^    |
     * \----/
     *
     * The following Graph contains a cycle and is NOT simple:
     *
     *      /->4
     *      |
     * 1 -> 2 -> 3 -\
     *      ^       |
     *      \-------/
     *
     * The following Graph IS a cycle and thus automatically contains a cycle.
     * Due to the additional "inner" cycle (loop at vertex 2), it is NOT simple:
     *
     *      /->3--\
     *      |     |
     * 1 -> 2 -\  |
     * ^    ^  |  |
     * |    \--/  |
     * |          |
     * \----------/
     *
     * @return bool
     * @uses self::isCycle()
     * @uses self::hasArrayDuplicates()
     * @see self::hasCycle()
     */
    public function isSimple()
    {
        $vertices = $this->walk->getVertices()->getVector();
        // ignore starting vertex for cycles as it's always the same as ending vertex
        if ($this->isCycle()) {
            unset($vertices[0]);
        }

        return !$this->hasArrayDuplicates($vertices);
    }

    /**
     * checks whether walk is hamiltonian (i.e. walk over ALL VERTICES of the graph)
     *
     * A hamiltonian Walk is also known as a spanning walk.
     *
     * @return bool
     * @see self::isEulerian() if you want to check for all EDGES instead of VERTICES
     * @uses self::isArrayContentsEqual()
     * @link http://en.wikipedia.org/wiki/Hamiltonian_path
     */
    public function isHamiltonian()
    {
        $vertices = $this->walk->getVertices()->getVector();
        // ignore starting vertex for cycles as it's always the same as ending vertex
        if ($this->isCycle()) {
            unset($vertices[0]);
        }
        return $this->isArrayContentsEqual($vertices, $this->walk->getGraph()->getVertices()->getVector());
    }

    /**
     * checks whether walk is eulerian (i.e. a walk over ALL EDGES of the graph)
     *
     * @return bool
     * @see self::isHamiltonian() if you want to check for all VERTICES instead of EDGES
     * @uses self::isArrayContentsEqual()
     * @link http://en.wikipedia.org/wiki/Eulerian_path
     */
    public function isEulerian()
    {
        return $this->isArrayContentsEqual($this->walk->getEdges()->getVector(), $this->walk->getGraph()->getEdges()->getVector());
    }

    /**
     * checks whether ths given array contains duplicate identical entries
     *
     * @param  array $array
     * @return bool
     */
    private function hasArrayDuplicates($array)
    {
        $compare = array();
        foreach ($array as $element) {
            // duplicate element found
            if (\in_array($element, $compare, true)) {
                return true;
            } else {
                // add element to temporary array to check for duplicates
                $compare [] = $element;
            }
        }

        return false;
    }

    /**
     * checks whether the contents of array a equals those of array b (ignore keys and order but otherwise strict check)
     *
     * @param  array   $a
     * @param  array   $b
     * @return bool
     */
    private function isArrayContentsEqual($a, $b)
    {
        foreach ($b as $one) {
            $pos = \array_search($one, $a, true);
            if ($pos === false) {
                return false;
            } else {
                unset($a[$pos]);
            }
        }

        return $a ? false : true;
    }
}


================================================
FILE: src/ResidualGraph.php
================================================
<?php

namespace Graphp\Algorithms;

use Graphp\Graph\Edge;
use Graphp\Graph\EdgeDirected;
use Graphp\Graph\Exception\UnexpectedValueException;
use Graphp\Graph\Graph;

class ResidualGraph extends BaseGraph
{
    private $keepNullCapacity = false;
    private $mergeParallelEdges = false;

    public function setKeepNullCapacity($toggle)
    {
        $this->keepNullCapacity = !!$toggle;

        return $this;
    }

    public function setMergeParallelEdges($toggle)
    {
        $this->mergeParallelEdges = !!$toggle;

        return $this;
    }

    /**
     * create residual graph
     *
     * @throws UnexpectedValueException if input graph has undirected edges or flow/capacity is not set
     * @return Graph
     * @uses Graph::createGraphCloneEdgeless()
     * @uses Graph::createEdgeClone()
     * @uses Graph::createEdgeCloneInverted()
     */
    public function createGraph()
    {
        $newgraph = $this->graph->createGraphCloneEdgeless();

        foreach ($this->graph->getEdges() as $edge) {
            if (!($edge instanceof EdgeDirected)) {
                throw new UnexpectedValueException('Edge is undirected');
            }

            $flow = $edge->getFlow();
            if ($flow === NULL) {
                throw new UnexpectedValueException('Flow not set');
            }

            $capacity = $edge->getCapacity();
            if ($capacity === NULL) {
                throw new UnexpectedValueException('Capacity not set');
            }

            // capacity is still available, clone remaining capacity into new edge
            if ($this->keepNullCapacity || $flow < $capacity) {
                $newEdge = $newgraph->createEdgeClone($edge)->setFlow(0)->setCapacity($capacity - $flow);

                if ($this->mergeParallelEdges) {
                    $this->mergeParallelEdges($newEdge);
                }
            }

            // flow is set, clone current flow as capacity for back-flow into new inverted edge (opposite direction)
            if ($this->keepNullCapacity || $flow > 0) {
                $newEdge = $newgraph->createEdgeCloneInverted($edge)->setFlow(0)->setCapacity($flow);

                // if weight is set, use negative weight for back-edges
                if ($newEdge->getWeight() !== NULL) {
                    $newEdge->setWeight(-$newEdge->getWeight());
                }

                if ($this->mergeParallelEdges) {
                    $this->mergeParallelEdges($newEdge);
                }
            }
        }

        return $newgraph;
    }

    /**
     * Will merge all edges that are parallel to to given edge
     *
     * @param Edge $newEdge
     */
    private function mergeParallelEdges(Edge $newEdge)
    {
        $alg = new Parallel($this->graph);
        $parallelEdges = $alg->getEdgesParallelEdge($newEdge)->getVector();

        if (!$parallelEdges) {
            return;
        }

        $mergedCapacity = 0;
        foreach ($parallelEdges as $parallelEdge) {
            $mergedCapacity += $parallelEdge->getCapacity();
        }

        $newEdge->setCapacity($newEdge->getCapacity() + $mergedCapacity);

        foreach ($parallelEdges as $parallelEdge) {
            $parallelEdge->destroy();
        }
    }
}


================================================
FILE: src/Search/Base.php
================================================
<?php

namespace Graphp\Algorithms\Search;

use Graphp\Algorithms\BaseVertex;
use Graphp\Graph\Exception\InvalidArgumentException;
use Graphp\Graph\Set\Vertices;
use Graphp\Graph\Vertex;

abstract class Base extends BaseVertex
{
    const DIRECTION_FORWARD = 0;
    const DIRECTION_REVERSE = 1;
    const DIRECTION_BOTH = 2;

    private $direction = self::DIRECTION_FORWARD;

    /**
     * set direction in which to follow adjacent vertices
     *
     * @param  int             $direction
     * @return $this (chainable)
     * @throws InvalidArgumentException
     * @see self::getVerticesAdjacent()
     */
    public function setDirection($direction)
    {
        if ($direction !== self::DIRECTION_FORWARD && $direction !== self::DIRECTION_REVERSE && $direction !== self::DIRECTION_BOTH) {
            throw new InvalidArgumentException('Invalid direction given');
        }
        $this->direction = $direction;

        return $this;
    }

    protected function getVerticesAdjacent(Vertex $vertex)
    {
        if ($this->direction === self::DIRECTION_FORWARD) {
            return $vertex->getVerticesEdgeTo();
        } elseif ($this->direction === self::DIRECTION_REVERSE) {
            return $vertex->getVerticesEdgeFrom();
        } else {
            return $vertex->getVerticesEdge();
        }
    }

    /**
     * get set of all Vertices that can be reached from start vertex
     *
     * @return Vertices
     */
    abstract public function getVertices();
}


================================================
FILE: src/Search/BreadthFirst.php
================================================
<?php

namespace Graphp\Algorithms\Search;

use Graphp\Graph\Set\Vertices;

class BreadthFirst extends Base
{
    /**
     * @param int $maxDepth
     * @return Vertices
     */
    public function getVertices($maxDepth = PHP_INT_MAX)
    {
        $queue = array($this->vertex);
        // to not add vertices twice in array visited
        $mark = array($this->vertex->getId() => true);
        // visited vertices
        $visited = array();

        // keep track of depth
        $currentDepth = 0;
        $nodesThisLevel = 1;
        $nodesNextLevel = 0;

        do {
            // get first from queue
            $t = \array_shift($queue);
            // save as visited
            $visited[$t->getId()] = $t;

            // get next vertices
            $children = $this->getVerticesAdjacent($t);

            // track depth
            $nodesNextLevel = $children->count();
            if (--$nodesThisLevel === 0) {
                if (++$currentDepth > $maxDepth) {
                    return new Vertices($visited);
                }
                $nodesThisLevel = $nodesNextLevel;
                $nodesNextLevel = 0;
            }

            // process next vertices
            foreach ($children->getMap() as $id => $vertex) {
                // if not "touched" before
                if (!isset($mark[$id])) {
                    // add to queue
                    $queue[] = $vertex;
                    // and mark
                    $mark[$id] = true;
                }
            }

            // until queue is empty
        } while ($queue);

        return new Vertices($visited);
    }
}


================================================
FILE: src/Search/DepthFirst.php
================================================
<?php

namespace Graphp\Algorithms\Search;

use Graphp\Graph\Set\Vertices;

class DepthFirst extends Base
{
    /**
     * calculates an iterative depth-first search
     *
     * @return Vertices
     */
    public function getVertices()
    {
        $visited = array();
        $todo = array($this->vertex);
        while ($vertex = \array_shift($todo)) {
            if (!isset($visited[$vertex->getId()])) {
                $visited[$vertex->getId()] = $vertex;

                foreach (\array_reverse($this->getVerticesAdjacent($vertex)->getMap(), true) as $nextVertex) {
                    $todo[] = $nextVertex;
                }
            }
        }

        return new Vertices($visited);
    }
}


================================================
FILE: src/ShortestPath/Base.php
================================================
<?php

namespace Graphp\Algorithms\ShortestPath;

use Graphp\Algorithms\BaseVertex;
use Graphp\Graph\Edge;
use Graphp\Graph\Exception\InvalidArgumentException;
use Graphp\Graph\Exception\OutOfBoundsException;
use Graphp\Graph\Graph;
use Graphp\Graph\Set\Edges;
use Graphp\Graph\Set\Vertices;
use Graphp\Graph\Vertex;
use Graphp\Graph\Walk;

/**
 * Abstract base class for shortest path algorithms
 *
 * This abstract base class provides the base interface for working with
 * single-source shortest paths (SSSP).
 *
 * The shortest path problem is the problem of finding a path between two
 * vertices such that the sum of the weights of its constituent edges is
 * minimized. The weight of the shortest path is referred to as distance.
 *
 *    A--[10]-------------B---E<--F
 *     \                 /
 *      \--[4]--C--[2]--D
 *
 * In the above pictured graph, the distance (weight of the shortest path)
 * between A and C is 4, and the shortest path between A and B is "A->C->D->B"
 * with a distance (total weight) of 6.
 *
 * In graph theory, it is usually assumed that a path to an unreachable vertex
 * has infinite distance. In the above pictured graph, there's no way path
 * from A to F, i.e. vertex F is unreachable from vertex A because of the
 * directed edge "E <- F" pointing in the opposite direction. This library
 * considers this an Exception instead. So if you're asking for the distance
 * between A and F, you'll receive an OutOfBoundsException instead.
 *
 * In graph theory, it is usually assumed that each vertex has a (pseudo-)path
 * to itself with a distance of 0. In order to produce reliable, consistent
 * results, this library considers this (pseudo-)path to be non-existant, i.e.
 * there's NO "magic" path between A and A. So if you're asking for the distance
 * between A and A, you'll receive an OutOfBoundsException instead. This allows
 * us to check hether there's a real path between A and A (cycle via other
 * vertices) as well as working with loop edges.
 *
 * @link http://en.wikipedia.org/wiki/Shortest_path_problem
 * @link http://en.wikipedia.org/wiki/Tree_%28data_structure%29
 * @see ShortestPath\Dijkstra
 * @see ShortestPath\MooreBellmanFord which also supports negative Edge weights
 * @see ShortestPath\BreadthFirst with does not consider Edge weights, but only the number of hops
 */
abstract class Base extends BaseVertex
{
    /**
     * get walk (path) from start vertex to given end vertex
     *
     * @param  Vertex    $endVertex
     * @return Walk
     * @throws OutOfBoundsException if there's no path to the given end vertex
     * @uses self::getEdgesTo()
     * @uses Walk::factoryFromEdges()
     */
    public function getWalkTo(Vertex $endVertex)
    {
        return Walk::factoryFromEdges($this->getEdgesTo($endVertex), $this->vertex);
    }

    /**
     * get array of edges (path) from start vertex to given end vertex
     *
     * @param  Vertex    $endVertex
     * @throws OutOfBoundsException if there's no path to the given end vertex
     * @return Edges
     * @uses self::getEdges()
     * @uses self::getEdgesToInternal()
     */
    public function getEdgesTo(Vertex $endVertex)
    {
        return $this->getEdgesToInternal($endVertex, $this->getEdges());
    }

    /**
     * get array of edges (path) from start vertex to given end vertex
     *
     * @param  Vertex       $endVertex
     * @param  Edges|Edge[] $edges     set or array of all input edges to operate on
     * @throws OutOfBoundsException if there's no path to the given vertex
     * @return Edges
     * @uses self::getEdges() if no edges were given
     */
    protected function getEdgesToInternal(Vertex $endVertex, $edges)
    {
        $currentVertex = $endVertex;
        $path = array();
        do {
            $pre = NULL;
            // check all edges to search for edge that points TO current vertex
            foreach ($edges as $edge) {
                try {
                    // get start point of this edge (fails if current vertex is not its end point)
                    $pre = $edge->getVertexFromTo($currentVertex);
                    $path[] = $edge;
                    $currentVertex = $pre;
                    break;
                } catch (InvalidArgumentException $ignore) {
                } // ignore: this edge does not point TO current vertex
            }
            if ($pre === NULL) {
                throw new OutOfBoundsException('No edge leading to vertex');
            }
        } while ($currentVertex !== $this->vertex);

        return new Edges(\array_reverse($path));
    }

    /**
     * get sum of weight of given edges
     *
     * @param  Edges $edges
     * @return float
     * @uses Edge::getWeight()
     */
    private function sumEdges(Edges $edges)
    {
        $sum = 0;
        foreach ($edges as $edge) {
            $sum += $edge->getWeight();
        }

        return $sum;
    }

    /**
     * get set of all Vertices the given start vertex has a path to
     *
     * @return Vertices
     * @uses self::getDistanceMap()
     */
    public function getVertices()
    {
        $vertices = array();
        $map = $this->getDistanceMap();
        foreach ($this->vertex->getGraph()->getVertices()->getMap() as $vid => $vertex) {
            if (isset($map[$vid])) {
                $vertices[$vid] = $vertex;
            }
        }

        return new Vertices($vertices);
    }

    /**
     * checks whether there's a path from this start vertex to given end vertex
     *
     * @param  Vertex  $vertex
     * @return bool
     * @uses self::getEdgesTo()
     */
    public function hasVertex(Vertex $vertex)
    {
        try {
            $this->getEdgesTo($vertex);
        } catch (OutOfBoundsException $e) {
            return false;
        }
        return true;
    }

    /**
     * get map of vertex IDs to distance
     *
     * @return float[]
     * @uses self::getEdges()
     * @uses self::getEdgesToInternal()
     * @uses self::sumEdges()
     */
    public function getDistanceMap()
    {
        $edges = $this->getEdges();
        $ret = array();
        foreach ($this->vertex->getGraph()->getVertices()->getMap() as $vid => $vertex) {
            try {
                $ret[$vid] = $this->sumEdges($this->getEdgesToInternal($vertex, $edges));
            } catch (OutOfBoundsException $ignore) {
            } // ignore vertices that can not be reached
        }

        return $ret;
    }

    /**
     * get distance (sum of weights) between start vertex and given end vertex
     *
     * @param  Vertex    $endVertex
     * @return float
     * @throws OutOfBoundsException if there's no path to the given end vertex
     * @uses self::getEdgesTo()
     * @uses self::sumEdges()
     */
    public function getDistance(Vertex $endVertex)
    {
        return $this->sumEdges($this->getEdgesTo($endVertex));
    }

    /**
     * create new resulting graph with only edges on shortest path
     *
     * The resulting Graph will always represent a tree with the start vertex
     * being the root vertex.
     *
     * For example considering the following input Graph with equal weights on
     * each edge:
     *
     *     A----->F
     *    / \     ^
     *   /   \   /
     *  /     \ /
     *  |      E
     *  |       \
     *  |        \
     *  B--->C<---D
     *
     * The resulting shortest path tree Graph will look like this:
     *
     *     A----->F
     *    / \
     *   /   \
     *  /     \
     *  |      E
     *  |       \
     *  |        \
     *  B--->C    D
     *
     * Or by just arranging the Vertices slightly different:
     *
     *          A
     *         /|\
     *        / | \
     *       B  E  \->F
     *      /   |
     *  C<-/    D
     *
     * @return Graph
     * @uses self::getEdges()
     * @uses Graph::createGraphCloneEdges()
     */
    public function createGraph()
    {
        return $this->vertex->getGraph()->createGraphCloneEdges($this->getEdges());
    }

    /**
     * get cheapest edges (lowest weight) for given map of vertex predecessors
     *
     * @param  Vertex[] $predecessor
     * @return Edges
     * @uses Graph::getVertices()
     * @uses Vertex::getEdgesTo()
     * @uses Edges::getEdgeOrder()
     */
    protected function getEdgesCheapestPredecesor(array $predecessor)
    {
        $vertices = $this->vertex->getGraph()->getVertices()->getMap();

        $edges = array();
        foreach ($vertices as $vid => $vertex) {
            if (isset($predecessor[$vid])) {
                // get predecor
                $predecesVertex = $predecessor[$vid];

                // get cheapest edge
                $edges[] = $predecesVertex->getEdgesTo($vertex)->getEdgeOrder(Edges::ORDER_WEIGHT);
            }
        }

        return new Edges($edges);
    }

    /**
     * get all edges on shortest path for this vertex
     *
     * @return Edges
     */
    abstract public function getEdges();
}


================================================
FILE: src/ShortestPath/BreadthFirst.php
================================================
<?php

namespace Graphp\Algorithms\ShortestPath;

use Graphp\Graph\Vertex;
use Graphp\Graph\Exception\OutOfBoundsException;
use Graphp\Graph\Set\Edges;
use Graphp\Graph\Set\Vertices;

/**
 * Simple breadth-first shortest path algorithm
 *
 * This algorithm ignores edge weights and operates as a level-order algorithm
 * on the number of hops. As such, it considers the path with the least number
 * of hops to be shortest.
 *
 * This is particularly useful your Graph doesn't have Edge weights assigned to
 * begin with or if you're merely interested in knowing which Vertices can be
 * reached at all (path finding). This avoids running expensive operations to
 * determine the actual weight (distance) of a path.
 */
class BreadthFirst extends Base
{
    /**
     * get distance between start vertex and given end vertex
     *
     * @param  Vertex               $endVertex
     * @throws OutOfBoundsException if there's no path to given end vertex
     * @return float
     * @uses self::getEdgesTo()
     */
    public function getDistance(Vertex $endVertex)
    {
        return (float)\count($this->getEdgesTo($endVertex));
    }

    /**
     * get array of edges on the walk for each vertex (vertex ID => array of walk edges)
     *
     * @return array[]
     */
    public function getEdgesMap()
    {
        $vertexQueue = array();
        $edges = array();

        // $edges[$this->vertex->getId()] = array();

        $vertexCurrent = $this->vertex;
        $edgesCurrent = array();

        do {
            foreach ($vertexCurrent->getEdgesOut() as $edge) {
                $vertexTarget = $edge->getVertexToFrom($vertexCurrent);
                $vid = $vertexTarget->getId();
                if (!isset($edges[$vid])) {
                    $vertexQueue[] = $vertexTarget;
                    $edges[$vid] = \array_merge($edgesCurrent, array($edge));
                }
            }

            // get next from queue
            $vertexCurrent = \array_shift($vertexQueue);
            if ($vertexCurrent) {
                $edgesCurrent = $edges[$vertexCurrent->getId()];
            }
        // untill queue is empty
        } while ($vertexCurrent);

        return $edges;
    }

    public function getEdgesTo(Vertex $endVertex)
    {
        if ($endVertex->getGraph() === $this->vertex->getGraph()) {
            $map = $this->getEdgesMap();

            if (isset($map[$endVertex->getId()])) {
                return new Edges($map[$endVertex->getId()]);
            }
        }
        throw new OutOfBoundsException('Given target vertex can not be reached from start vertex');
    }

    /**
     * get map of vertex IDs to distance
     *
     * @return float[]
     * @uses Vertex::hasLoop()
     */
    public function getDistanceMap()
    {
        $ret = array();
        foreach ($this->getEdgesMap() as $vid => $edges) {
            $ret[$vid] = (float)\count($edges);
        }

        return $ret;
    }

    /**
     * get array of all target vertices this vertex has a path to
     *
     * @return Vertices
     * @uses self::getEdgesMap()
     */
    public function getVertices()
    {
        $ret = array();
        $graph = $this->vertex->getGraph();
        foreach (\array_keys($this->getEdgesMap()) as $vid) {
            $ret[$vid] = $graph->getVertex($vid);
        }

        return new Vertices($ret);
    }

    public function getEdges()
    {
        $ret = array();
        foreach ($this->getEdgesMap() as $edges) {
            foreach ($edges as $edge) {
                if (!\in_array($edge, $ret, true)) {
                    $ret[] = $edge;
                }
            }
        }

        return new Edges($ret);
    }
}


================================================
FILE: src/ShortestPath/Dijkstra.php
================================================
<?php

namespace Graphp\Algorithms\ShortestPath;

use Graphp\Graph\Exception\UnexpectedValueException;
use Graphp\Graph\Set\Edges;
use SplPriorityQueue;

/**
 * Commonly used Dijkstra's shortest path algorithm
 *
 * This is asymptotically the fastest known single-source shortest-path
 * algorithm for arbitrary graphs with non-negative weights. If your Graph
 * contains an Edge with negative weight, if will throw an
 * UnexpectedValueException. Consider using (the slower) MooreBellmanFord
 * algorithm instead.
 *
 * @link http://en.wikipedia.org/wiki/Dijkstra%27s_algorithm
 * @see MooreBellmanFord
 */
class Dijkstra extends Base
{
    /**
     * get all edges on shortest path for this vertex
     *
     * @return Edges
     * @throws UnexpectedValueException when encountering an Edge with negative weight
     */
    public function getEdges()
    {
        $totalCostOfCheapestPathTo  = Array();
        // start node distance
        $totalCostOfCheapestPathTo[$this->vertex->getId()] = INF;

        // just to get the cheapest vertex in the correct order
        $cheapestVertex = new SplPriorityQueue();
        $cheapestVertex->insert($this->vertex, 0);

        // predecessor
        $predecesVertexOfCheapestPathTo  = Array();
        $predecesVertexOfCheapestPathTo[$this->vertex->getId()] = $this->vertex;

        // mark vertices when their cheapest path has been found
        $usedVertices  = Array();

        $isFirst = true;

        // Repeat until all vertices have been marked
        $totalCountOfVertices = \count($this->vertex->getGraph()->getVertices());
        for ($i = 0; $i < $totalCountOfVertices; ++$i) {
            $currentVertex = NULL;
            $currentVertexId = NULL;
            $isEmpty = false;
            do {
                // if the priority queue is empty there are isolated vertices, but the algorithm visited all other vertices
                if ($cheapestVertex->isEmpty()) {
                    $isEmpty = true;
                    break;
                }
                // Get cheapest unmarked vertex
                $currentVertex = $cheapestVertex->extract();
                $currentVertexId = $currentVertex->getId();
            // Vertices can be in the priority queue multiple times, with different path costs (if vertex is already marked, this is an old unvalid entry)
            } while (isset($usedVertices[$currentVertexId]));

            // catch "algorithm ends" condition
            if ($isEmpty) {
                break;
            }

            if ($isFirst) {
                $isFirst = false;
            } else {
                // mark this vertex
                $usedVertices[$currentVertexId] = true;
            }

            // check for all edges of current vertex if there is a cheaper path (or IN OTHER WORDS: Add reachable nodes from currently added node and refresh the current possible distances)
            foreach ($currentVertex->getEdgesOut() as $edge) {
                $weight = $edge->getWeight();
                if ($weight < 0) {
                    throw new UnexpectedValueException('Djkstra not supported for negative weights - Consider using MooreBellmanFord');
                }

                $targetVertex = $edge->getVertexToFrom($currentVertex);
                $targetVertexId = $targetVertex->getId();

                // if the targetVertex is marked, the cheapest path for this vertex has already been found (no negative edges) {
                if (!isset($usedVertices[$targetVertexId])) {
                    // calculate new cost to vertex
                    $newCostsToTargetVertex = $totalCostOfCheapestPathTo[$currentVertexId] + $weight;
                    if (\is_infinite($newCostsToTargetVertex)) {
                        $newCostsToTargetVertex = $weight;
                    }

                    if ((!isset($predecesVertexOfCheapestPathTo[$targetVertexId]))
                           // is the new path cheaper?
                           || $totalCostOfCheapestPathTo[$targetVertexId] > $newCostsToTargetVertex){

                        // Not an update, just an new insert with lower cost
                        $cheapestVertex->insert($targetVertex, - $newCostsToTargetVertex);
                                                                                                    // so the lowest cost will be extraced first
                                                                                                    // and higher cost will be skipped during extraction

                        // update/set costs found with the new connection
                        $totalCostOfCheapestPathTo[$targetVertexId] = $newCostsToTargetVertex;
                        // update/set predecessor vertex from the new connection
                        $predecesVertexOfCheapestPathTo[$targetVertexId] = $currentVertex;
                    }
                }
            }
        }

        if ($totalCostOfCheapestPathTo[$this->vertex->getId()] === INF) {
            unset($predecesVertexOfCheapestPathTo[$this->vertex->getId()]);
        }

        // algorithm is done, return resulting edges
        return $this->getEdgesCheapestPredecesor($predecesVertexOfCheapestPathTo);
    }
}


================================================
FILE: src/ShortestPath/MooreBellmanFord.php
================================================
<?php

namespace Graphp\Algorithms\ShortestPath;

use Graphp\Graph\Exception\NegativeCycleException;
use Graphp\Graph\Exception\UnderflowException;
use Graphp\Graph\Set\Edges;
use Graphp\Graph\Vertex;
use Graphp\Graph\Walk;

/**
 * Moore-Bellman-Ford's shortest path algorithm
 *
 * It is slower than Dijkstra's algorithm for the same problem, but more
 * versatile, as it is capable of handling Graphs with negative Edge weights.
 *
 * Also known as just "Bellman–Ford algorithm".
 *
 * @link http://en.wikipedia.org/wiki/Bellman%E2%80%93Ford_algorithm
 */
class MooreBellmanFord extends Base
{
    /**
     * @param Edges    $edges
     * @param int[]    $totalCostOfCheapestPathTo
     * @param Vertex[] $predecessorVertexOfCheapestPathTo
     * @return Vertex|NULL
     */
    private function bigStep(Edges $edges, array &$totalCostOfCheapestPathTo, array &$predecessorVertexOfCheapestPathTo)
    {
        $changed = NULL;
        // check for all edges
        foreach ($edges as $edge) {
            // check for all "ends" of this edge (or for all targetes)
            foreach ($edge->getVerticesTarget() as $toVertex) {
                $fromVertex = $edge->getVertexFromTo($toVertex);

                // If the fromVertex already has a path
                if (isset($totalCostOfCheapestPathTo[$fromVertex->getId()])) {
                    // New possible costs of this path
                    $newCost = $totalCostOfCheapestPathTo[$fromVertex->getId()] + $edge->getWeight();
                    if (\is_infinite($newCost)) {
                        $newCost = $edge->getWeight() + 0;
                    }

                    // No path has been found yet
                    if (!isset($totalCostOfCheapestPathTo[$toVertex->getId()])
                            // OR this path is cheaper than the old path
                            || $totalCostOfCheapestPathTo[$toVertex->getId()] > $newCost){

                        $changed = $toVertex;
                        $totalCostOfCheapestPathTo[$toVertex->getId()] = $newCost;
                        $predecessorVertexOfCheapestPathTo[$toVertex->getId()] = $fromVertex;
                    }
                }
            }
        }

        return $changed;
    }

    /**
     * Calculate the Moore-Bellman-Ford-Algorithm and get all edges on shortest path for this vertex
     *
     * @return Edges
     * @throws NegativeCycleException if there is a negative cycle
     */
    public function getEdges()
    {
        // start node distance, add placeholder weight
        $totalCostOfCheapestPathTo  = array($this->vertex->getId() => INF);

        // predecessor
        $predecessorVertexOfCheapestPathTo  = array($this->vertex->getId() => $this->vertex);

        // the usal algorithm says we repeat (n-1) times.
        // but because we also want to check for loop edges on the start vertex,
        // we have to add an additional step:
        $numSteps = \count($this->vertex->getGraph()->getVertices());
        $edges = $this->vertex->getGraph()->getEdges();
        $changed = true;

        for ($i = 0; $i < $numSteps && $changed; ++$i) {
            $changed = $this->bigStep($edges, $totalCostOfCheapestPathTo, $predecessorVertexOfCheapestPathTo);
        }

        // no cheaper edge to start vertex found => remove placeholder weight
        if ($totalCostOfCheapestPathTo[$this->vertex->getId()] === INF) {
            unset($predecessorVertexOfCheapestPathTo[$this->vertex->getId()]);
        }

        // algorithm is done, build graph
        $returnEdges = $this->getEdgesCheapestPredecesor($predecessorVertexOfCheapestPathTo);

        // Check for negative cycles (only if last step didn't already finish anyway)
        // something is still changing...
        if ($changed && $changed = $this->bigStep($edges, $totalCostOfCheapestPathTo, $predecessorVertexOfCheapestPathTo)) {
            $cycle = Walk::factoryCycleFromPredecessorMap($predecessorVertexOfCheapestPathTo, $changed, Edges::ORDER_WEIGHT);
            throw new NegativeCycleException('Negative cycle found', 0, NULL, $cycle);
        }

        return $returnEdges;
    }

    /**
     * get negative cycle
     *
     * @return Walk
     * @throws UnderflowException if there's no negative cycle
     */
    public function getCycleNegative()
    {
        try {
            $this->getEdges();
        } catch (NegativeCycleException $e) {
            return $e->getCycle();
        }
        throw new UnderflowException('No cycle found');
    }
}


================================================
FILE: src/Symmetric.php
================================================
<?php

namespace Graphp\Algorithms;

use Graphp\Graph\EdgeDirected;

/**
 * Basic algorithms for working with symmetric digraphs
 *
 * A directed graph is called symmetric if, for every arc that belongs to it,
 * the corresponding reversed arc (antiparallel directed edge) also belongs to it.
 *
 * @link http://en.wikipedia.org/wiki/Directed_graph#Classes_of_digraphs
 */
class Symmetric extends BaseGraph
{
    /**
     * checks whether this graph is symmetric (for every edge a->b there's also an edge b->a)
     *
     * @return bool
     * @uses Graph::getEdges()
     * @uses EdgeDirected::getVertexStart()
     * @uses EdgeDirected::getVertedEnd()
     * @uses Vertex::hasEdgeTo()
     */
    public function isSymmetric()
    {
        // check all edges
        foreach ($this->graph->getEdges() as $edge) {
            // only check directed edges (undirected ones are symmetric by definition)
            if ($edge instanceof EdgeDirected) {
                // check if end also has an edge to start
                if (!$edge->getVertexEnd()->hasEdgeTo($edge->getVertexStart())) {
                    return false;
                }
            }
        }

        return true;
    }
}


================================================
FILE: src/TopologicalSort.php
================================================
<?php

namespace Graphp\Algorithms;

use Graphp\Graph\Exception\UnexpectedValueException;
use Graphp\Graph\Set\Vertices;
use Graphp\Graph\Vertex;

/**
 * topological sorting / order, also known as toposort / topsort, commonly used in resolving dependencies
 *
 * @author clue
 * @link http://en.wikipedia.org/wiki/Topological_sorting
 */
class TopologicalSort extends BaseGraph
{
    /**
     * run algorithm and return an ordered/sorted set of Vertices
     *
     * the topological sorting may be non-unique depending on your edges
     *
     * @return Vertices
     */
    public function getVertices()
    {
        $stack = array(); // visited nodes with unvisited children
        $visited = array();
        $output = array();

        // TODO: avoid having to reverse all vertices multiple times
        // pick a node to examine next - assume there are isolated nodes
        foreach (\array_reverse($this->graph->getVertices()->getVector()) as $top) {
            assert($top instanceof Vertex);
            $tid = $top->getId();
            if (!isset($visited[$tid])) { // don't examine if already found
                \array_push($stack, $top);
            }

            while ($stack) {
                $node = \end($stack);
                assert($node instanceof Vertex);
                $nid = $node->getId();

                $visited[$nid] = false; // temporary mark
                $found = false; // new children found during visit to this node

                // find the next node to visit
                foreach (\array_reverse($node->getVerticesEdgeTo()->getVector()) as $child) {
                    assert($child instanceof Vertex);
                    $cid = $child->getId();
                    if (!isset($visited[$cid])) {
                        // found a new node - push it onto the stack
                        \array_push($stack, $child);
                        $found = true; // move onto the new node
                        break;
                    } else if ($visited[$cid] === false) {
                        // temporary mark => not a DAG
                        throw new UnexpectedValueException('Not a DAG');
                    }
                }

                if (!$found) {
                    \array_pop($stack); // no new children found - we're done with this node
                    $visited[$nid] = true; // mark as visited
                    \array_push($output, $node); // add to results
                }
            }
        }

        return new Vertices(\array_reverse($output, true));
    }
}


================================================
FILE: src/TransposeGraph.php
================================================
<?php

namespace Graphp\Algorithms;

use Graphp\Graph\EdgeDirected;
use Graphp\Graph\Exception\UnexpectedValueException;
use Graphp\Graph\Graph;

class TransposeGraph extends BaseGraph
{
    /**
     * create transpose graph
     *
     * @throws UnexpectedValueException if input graph has undirected edges
     * @return Graph
     * @uses Graph::createGraphCloneEdgeless()
     * @uses Graph::createEdgeClone()
     * @uses Graph::createEdgeCloneInverted()
     */
    public function createGraph()
    {
        $newgraph = $this->graph->createGraphCloneEdgeless();

        foreach ($this->graph->getEdges() as $edge) {
            if (!($edge instanceof EdgeDirected)) {
                throw new UnexpectedValueException('Edge is undirected');
            }
            $newgraph->createEdgeCloneInverted($edge);
        }

        return $newgraph;
    }
}


================================================
FILE: src/TravelingSalesmanProblem/Base.php
================================================
<?php

namespace Graphp\Algorithms\TravelingSalesmanProblem;

use Graphp\Algorithms\Base as AlgorithmBase;
use Graphp\Graph\Graph;
use Graphp\Graph\Set\Edges;
use Graphp\Graph\Vertex;
use Graphp\Graph\Walk;

abstract class Base extends AlgorithmBase
{
    /**
     * get resulting graph with the (first) best circle of edges connecting all vertices
     *
     * @throws \Exception on error
     * @return Graph
     * @uses self::getGraph()
     * @uses self::getEdges()
     * @uses Graph::createGraphCloneEdges()
     */
    public function createGraph()
    {
        return $this->getGraph()->createGraphCloneEdges($this->getEdges());
    }

    /**
     * get graph this algorithm operates on
     *
     * @return Graph
     */
    abstract protected function getGraph();

    /**
     * get start vertex this algorithm starts on
     *
     * @return Vertex
     */
    abstract protected function getVertexStart();

    /**
     * get (first) best circle connecting all vertices
     *
     * @return Walk
     * @uses self::getEdges()
     * @uses self::getVertexStart()
     * @uses Walk::factoryCycleFromEdges()
     */
    public function getCycle()
    {
        return Walk::factoryCycleFromEdges($this->getEdges(), $this->getVertexStart());
    }

    public function getWeight()
    {
        $weight = 0;
        foreach ($this->getEdges() as $edge) {
            $weight += $edge->getWeight();
        }

        return $weight;
    }

    /**
     * get array of edges connecting all vertices in a circle
     *
     * @return Edges
     */
    abstract public function getEdges();
}


================================================
FILE: src/TravelingSalesmanProblem/Bruteforce.php
================================================
<?php

namespace Graphp\Algorithms\TravelingSalesmanProblem;

use Graphp\Algorithms\TravelingSalesmanProblem\MinimumSpanningTree as AlgorithmTspMst;
use Graphp\Graph\Edge;
use Graphp\Graph\Exception\UnexpectedValueException;
use Graphp\Graph\Exception\UnderflowException;
use Graphp\Graph\Graph;
use Graphp\Graph\Set\Edges;
use Graphp\Graph\Vertex;

class Bruteforce extends Base
{
    /**
     *
     * @var Graph
     */
    private $graph;

    /**
     * best weight so for (used for branch-and-bound)
     *
     * @var number|NULL
     */
    private $bestWeight;

    /**
     * reference to start vertex
     *
     * @var Vertex
     */
    private $startVertex;

    /**
     * total number of edges needed
     *
     * @var int
     */
    private $numEdges;

    /**
     * upper limit to use for branch-and-bound (BNB)
     *
     * @var float|NULL
     * @see self::setUpperLimit()
     */
    private $upperLimit = NULL;

    /**
     * whether to use branch-and-bound
     *
     * simply put, there's no valid reason why anybody would want to turn off this flag
     *
     * @var bool
     */
    private $branchAndBound = true;

    /**
     *
     * @param Graph $graph
     */
    public function __construct(Graph $graph)
    {
        $this->graph = $graph;
    }

    /**
     * explicitly set upper limit to use for branch-and-bound
     *
     * this method can be used to optimize the algorithm by providing an upper
     * bound of when to stop branching any further.
     *
     * @param  float $limit
     * @return self  $this (chainable)
     */
    public function setUpperLimit($limit)
    {
        $this->upperLimit = $limit;

        return $this;
    }

    /**
     * automatically sets upper limit to use for branch-and-bound from the MST heuristic
     *
     * @return self $this (chainable)
     * @uses AlgorithmTspMst
     */
    public function setUpperLimitMst()
    {
        $alg = new AlgorithmTspMst($this->graph);
        $this->upperLimit = $alg->getWeight();

        return $this;
    }

    protected function getVertexStart()
    {
        // actual start doesn't really matter as we're only considering complete graphs here
        return $this->graph->getVertices()->getVertexFirst();
    }

    protected function getGraph()
    {
        return $this->graph;
    }

    /**
     * get resulting (first) best circle of edges connecting all vertices
     *
     * @throws \Exception on error
     * @return Edges
     */
    public function getEdges()
    {
        $this->numEdges = \count($this->graph->getVertices());
        if ($this->numEdges < 3) {
            throw new UnderflowException('Needs at least 3 vertices');
        }

        // numEdges 3-12 should work

        $this->bestWeight = $this->upperLimit;
        $this->startVertex = $this->getVertexStart();

        $result = $this->step($this->startVertex,
                              0,
                              array(),
                              array()
                  );

        if ($result === NULL) {
            throw new UnexpectedValueException('No resulting solution for TSP found');
        }

        return new Edges($result);
    }

    /**
     *
     * @param  Vertex    $vertex          current point-of-view
     * @param  number    $totalWeight     total weight (so far)
     * @param  bool[] $visitedVertices
     * @param  Edge[]    $visitedEdges
     * @return Edge[]|null
     */
    private function step(Vertex $vertex, $totalWeight, array $visitedVertices, array $visitedEdges)
    {
        // stop recursion if best result is exceeded (branch and bound)
        if ($this->branchAndBound && $this->bestWeight !== NULL && $totalWeight >= $this->bestWeight) {
            return NULL;
        }
        // kreis geschlossen am Ende
        if ($vertex === $this->startVertex && \count($visitedEdges) === $this->numEdges) {
            // new best result
            $this->bestWeight = $totalWeight;

            return $visitedEdges;
        }

        // only visit each vertex once
        if (isset($visitedVertices[$vertex->getId()])) {
            return NULL;
        }
        $visitedVertices[$vertex->getId()] = true;

        $bestResult = NULL;

        // weiter verzweigen in alle vertices
        foreach ($vertex->getEdgesOut() as $edge) {
            // get target vertex of this edge
            $target = $edge->getVertexToFrom($vertex);

            $weight = $edge->getWeight();
            if ($weight < 0) {
                throw new UnexpectedValueException('Edge with negative weight "' . $weight . '" not supported');
            }

            $result = $this->step($target,
                                  $totalWeight + $weight,
                                  $visitedVertices,
                                  \array_merge($visitedEdges, array($edge))
                      );

            // new result found
            if ($result !== NULL) {
                // branch and bound enabled (default): returned result MUST be the new best result
                if($this->branchAndBound ||
                   // this is the first result, just use it anyway
                   $bestResult === NULL ||
                   // this is the new best result
                   $this->sumEdges($result) < $this->sumEdges($bestResult)){
                    $bestResult = $result;
                }
            }
        }

        return $bestResult;
    }

    /**
     * get sum of weight of given edges
     *
     * no need to optimize this further, as it's only evaluated if branchAndBound is disabled and
     * there's no valid reason why anybody would want to do so.
     *
     * @param  Edge[] $edges
     * @return float
     */
    private function sumEdges(array $edges)
    {
        $sum = 0;
        foreach ($edges as $edge) {
            $sum += $edge->getWeight();
        }

        return $sum;
    }
}


================================================
FILE: src/TravelingSalesmanProblem/MinimumSpanningTree.php
================================================
<?php

namespace Graphp\Algorithms\TravelingSalesmanProblem;

use Graphp\Algorithms\MinimumSpanningTree\Kruskal as MstKruskal;
use Graphp\Algorithms\Search\BreadthFirst as SearchDepthFirst;
use Graphp\Graph\Graph;
use Graphp\Graph\Set\Edges;

class MinimumSpanningTree extends Base
{
    /**
     * @var Graph
     */
    private $graph;

    public function __construct(Graph $inputGraph)
    {
        $this->graph = $inputGraph;
    }

    protected function getVertexStart()
    {
        return $this->graph->getVertices()->getVertexFirst();
    }

    protected function getGraph()
    {
        return $this->graph;
    }

    /**
     * @return Edges
     */
    public function getEdges()
    {
        $returnEdges = array();

        // Create minimum spanning tree
        $minimumSpanningTreeAlgorithm = new MstKruskal($this->graph);
        $minimumSpanningTree = $minimumSpanningTreeAlgorithm->createGraph();

        $alg = new SearchDepthFirst($minimumSpanningTree->getVertices()->getVertexFirst());
        // Depth first search in minmum spanning tree (for the eulerian path)

        $startVertex = NULL;
        $oldVertex = NULL;

        // connect vertices in order of the depth first search
        foreach ($alg->getVertices() as $vertex) {

            // get vertex from the original graph (not from the depth first search)
            $vertex = $this->graph->getVertex($vertex->getId());
                                                                                // need to clone the edge from the original graph, therefore i need the original edge
            if ($startVertex === NULL) {
                $startVertex = $vertex;
            } else {
                // get edge(s) to clone, multiple edges are possible (returns an array if undirected edge)
                assert($oldVertex !== null);
                $returnEdges[] = $oldVertex->getEdgesTo($vertex)->getEdgeFirst();
            }

            $oldVertex = $vertex;
        }

        // connect last vertex with start vertex
        // multiple edges are possible (returns an array if undirected edge)
        assert($startVertex !== null && $oldVertex !== null);
        $returnEdges[] = $oldVertex->getEdgesTo($startVertex)->getEdgeFirst();

        return new Edges($returnEdges);
    }
}


================================================
FILE: src/TravelingSalesmanProblem/NearestNeighbor.php
================================================
<?php

namespace Graphp\Algorithms\TravelingSalesmanProblem;

use Graphp\Graph\Exception\UnexpectedValueException;
use Graphp\Graph\Set\Edges;
use Graphp\Graph\Vertex;
use SplPriorityQueue;

class NearestNeighbor extends Base
{
    /**
     * @var Vertex
     */
    private $vertex;

    public function __construct(Vertex $startVertex)
    {
         $this->vertex = $startVertex;
    }

    protected function getVertexStart()
    {
        return $this->vertex;
    }

    protected function getGraph()
    {
        return $this->vertex->getGraph();
    }

    /**
     * @return Edges
     */
    public function getEdges()
    {
        $returnEdges = array();

        $n = \count($this->vertex->getGraph()->getVertices());

        $vertex = $nextVertex = $this->vertex;
        $visitedVertices = array($vertex->getId() => true);

        for ($i = 0; $i < $n - 1; ++$i,
                                    // n-1 steps (spanning tree)
                                    $vertex = $nextVertex) {

            // get all edges from the aktuel vertex
            $edges = $vertex->getEdgesOut();

            $sortedEdges = new SplPriorityQueue();

            // sort the edges
            foreach ($edges as $edge) {
                $sortedEdges->insert($edge, - $edge->getWeight());
            }

            // Untill first is found: get cheepest edge
            foreach ($sortedEdges as $edge) {

                // Get EndVertex of this edge
                $nextVertex = $edge->getVertexToFrom($vertex);

                // is unvisited
                if (!isset($visitedVertices[$nextVertex->getId()])) {
                    break;
                }
            }

            // check if there is a way i can use
            if (isset($visitedVertices[$nextVertex->getId()])) {
                throw new UnexpectedValueException('Graph is not complete - can\'t find an edge to unconnected vertex');
            }

            $visitedVertices[$nextVertex->getId()] = TRUE;

            // clone edge in new Graph
            assert(isset($edge));
            $returnEdges[] = $edge;

        }

        // check if there is a way from end edge to start edge
        // get first connecting edge
        // connect the last vertex with the start vertex
        $returnEdges[] = $vertex->getEdgesTo($this->vertex)->getEdgeFirst();

        return new Edges($returnEdges);
    }
}


================================================
FILE: src/Tree/Base.php
================================================
<?php

namespace Graphp\Algorithms\Tree;

use Graphp\Algorithms\BaseGraph;
use Graphp\Algorithms\Degree;
use Graphp\Graph\Graph;
use Graphp\Graph\Set\Vertices;
use Graphp\Graph\Vertex;

/**
 * Abstract base class for tree algorithms
 *
 * This abstract base class provides the base interface for working with
 * graphs that represent a tree.
 *
 * A tree is a connected Graph (single component) with no cycles. Every Tree is
 * a Graph, but not every Graph is a Tree. A null Graph (a Graph with no Vertices
 * and thus no Edges) is *NOT* considered a valid Tree, as it is not considered
 * connected (@see ConnectedComponents and @link)
 *
 *    A
 *   / \
 *  B   C
 *     / \
 *    D   E
 *
 * Special cases are undirected trees (like the one pictured above), handled via
 * Tree\Undirected and directed, rooted trees (InTree and OutTree), handled via
 * Tree\BaseDirected.
 *
 * @link http://en.wikipedia.org/wiki/Tree_%28graph_theory%29
 * @link http://en.wikipedia.org/wiki/Tree_%28data_structure%29
 * @link http://mathoverflow.net/questions/120536/is-the-empty-graph-a-tree
 * @see Undirected for an implementation of these algorithms on (undirected) trees
 * @see BaseDirected for an abstract implementation of these algorithms on directed, rooted trees
 */
abstract class Base extends BaseGraph
{
    /**
     * @var Degree
     */
    protected $degree;

    public function __construct(Graph $graph)
    {
        parent::__construct($graph);

        $this->degree = new Degree($graph);
    }

    /**
     * checks whether the given graph is actually a tree
     *
     * @return bool
     */
    abstract public function isTree();

    /**
     * checks if the given $vertex is a leaf (outermost vertext)
     *
     * leaf vertex is also known as leaf node, external node or terminal node
     *
     * @param Vertex $vertex
     * @return bool
     */
    abstract public function isVertexLeaf(Vertex $vertex);

    /**
     * checks if the given $vertex is an internal vertex (somewhere in the "middle" of the tree)
     *
     * internal vertex is also known as inner node (inode) or branch node
     *
     * @param Vertex $vertex
     * @return bool
     */
    abstract public function isVertexInternal(Vertex $vertex);

    /**
     * get array of leaf vertices (outermost vertices with no children)
     *
     * @return Vertices
     * @uses Graph::getVertices()
     * @uses self::isVertexLeaf()
     */
    public function getVerticesLeaf()
    {
        return $this->graph->getVertices()->getVerticesMatch(array($this, 'isVertexLeaf'));
    }

    /**
     * get array of internal vertices
     *
     * @return Vertices
     * @uses Graph::getVertices()
     * @uses self::isVertexInternal()
     */
    public function getVerticesInternal()
    {
        return $this->graph->getVertices()->getVerticesMatch(array($this, 'isVertexInternal'));
    }
}


================================================
FILE: src/Tree/BaseDirected.php
================================================
<?php

namespace Graphp\Algorithms\Tree;

use Graphp\Algorithms\Tree\Base as Tree;
use Graphp\Graph\Exception\UnderflowException;
use Graphp\Graph\Exception\UnexpectedValueException;
use Graphp\Graph\Set\Vertices;
use Graphp\Graph\Vertex;

/**
 * Abstract algorithm base class for working with directed, rooted trees
 *
 * Directed trees have an designated root Vertex, which is the uppermost Vertex.
 * Every other Vertex is either a directed child of this root Vertex or an
 * indirect descendant (recursive child).
 *
 * There are two common implementations of directed trees:
 *
 * - Usual OutTree implementation where Edges "point away" from root Vertex
 *
 *          ROOT
 *          /  \
 *    A <--/    \--> B
 *                   \
 *                    \--> C
 *
 * - Alternative InTree implementation where Edges "point towards" root Vertex
 *
 *         ROOT
 *         ^  ^
 *        /    \
 *       A      B
 *              ^
 *               \
 *                C
 *
 * It's your choice on how to direct the edges, but make sure they all point in
 * the "same direction", or it will not be a valid tree anymore. However your
 * decision may be, in the above example, ROOT is always the root Vertex,
 * B is the parent of "C" and A, B are the children of ROOT.
 *
 * For performance reasons, except for `isTree()`, none of the below methods
 * check if the given Graph is actually a valid tree. So make sure to verify
 * `isTree()` returns `true` before relying on any of the methods.
 *
 * @link http://en.wikipedia.org/wiki/Arborescence_%28graph_theory%29
 * @link http://en.wikipedia.org/wiki/Spaghetti_stack
 * @see OutTree usual implementation where Edges "point away" from root vertex
 * @see InTree alternative implementation where Edges "point towards" root vertex
 */
abstract class BaseDirected extends Tree
{
    /**
     * get root vertex for this in-tree
     *
     * @return Vertex
     * @throws UnderflowException if given graph is empty or no possible root candidate was found (check isTree()!)
     * @uses Graph::getVertices() to iterate over each Vertex
     * @uses self::isVertexPossibleRoot() to check if any Vertex is a possible root candidate
     */
    public function getVertexRoot()
    {
        foreach ($this->graph->getVertices() as $vertex) {
            if ($this->isVertexPossibleRoot($vertex)) {
                return $vertex;
            }
        }
        throw new UnderflowException('No possible root found. Either empty graph or no Vertex with proper degree found.');
    }

    /**
     * checks if this is a tree
     *
     * @return bool
     * @uses self::getVertexRoot() to get root Vertex to start search from
     * @uses self::getVerticesSubtree() to count number of vertices connected to root
     */
    public function isTree()
    {
        try {
            $root = $this->getVertexRoot();
        } catch (UnderflowException $e) {
            return false;
        } catch (UnexpectedValueException $e) {
            return false;
        }

        try {
            $num = \count($this->getVerticesSubtree($root));
        } catch (UnexpectedValueException $e) {
            return false;
        }

        // check number of vertices reachable from root should match total number of vertices
        return ($num === \count($this->graph->getVertices()));
    }

    /**
     * get parent vertex for given $vertex
     *
     * @param Vertex $vertex
     * @throws UnderflowException if vertex has no parent (is a root vertex)
     * @throws UnexpectedValueException if vertex has more than one possible parent (check isTree()!)
     * @return Vertex
     * @uses self::getVerticesParents() to get array of parent vertices
     */
    public function getVertexParent(Vertex $vertex)
    {
        $parents = $this->getVerticesParent($vertex);
        if (\count($parents) !== 1) {
            if ($parents->isEmpty()) {
                throw new UnderflowException('No parents for given vertex found');
            } else {
                throw new UnexpectedValueException('More than one parent');
            }
        }
        return $parents->getVertexFirst();
    }

    /**
     * get array of child vertices for given $vertex
     *
     * @param Vertex $vertex
     * @return Vertices
     * @throws UnexpectedValueException if the given $vertex contains invalid / parallel edges (check isTree()!)
     */
    abstract public function getVerticesChildren(Vertex $vertex);

    /**
     * internal helper to get all parents vertices
     *
     * a valid tree vertex only ever has a single parent, except for the root,
     * which has none.
     *
     * @param Vertex $vertex
     * @return Vertices
     * @throws UnexpectedValueException if the given $vertex contains invalid / parallel edges (check isTree()!)
     */
    abstract protected function getVerticesParent(Vertex $vertex);

    /**
     * check if given vertex is a possible root (i.e. has no parent)
     *
     * @param Vertex $vertex
     * @return bool
     * @uses self::getVerticesParent()
     */
    protected function isVertexPossibleRoot(Vertex $vertex)
    {
        return (\count($this->getVerticesParent($vertex)) === 0);
    }

    /**
     * checks if the given $vertex is a leaf (outermost vertex with no children)
     *
     * @param Vertex $vertex
     * @return bool
     * @uses self::getVerticesChildren() to check given vertex has no children
     */
    public function isVertexLeaf(Vertex $vertex)
    {
        return (\count($this->getVerticesChildren($vertex)) === 0);
    }

    /**
     * checks if the given $vertex is an internal vertex (has children and is not root)
     *
     * @param Vertex $vertex
     * @return bool
     * @uses self::getVerticesParent() to check given vertex has a parent (is not root)
     * @uses self::getVerticesChildren() to check given vertex has children (is not a leaf)
     * @see \Graphp\Algorithms\Tree\Base::isVertexInternal() for more information
     */
    public function isVertexInternal(Vertex $vertex)
    {
        return (!$this->getVerticesParent($vertex)->isEmpty() && !$this->getVerticesChildren($vertex)->isEmpty());
    }

    /**
     * get degree of tree (maximum number of children)
     *
     * @return int
     * @throws UnderflowException for empty graphs
     * @uses Graph::getVertices()
     * @uses self::getVerticesChildren()
     */
    public function getDegree()
    {
        $max = null;
        foreach ($this->graph->getVertices() as $vertex) {
            $num = \count($this->getVerticesChildren($vertex));
            if ($max === null || $num > $max) {
                $max = $num;
            }
        }
        if ($max === null) {
            throw new UnderflowException('No vertices found');
        }
        return $max;
    }

    /**
     * get depth of given $vertex (number of edges between root vertex)
     *
     * root has depth zero
     *
     * @param Vertex $vertex
     * @return int
     * @throws UnderflowException for empty graphs
     * @throws UnexpectedValueException if there's no path to root node (check isTree()!)
     * @uses self::getVertexRoot()
     * @uses self::getVertexParent() for each step
     */
    public function getDepthVertex(Vertex $vertex)
    {
        $root = $this->getVertexRoot();

        $depth = 0;
        while ($vertex !== $root) {
            $vertex = $this->getVertexParent($vertex);
            ++$depth;
        }
        return $depth;
    }

    /**
     * get height of this tree (longest downward path to a leaf)
     *
     * a single vertex graph has height zero
     *
     * @return int
     * @throws UnderflowException for empty graph
     * @uses self::getVertexRoot()
     * @uses self::getHeightVertex()
     */
    public function getHeight()
    {
        return $this->getHeightVertex($this->getVertexRoot());
    }

    /**
     * get height of given vertex (longest downward path to a leaf)
     *
     * leafs has height zero
     *
     * @param Vertex $vertex
     * @return int
     * @uses self::getVerticesChildren() to get children of given vertex
     * @uses self::getHeightVertex() to recurse into sub-children
     */
    public function getHeightVertex(Vertex $vertex)
    {
        $max = 0;
        foreach ($this->getVerticesChildren($vertex) as $vertex) {
            $height = $this->getHeightVertex($vertex) + 1;
            if ($height > $max) {
                $max = $height;
            }
        }
        return $max;
    }

    /**
     * get all vertices that are in the subtree of the given $vertex (which IS included)
     *
     * root vertex will return the whole tree, leaf vertices will only return themselves
     *
     * @param Vertex $vertex
     * @throws UnexpectedValueException if there are invalid edges (check isTree()!)
     * @return Vertices
     * @uses self::getVerticesSubtreeRecursive()
     * @uses self::getVerticesSubtree()
     */
    public function getVerticesSubtree(Vertex $vertex)
    {
        $vertices = array();
        $this->getVerticesSubtreeRecursive($vertex, $vertices);

        return new Vertices($vertices);
    }

    /**
     * helper method to get recursively get subtree for given $vertex
     *
     * @param Vertex   $vertex
     * @param Vertex[] $vertices
     * @throws UnexpectedValueException if multiple links were found to the given edge (check isTree()!)
     * @uses self::getVerticesChildren()
     * @uses self::getVerticesSubtreeRecursive() to recurse into subtrees
     */
    private function getVerticesSubtreeRecursive(Vertex $vertex, array &$vertices)
    {
        $vid = $vertex->getId();
        if (isset($vertices[$vid])) {
            throw new UnexpectedValueException('Multiple links found');
        }
        $vertices[$vid] = $vertex;

        foreach ($this->getVerticesChildren($vertex) as $vertexChild) {
            $this->getVerticesSubtreeRecursive($vertexChild, $vertices);
        }
    }

    /**
     * get all vertices below the given $vertex (which is NOT included)
     *
     * think of this as the recursive version of getVerticesChildren()
     *
     * @param Vertex $vertex
     * @return Vertices
     * @throws UnexpectedValueException if there are invalid edges (check isTree()!)
     * @uses self::getVerticesSubtree()
     */
    public function getVerticesDescendant(Vertex $vertex)
    {
        $vertices = $this->getVerticesSubtree($vertex)->getMap();
        unset($vertices[$vertex->getId()]);

        return new Vertices($vertices);
    }
}


================================================
FILE: src/Tree/InTree.php
================================================
<?php

namespace Graphp\Algorithms\Tree;

use Graphp\Algorithms\Tree\BaseDirected as DirectedTree;
use Graphp\Graph\Exception\UnexpectedValueException;
use Graphp\Graph\Vertex;

/**
 * Alternative InTree implementation where Edges "point towards" root Vertex
 *
 *         ROOT
 *         ^  ^
 *        /    \
 *       A      B
 *              ^
 *               \
 *                C
 *
 * @link http://en.wikipedia.org/wiki/Spaghetti_stack
 * @see DirectedTree for more information on directed, rooted trees
 */
class InTree extends DirectedTree
{
    public function getVerticesChildren(Vertex $vertex)
    {
        $vertices = $vertex->getVerticesEdgeFrom();
        if ($vertices->hasDuplicates()) {
            throw new UnexpectedValueException();
        }

        return $vertices;
    }

    protected function getVerticesParent(Vertex $vertex)
    {
        $vertices = $vertex->getVerticesEdgeTo();
        if ($vertices->hasDuplicates()) {
            throw new UnexpectedValueException();
        }

        return $vertices;
    }
}


================================================
FILE: src/Tree/OutTree.php
================================================
<?php

namespace Graphp\Algorithms\Tree;

use Graphp\Algorithms\Tree\BaseDirected as DirectedTree;
use Graphp\Graph\Exception\UnexpectedValueException;
use Graphp\Graph\Vertex;

/**
 * Usual OutTree implementation where Edges "point away" from root Vertex
 *
 *          ROOT
 *          /  \
 *    A <--/    \--> B
 *                   \
 *                    \--> C
 *
 * also known as arborescence
 *
 * @link http://en.wikipedia.org/wiki/Arborescence_%28graph_theory%29
 * @see DirectedTree for more information on directed, rooted trees
 */
class OutTree extends DirectedTree
{
    public function getVerticesChildren(Vertex $vertex)
    {
        $vertices = $vertex->getVerticesEdgeTo();
        if ($vertices->hasDuplicates()) {
            throw new UnexpectedValueException();
        }

        return $vertices;
    }

    protected function getVerticesParent(Vertex $vertex)
    {
        $vertices = $vertex->getVerticesEdgeFrom();
        if ($vertices->hasDuplicates()) {
            throw new UnexpectedValueException();
        }

        return $vertices;
    }
}


================================================
FILE: src/Tree/Undirected.php
================================================
<?php

namespace Graphp\Algorithms\Tree;

use Graphp\Algorithms\Tree\Base as Tree;
use Graphp\Graph\EdgeUndirected;
use Graphp\Graph\Exception\UnexpectedValueException;
use Graphp\Graph\Set\Vertices;
use Graphp\Graph\Vertex;

/**
 * Undirected tree implementation
 *
 * An undirected tree is a connected Graph (single component) with no cycles.
 * Every undirected Tree is an undirected Graph, but not every undirected Graph
 * is an undirected Tree.
 *
 *    A
 *   / \
 *  B   C
 *     / \
 *    D   E
 *
 * Undirected trees do not have special root Vertices (like the above picture
 * might suggest). The above tree Graph can also be equivalently be pictured
 * like this:
 *
 *      C
 *     /|\
 *    / | \
 *   A  D  E
 *  /
 * B
 *
 * If you're looking for a tree with a designated root Vertex, use directed,
 * rooted trees (BaseDirected).
 *
 * @link http://en.wikipedia.org/wiki/Tree_%28graph_theory%29
 * @see BaseDirected if you're looking for directed, rooted trees
 */
class Undirected extends Tree
{
    /**
     * checks if this is a tree
     *
     * @return bool
     * @uses Vertices::isEmpty() to skip null Graphs (a Graph with no Vertices is *NOT* a valid tree)
     * @uses Vertices::getVertexFirst() to get get get random "root" Vertex to start search from
     * @uses self::getVerticesSubtreeRecursive() to count number of vertices connected to root
     */
    public function isTree()
    {
        if ($this->graph->getVertices()->isEmpty()) {
            return false;
        }

        // every vertex can represent a root vertex, so just pick one
        $root = $this->graph->getVertices()->getVertexFirst();

        $vertices = array();
        try {
            $this->getVerticesSubtreeRecursive($root, $vertices, null);
        } catch (UnexpectedValueException $e) {
            return false;
        }

        return (\count($vertices) === \count($this->graph->getVertices()));
    }

    /**
     * checks if the given $vertex is a leaf (outermost vertex with exactly one edge)
     *
     * @param Vertex $vertex
     * @return bool
     * @uses Degree::getDegreeVertex()
     */
    public function isVertexLeaf(Vertex $vertex)
    {
        return ($this->degree->getDegreeVertex($vertex) === 1);
    }

    /**
     * checks if the given $vertex is an internal vertex (inner vertex with at least 2 edges)
     *
     * @param Vertex $vertex
     * @return bool
     * @uses Degree::getDegreeVertex()
     */
    public function isVertexInternal(Vertex $vertex)
    {
        return ($this->degree->getDegreeVertex($vertex) >= 2);
    }

    /**
     * get subtree for given Vertex and ignore path to "parent" ignoreVertex
     *
     * @param Vertex      $vertex
     * @param Vertex[]    $vertices
     * @param Vertex|null $ignore
     * @throws UnexpectedValueException for cycles or directed edges (check isTree()!)
     * @uses self::getVerticesNeighbor()
     * @uses self::getVerticesSubtreeRecursive() to recurse into sub-subtrees
     */
    private function getVerticesSubtreeRecursive(Vertex $vertex, array &$vertices, Vertex $ignore = null)
    {
        if (isset($vertices[$vertex->getId()])) {
            // vertex already visited => must be a cycle
            throw new UnexpectedValueException('Vertex already visited');
        }
        $vertices[$vertex->getId()] = $vertex;

        foreach ($this->getVerticesNeighbor($vertex) as $vertexNeighboor) {
            if ($vertexNeighboor === $ignore) {
                // ignore source vertex only once
                $ignore = null;
                continue;
            }
            $this->getVerticesSubtreeRecursive($vertexNeighboor, $vertices, $vertex);
        }
    }

    /**
     * get neighbor vertices for given start vertex
     *
     * @param Vertex $vertex
     * @return Vertices (might include possible duplicates)
     * @throws UnexpectedValueException for directed edges
     * @uses Vertex::getEdges()
     * @uses Edge::getVertexToFrom()
     * @see Vertex::getVerticesEdge()
     */
    private function getVerticesNeighbor(Vertex $vertex)
    {
        $vertices = array();
        foreach ($vertex->getEdges() as $edge) {
            if (!$edge instanceof EdgeUndirected) {
                throw new UnexpectedValueException('Directed edge encountered');
            }
            $vertices[] = $edge->getVertexToFrom($vertex);
        }
        return new Vertices($vertices);
    }
}


================================================
FILE: src/Weight.php
================================================
<?php

namespace Graphp\Algorithms;

use Graphp\Graph\Graph;

/**
 * Basic algorithms for working with the (total) weight of a Graph/Walk
 *
 * A weighted graph associates a label (weight) with every edge in the graph.
 * Sometimes the word cost is used instead of weight. The term network is a
 * synonym for a weighted graph.
 *
 * @link http://en.wikipedia.org/wiki/Glossary_of_graph_theory#Weighted_graphs_and_networks
 */
class Weight extends BaseDual
{
    /**
     * checks whether this graph has any weighted edges
     *
     * edges usually have no weight attached. a weight explicitly set to (int) 0
     * will be considered as 'weighted'.
     *
     * @return bool
     * @uses Edge::getWeight()
     */
    public function isWeighted()
    {
        foreach ($this->set->getEdges() as $edge) {
            if ($edge->getWeight() !== NULL) {
                return true;
            }
        }

        return false;
    }

    /**
     * get total weight of graph (sum of weight of all edges)
     *
     * edges with no weight assigned will evaluate to weight (int) 0. thus an
     * unweighted graph (see isWeighted()) will return total weight of (int) 0.
     *
     * returned weight can also be negative or (int) 0 if edges have been
     * assigned a negative weight or a weight of (int) 0.
     *
     * @return float total weight
     * @see self::isWeighted()
     * @uses Edge::getWeight()
     */
    public function getWeight()
    {
        $weight = 0;
        foreach ($this->set->getEdges() as $edge) {
            $w = $edge->getWeight();
            if ($w !== NULL) {
                $weight += $w;
            }
        }

        return $weight;
    }

    /**
     * get minimum weight assigned to all edges
     *
     * minimum weight is often needed because some algorithms do not support
     * negative weights or edges with zero weight.
     *
     * edges with NO (null) weight will NOT be considered for the minimum weight.
     *
     * @return float|NULL minimum edge weight or NULL if graph is not weighted or empty
     * @uses Edge::getWeight()
     */
    public function getWeightMin()
    {
        $min = NULL;
        foreach ($this->set->getEdges() as $edge) {
            $weight = $edge->getWeight();
            if ($weight !== null && ($min === NULL || $weight < $min)) {
                $min = $weight;
            }
        }

        return $min;
    }

    /**
     * get total weight of current flow (sum of all edges flow(e) * weight(e))
     *
     * @return float
     * @see Graph::getWeight() to just get the sum of all edges' weights
     * @uses Edge::getFlow()
     * @uses Edge::getWeight()
     */
    public function getWeightFlow()
    {
        $sum = 0;
        foreach ($this->set->getEdges() as $edge) {
            $sum += $edge->getFlow() * $edge->getWeight();
        }

        return $sum;
    }
}


================================================
FILE: tests/BipartitTest.php
================================================
<?php

namespace Graphp\Tests\Algorithms;

use Graphp\Algorithms\Bipartit as AlgorithmBipartit;
use Graphp\Graph\Graph;

class BipartitTest extends TestCase
{
    public function testGraphEmpty()
    {
        $graph = new Graph();

        $alg = new AlgorithmBipartit($graph);

        $this->assertTrue($alg->isBipartit());
        $this->assertEquals(array(), $alg->getColors());
        $this->assertEquals(array(0 => array(), 1 => array()), $alg->getColorVertices());
    }

    public function testGraphPairIsBipartit()
    {
        // 1 -> 2
        $graph = new Graph();
        $v1 = $graph->createVertex(1);
        $v2 = $graph->createVertex(2);
        $graph->createEdgeDirected($v1, $v2);

        $alg = new AlgorithmBipartit($graph);

        $this->assertTrue($alg->isBipartit());
        $this->assertEquals(array(1 => 0, 2 => 1), $alg->getColors());
        $this->assertEquals(array(0 => array(1 => $v1), 1 => array(2 => $v2)), $alg->getColorVertices());

        return $alg;
    }

    /**
     *
     * @param AlgorithmBipartit $alg
     * @depends testGraphPairIsBipartit
     */
    public function testGraphPairBipartitGroups(AlgorithmBipartit $alg)
    {
        // graph does not have any groups assigned, so its groups are not bipartit
        $this->assertFalse($alg->isBipartitGroups());

        // create a cloned graph with groups assigned according to bipartition
        $graph = $alg->createGraphGroups();

        $this->assertInstanceOf('Graphp\Graph\Graph', $graph);

        $alg2 = new AlgorithmBipartit($graph);
        $this->assertTrue($alg2->isBipartitGroups());
    }

    public function testGraphTriangleCycleIsNotBipartit()
    {
        // 1 -> 2 --> 3 --> 1
        $graph = new Graph();
        $v1 = $graph->createVertex(1);
        $v2 = $graph->createVertex(2);
        $v3 = $graph->createVertex(3);
        $graph->createEdgeDirected($v1, $v2);
        $graph->createEdgeDirected($v2, $v3);
        $graph->createEdgeDirected($v3, $v1);

        $alg = new AlgorithmBipartit($graph);

        $this->assertFalse($alg->isBipartit());

        return $alg;
    }

    /**
     *
     * @param AlgorithmBipartit $alg
     * @depends testGraphTriangleCycleIsNotBipartit
     */
    public function testGraphTriangleCycleColorsInvalid(AlgorithmBipartit $alg)
    {
        $this->setExpectedException('UnexpectedValueException');
        $alg->getColors();
    }

    /**
     *
     * @param AlgorithmBipartit $alg
     * @depends testGraphTriangleCycleIsNotBipartit
     */
    public function testGraphTriangleCycleColorVerticesInvalid(AlgorithmBipartit $alg)
    {
        $this->setExpectedException('UnexpectedValueException');
        $alg->getColorVertices();
    }
}


================================================
FILE: tests/CompleteTest.php
================================================
<?php

namespace Graphp\Tests\Algorithms;

use Graphp\Algorithms\Complete as AlgorithmComplete;
use Graphp\Graph\Graph;

class CompleteTest extends TestCase
{
    public function testGraphEmptyK0()
    {
        $graph = new Graph();

        $alg = new AlgorithmComplete($graph);

        $this->assertTrue($alg->isComplete());
    }

    public function testGraphSingleTrivialK1()
    {
        $graph = new Graph();
        $graph->createVertex(1);

        $alg = new AlgorithmComplete($graph);

        $this->assertTrue($alg->isComplete());
    }

    public function testGraphSimplePairK2()
    {
        // 1 -- 2
        $graph = new Graph();
        $graph->createEdgeUndirected($graph->createVertex(1), $graph->createVertex(2));

        $alg = new AlgorithmComplete($graph);

        $this->assertTrue($alg->isComplete());
    }

    public function testGraphSingleDirectedIsNotComplete()
    {
        // 1 -> 2
        $graph = new Graph();
        $graph->createEdgeDirected($graph->createVertex(1), $graph->createVertex(2));

        $alg = new AlgorithmComplete($graph);

        $this->assertFalse($alg->isComplete());
    }

    public function testAdditionalEdgesToNotAffectCompleteness()
    {
        // 1 -> 2
        // 1 -- 2
        // 2 -> 1
        // 1 -> 1
        $graph = new Graph();
        $graph->createEdgeDirected($graph->createVertex(1), $graph->createVertex(2));
        $graph->createEdgeUndirected($graph->getVertex(1), $graph->getVertex(2));
        $graph->createEdgeDirected($graph->getVertex(2), $graph->getVertex(1));
        $graph->createEdgeDirected($graph->getVertex(1), $graph->getVertex(1));

        $alg = new AlgorithmComplete($graph);

        $this->assertTrue($alg->isComplete());
    }
}


================================================
FILE: tests/ConnectedComponentsTest.php
================================================
<?php

namespace Graphp\Tests\Algorithms;

use Graphp\Algorithms\ConnectedComponents as AlgorithmConnected;
use Graphp\Graph\Graph;

class ConnectedComponentsTest extends TestCase
{
    public function testNullGraph()
    {
        $graph = new Graph();

        $alg = new AlgorithmConnected($graph);

        $this->assertEquals(0, $alg->getNumberOfComponents());
        $this->assertFalse($alg->isSingle());
        $this->assertCount(0, $alg->createGraphsComponents());
    }

    public function testGraphSingleTrivial()
    {
        $graph = new Graph();
        $graph->createVertex(1);

        $alg = new AlgorithmConnected($graph);

        $this->assertEquals(1, $alg->getNumberOfComponents());
        $this->assertTrue($alg->isSingle());

        $graphs = $alg->createGraphsComponents();

        $this->assertCount(1, $graphs);
        $this->assertGraphEquals($graph, \reset($graphs));
    }

    public function testGraphEdgeDirections()
    {
        // 1 -- 2 -> 3 <- 4
        $graph = new Graph();
        $graph->createEdgeUndirected($graph->createVertex(1), $graph->createVertex(2));
        $graph->createEdgeDirected($graph->getVertex(2), $graph->createVertex(3));
        $graph->createEdgeDirected($graph->createVertex(4), $graph->getVertex(3));

        $alg = new AlgorithmConnected($graph);

        $this->assertEquals(1, $alg->getNumberOfComponents());
        $this->assertTrue($alg->isSingle());

        $graphs = $alg->createGraphsComponents();

        $this->assertCount(1, $graphs);
        $this->assertGraphEquals($graph, \reset($graphs));
        $this->assertGraphEquals($graph, $alg->createGraphComponentVertex($graph->getVertex(1)));
    }

    public function testComponents()
    {
        // 1 -- 2, 3 -> 4, 5
        $graph = new Graph();
        $v1 = $graph->createVertex(1);
        $v2 = $graph->createVertex(2);
        $v3 = $graph->createVertex(3);
        $v4 = $graph->createVertex(4);
        $v5 = $graph->createVertex(5);
        $graph->createEdgeUndirected($v1, $v2);
        $graph->createEdgeDirected($v3, $v4);

        $alg = new AlgorithmConnected($graph);

        $this->assertEquals(3, $alg->getNumberOfComponents());
        $this->assertFalse($alg->isSingle());

        $graphs = $alg->createGraphsComponents();
        $this->assertCount(3, $graphs);

        $ge = new Graph();
        $ge->createEdgeUndirected($ge->createVertex(1), $ge->createVertex(2));
        $this->assertGraphEquals($ge, $alg->createGraphComponentVertex($v2));

        $ge = new Graph();
        $ge->createVertex(5);
        $this->assertEquals($ge, $alg->createGraphComponentVertex($v5));
    }

    public function testInvalidVertexPassedToAlgorithm()
    {
        $graph = new Graph();

        $graph2 = new Graph();
        $v2 = $graph2->createVertex(12);

        $alg = new AlgorithmConnected($graph);

        $this->setExpectedException('InvalidArgumentException');
        $alg->createGraphComponentVertex($v2);
    }
}


================================================
FILE: tests/DegreeTest.php
================================================
<?php

namespace Graphp\Tests\Algorithms;

use Graphp\Algorithms\Degree as AlgorithmDegree;
use Graphp\Graph\Exception\UnderflowException;
use Graphp\Graph\Exception\UnexpectedValueException;
use Graphp\Graph\Graph;

class DegreeTest extends TestCase
{
    public function testGraphEmpty()
    {
        $graph = new Graph();

        $alg = new AlgorithmDegree($graph);

        try {
            $alg->getDegree();
            $this->fail();
        } catch (UnderflowException $e) { }

        try {
            $alg->getDegreeMin();
            $this->fail();
        } catch (UnderflowException $e) { }

        try {
            $alg->getDegreeMax();
            $this->fail();
        } catch (UnderflowException $e) { }

        $this->assertTrue($alg->isRegular());
        $this->assertTrue($alg->isBalanced());
    }

    public function testGraphIsolated()
    {
        $graph = new Graph();
        $graph->createVertex(1);
        $graph->createVertex(2);

        $alg = new AlgorithmDegree($graph);

        $this->assertEquals(0, $alg->getDegree());
        $this->assertEquals(0, $alg->getDegreeMin());
        $this->assertEquals(0, $alg->getDegreeMax());
        $this->assertTrue($alg->isRegular());
        $this->assertTrue($alg->isBalanced());
    }

    public function testGraphIrregular()
    {
        // 1 -> 2 -> 3
        $graph = new Graph();
        $v1 = $graph->createVertex(1);
        $v2 = $graph->createVertex(2);
        $v3 = $graph->createVertex(3);
        $graph->createEdgeDirected($v1, $v2);
        $graph->createEdgeDirected($v2, $v3);

        $alg = new AlgorithmDegree($graph);

        try {
            $this->assertEquals(0, $alg->getDegree());
            $this->fail();
        } catch (UnexpectedValueException $e) { }

        $this->assertEquals(1, $alg->getDegreeMin());
        $this->assertEquals(2, $alg->getDegreeMax());
        $this->assertFalse($alg->isRegular());
        $this->assertFalse($alg->isBalanced());


        $this->assertEquals(0, $alg->getDegreeInVertex($v1));
        $this->assertEquals(1, $alg->getDegreeOutVertex($v1));
        $this->assertEquals(1, $alg->getDegreeVertex($v1));
        $this->assertFalse($alg->isVertexIsolated($v1));
        $this->assertFalse($alg->isVertexSink($v1));
        $this->assertTrue($alg->isVertexSource($v1));

        $this->assertEquals(1, $alg->getDegreeInVertex($v2));
        $this->assertEquals(1, $alg->getDegreeOutVertex($v2));
        $this->assertEquals(2, $alg->getDegreeVertex($v2));
        $this->assertFalse($alg->isVertexIsolated($v2));
        $this->assertFalse($alg->isVertexSink($v2));
        $this->assertFalse($alg->isVertexSource($v2));

        $this->assertEquals(1, $alg->getDegreeInVertex($v3));
        $this->assertEquals(0, $alg->getDegreeOutVertex($v3));
        $this->assertEquals(1, $alg->getDegreeVertex($v3));
        $this->assertFalse($alg->isVertexIsolated($v3));
        $this->assertTrue($alg->isVertexSink($v3));
        $this->assertFalse($alg->isVertexSource($v3));
    }
}


================================================
FILE: tests/DetectNegativeCycleTest.php
================================================
<?php

namespace Graphp\Tests\Algorithms;

use Graphp\Algorithms\DetectNegativeCycle;
use Graphp\Graph\Graph;

class DetectNegativeCycleTest extends TestCase
{
    public function testNullGraph()
    {
        $graph = new Graph();

        $alg = new DetectNegativeCycle($graph);

        $this->assertFalse($alg->hasCycleNegative());

        return $alg;
    }

    /**
     *
     * @param DetectNegativeCycle $alg
     * @depends testNullGraph
     */
    public function testNullGraphHasNoCycle(DetectNegativeCycle $alg)
    {
        $this->setExpectedException('UnderflowException');
        $alg->getCycleNegative();
    }

    /**
     *
     * @param DetectNegativeCycle $alg
     * @depends testNullGraph
     */
    public function testNullGraphHasNoCycleGraph(DetectNegativeCycle $alg)
    {
        $this->setExpectedException('UnderflowException');
        $alg->createGraph();
    }

    public function testNegativeLoop()
    {
        // 1 --[-1]--> 1
        $graph = new Graph();
        $v1 = $graph->createVertex(1);
        $e1 = $graph->createEdgeDirected($v1, $v1)->setWeight(-1);

        $alg = new DetectNegativeCycle($graph);

        $this->assertTrue($alg->hasCycleNegative());

        $cycle = $alg->getCycleNegative();

        $this->assertCount(1, $cycle->getEdges());
        $this->assertCount(2, $cycle->getVertices());
        $this->assertEquals($e1, $cycle->getEdges()->getEdgeFirst());
        $this->assertEquals($v1, $cycle->getVertices()->getVertexFirst());
    }

    public function testNegativeCycle()
    {
        // 1 --[-1]--> 2
        // ^           |
        // \---[-2]----/
        $graph = new Graph();
        $v1 = $graph->createVertex(1);
        $v2 = $graph->createVertex(2);
        $graph->createEdgeDirected($v1, $v2)->setWeight(-1);
        $graph->createEdgeDirected($v2, $v1)->setWeight(-2);

        $alg = new DetectNegativeCycle($graph);

        $this->assertTrue($alg->hasCycleNegative());

        $cycle = $alg->getCycleNegative();

        $this->assertCount(2, $cycle->getEdges());
        $this->assertCount(3, $cycle->getVertices());
    }

    public function testNegativeUndirectedIsNegativeCycle()
    {
        // 1 --[-1]-- 2
        $graph = new Graph();
        $v1 = $graph->createVertex(1);
        $v2 = $graph->createVertex(2);
        $graph->createEdgeUndirected($v1, $v2)->setWeight(-1);

        $alg = new DetectNegativeCycle($graph);

        $this->assertTrue($alg->hasCycleNegative());

        $cycle = $alg->getCycleNegative();

        $this->assertCount(2, $cycle->getEdges());
        $this->assertCount(3, $cycle->getVertices());
    }

    public function testNegativeCycleSubgraph()
    {
        // 1 --[1]--> 2 --[1]--> 3 --[1]--> 4
        //                       ^          |
        //                       \---[-2]---/
        $graph = new Graph();
        $v1 = $graph->createVertex(1);
        $v2 = $graph->createVertex(2);
        $v3 = $graph->createVertex(3);
        $v4 = $graph->createVertex(4);
        $graph->createEdgeDirected($v1, $v2)->setWeight(1);
        $graph->createEdgeDirected($v2, $v3)->setWeight(1);
        $graph->createEdgeDirected($v3, $v4)->setWeight(1);
        $graph->createEdgeDirected($v4, $v3)->setWeight(-2);

        $alg = new DetectNegativeCycle($graph);

        $this->assertTrue($alg->hasCycleNegative());

        $cycle = $alg->getCycleNegative();

        $this->assertCount(2, $cycle->getEdges());
        $this->assertCount(3, $cycle->getVertices());
        $this->assertTrue($cycle->getVertices()->hasVertexId(3));
        $this->assertTrue($cycle->getVertices()->hasVertexId(4));
    }

    public function testNegativeComponents()
    {
        // 1 -- 2     3 --[-1]--> 4
        //            ^           |
        //            \---[-2]----/
        $graph = new Graph();
        $v1 = $graph->createVertex(1);
        $v2 = $graph->createVertex(2);
        $v3 = $graph->createVertex(3);
        $v4 = $graph->createVertex(4);
        $graph->createEdgeUndirected($v1, $v2);
        $graph->createEdgeDirected($v3, $v4)->setWeight(-1);
        $graph->createEdgeDirected($v4, $v3)->setWeight(-2);

        $alg = new DetectNegativeCycle($graph);

        $this->assertTrue($alg->hasCycleNegative());

        $cycle = $alg->getCycleNegative();

        $this->assertCount(2, $cycle->getEdges());
        $this->assertCount(3, $cycle->getVertices());
        $this->assertTrue($cycle->getVertices()->hasVertexId(3));
        $this->assertTrue($cycle->getVertices()->hasVertexId(4));
    }
}


================================================
FILE: tests/DirectedTest.php
================================================
<?php

namespace Graphp\Tests\Algorithms;

use Graphp\Algorithms\Directed as AlgorithmDirected;
use Graphp\Graph\Graph;

class DirectedTest extends TestCase
{
    public function testGraphEmpty()
    {
        $graph = new Graph();

        $alg = new AlgorithmDirected($graph);

        $this->assertFalse($alg->hasDirected());
        $this->assertFalse($alg->hasUndirected());
        $this->assertFalse($alg->isMixed());
    }

    public function testGraphUndirected()
    {
        // 1 -- 2
        $graph = new Graph();
        $graph->createEdgeUndirected($graph->createVertex(1), $graph->createVertex(2));

        $alg = new AlgorithmDirected($graph);

        $this->assertFalse($alg->hasDirected());
        $this->assertTrue($alg->hasUndirected());
        $this->assertFalse($alg->isMixed());
    }

    public function testGraphDirected()
    {
        // 1 -> 2
        $graph = new Graph();
        $graph->createEdgeDirected($graph->createVertex(1), $graph->createVertex(2));

        $alg = new AlgorithmDirected($graph);

        $this->assertTrue($alg->hasDirected());
        $this->assertFalse($alg->hasUndirected());
        $this->assertFalse($alg->isMixed());
    }

    public function testGraphMixed()
    {
        // 1 -- 2 -> 3
        $graph = new Graph();
        $graph->createEdgeUndirected($graph->createVertex(1), $graph->createVertex(2));
        $graph->createEdgeDirected($graph->getVertex(2), $graph->createVertex(3));

        $alg = new AlgorithmDirected($graph);

        $this->assertTrue($alg->hasDirected());
        $this->assertTrue($alg->hasUndirected());
        $this->assertTrue($alg->isMixed());
    }
}


================================================
FILE: tests/EulerianTest.php
================================================
<?php

namespace Graphp\Tests\Algorithms;

use Graphp\Algorithms\Eulerian as AlgorithmEulerian;
use Graphp\Graph\Graph;

class EulerianTest extends TestCase
{
    public function testGraphEmpty()
    {
        $graph = new Graph();

        $alg = new AlgorithmEulerian($graph);

        $this->assertFalse($alg->hasCycle());
    }

    public function testGraphPairHasNoCycle()
    {
        // 1 -- 2
        $graph = new Graph();
        $v1 = $graph->createVertex(1);
        $v2 = $graph->createVertex(2);
        $graph->createEdgeUndirected($v1, $v2);

        $alg = new AlgorithmEulerian($graph);

        $this->assertFalse($alg->hasCycle());
    }

    public function testGraphTriangleCycleIsNotBipartit()
    {
        // 1 -- 2 -- 3 -- 1
        $graph = new Graph();
        $v1 = $graph->createVertex(1);
        $v2 = $graph->createVertex(2);
        $v3 = $graph->createVertex(3);
        $graph->createEdgeUndirected($v1, $v2);
        $graph->createEdgeUndirected($v2, $v3);
        $graph->createEdgeUndirected($v3, $v1);

        $alg = new AlgorithmEulerian($graph);

        $this->assertTrue($alg->hasCycle());
    }
}


================================================
FILE: tests/FlowTest.php
================================================
<?php

namespace Graphp\Tests\Algorithms;

use Graphp\Algorithms\Flow as AlgorithmFlow;
use Graphp\Graph\Graph;

class FlowTest extends TestCase
{
    public function testGraphEmpty()
    {
        $graph = new Graph();

        $alg = new AlgorithmFlow($graph);

        $this->assertFalse($alg->hasFlow());
        $this->assertEquals(0, $alg->getBalance());
        $this->assertTrue($alg->isBalancedFlow());

        return $graph;
    }

    public function testEdgeWithZeroFlowIsConsideredFlow()
    {
        // 1 -> 2
        $graph = new Graph();
        $graph->createEdgeDirected($graph->createVertex(1), $graph->createVertex(2))->setFlow(0);

        $alg = new AlgorithmFlow($graph);

        $this->assertTrue($alg->hasFlow());
        $this->assertEquals(0, $alg->getFlowVertex($graph->getVertex(1)));
        $this->assertEquals(0, $alg->getFlowVertex($graph->getVertex(2)));
    }

    /**
     *
     * @param Graph $graph
     * @depends testGraphEmpty
     */
    public function testGraphSimple(Graph $graph)
    {
        // 1 -> 2
        $graph->createEdgeDirected($graph->createVertex(1), $graph->createVertex(2));

        $alg = new AlgorithmFlow($graph);

        $this->assertFalse($alg->hasFlow());
        $this->assertEquals(0, $alg->getFlowVertex($graph->getVertex(1)));
        $this->assertEquals(0, $alg->getFlowVertex($graph->getVertex(2)));

        return $graph;
    }

    /**
     *
     * @param Graph $graph
     * @depends testGraphSimple
     */
    public function testGraphWithUnweightedEdges(Graph $graph)
    {
        // additional flow edge: 2 -> 3
        $graph->createEdgeDirected($graph->getVertex(2), $graph->createVertex(3))->setFlow(10);

        $alg = new AlgorithmFlow($graph);

        $this->assertTrue($alg->hasFlow());
        $this->assertEquals(10, $alg->getFlowVertex($graph->getVertex(2)));
        $this->assertEquals(-10, $alg->getFlowVertex($graph->getVertex(3)));
    }

    public function testGraphBalance()
    {
        // source(+100) -> sink(-10)
        $graph = new Graph();
        $graph->createVertex('source')->setBalance(100);
        $graph->createVertex('sink')->setBalance(-10);

        $alg = new AlgorithmFlow($graph);

        $this->assertEquals(90, $alg->getBalance());
        $this->assertFalse($alg->isBalancedFlow());
    }

    public function testVertexWithUndirectedEdgeHasInvalidFlow()
    {
        // 1 -- 2
        $graph = new Graph();
        $graph->createEdgeUndirected($graph->createVertex(1), $graph->createVertex(2))->setFlow(10);

        $alg = new AlgorithmFlow($graph);

        $this->setExpectedException('UnexpectedValueException');
        $alg->getFlowVertex($graph->getVertex(1));
    }
}


================================================
FILE: tests/GroupsTest.php
================================================
<?php

namespace Graphp\Tests\Algorithms;

use Graphp\Algorithms\Groups as AlgorithmGroups;
use Graphp\Graph\Graph;

class GroupsTest extends TestCase
{
    public function testGraphEmpty()
    {
        $graph = new Graph();

        $alg = new AlgorithmGroups($graph);

        $this->assertEquals(array(), $alg->getGroups());
        $this->assertEquals(0, $alg->getNumberOfGroups());

        $this->assertTrue($alg->getVerticesGroup(123)->isEmpty());

        $this->assertFalse($alg->isBipartit());
    }

    public function testGraphPairIsBipartit()
    {
        // 1 -> 2
        $graph = new Graph();
        $v1 = $graph->createVertex(1)->setGroup(1);
        $v2 = $graph->createVertex(2)->setGroup(2);
        $graph->createEdgeDirected($v1, $v2);

        $alg = new AlgorithmGroups($graph);

        $this->assertEquals(array(1, 2), $alg->getGroups());
        $this->assertEquals(2, $alg->getNumberOfGroups());

        $this->assertTrue($alg->getVerticesGroup(123)->isEmpty());
        $this->assertEquals(array(1 => $v1), $alg->getVerticesGroup(1)->getMap());

        $this->assertTrue($alg->isBipartit());
    }

    public function testGraphTriangleCycleIsNotBipartit()
    {
        // 1 -> 2 -> 3 -> 1
        $graph = new Graph();
        $v1 = $graph->createVertex(1)->setGroup(1);
        $v2 = $graph->createVertex(2)->setGroup(2);
        $v3 = $graph->createVertex(3)->setGroup(1);
        $graph->createEdgeDirected($v1, $v2);
        $graph->createEdgeDirected($v2, $v3);
        $graph->createEdgeDirected($v3, $v1);

        $alg = new AlgorithmGroups($graph);

        $this->assertEquals(array(1, 2), $alg->getGroups());
        $this->assertEquals(2, $alg->getNumberOfGroups());

        $this->assertTrue($alg->getVerticesGroup(123)->isEmpty());
        $this->assertEquals(array(1 => $v1, 3 => $v3), $alg->getVerticesGroup(1)->getMap());

        $this->assertFalse($alg->isBipartit());
    }
}


================================================
FILE: tests/LoopTest.php
================================================
<?php

namespace Graphp\Tests\Algorithms;

use Graphp\Algorithms\Loop as AlgorithmLoop;
use Graphp\Graph\Graph;

class LoopTest extends TestCase
{
    public function testGraphEmpty()
    {
        $graph = new Graph();

        $alg = new AlgorithmLoop($graph);

        $this->assertFalse($alg->hasLoop());
    }

    public function testGraphWithMixedCircuitIsNotConsideredLoop()
    {
        // 1 -> 2
        // 2 -- 1
        $graph = new Graph();
        $v1 = $graph->createVertex(1);
        $v2 = $graph->createVertex(2);
        $graph->createEdgeDirected($v1, $v2);
        $graph->createEdgeUndirected($v2, $v1);

        $alg = new AlgorithmLoop($graph);

        $this->assertFalse($alg->hasLoop());
        $this->assertFalse($alg->hasLoopVertex($v1));
        $this->assertFalse($alg->hasLoopVertex($v2));
    }

    public function testGraphUndirectedLoop()
    {
        // 1 -- 1
        $graph = new Graph();
        $graph->createEdgeUndirected($graph->createVertex(1), $v1 = $graph->getVertex(1));

        $alg = new AlgorithmLoop($graph);

        $this->assertTrue($alg->hasLoop());
        $this->assertTrue($alg->hasLoopVertex($v1));
    }

    public function testGraphDirectedLoop()
    {
        // 1 -> 1
        $graph = new Graph();
        $graph->createEdgeDirected($graph->createVertex(1), $v1 = $graph->getVertex(1));

        $alg = new AlgorithmLoop($graph);

        $this->assertTrue($alg->hasLoop());
        $this->assertTrue($alg->hasLoopVertex($v1));
    }
}


================================================
FILE: tests/MaxFlow/EdmondsKarpTest.php
================================================
<?php

namespace Graphp\Tests\Algorithms\MaxFlow;

use Graphp\Algorithms\MaxFlow\EdmondsKarp as AlgorithmMaxFlowEdmondsKarp;
use Graphp\Graph\Graph;
use Graphp\Tests\Algorithms\TestCase;

class EdmondsKarpTest extends TestCase
{
    public function testEdgeDirected()
    {
        // 0 -[0/10]-> 1
        $graph = new Graph();
        $v0 = $graph->createVertex(0);
        $v1 = $graph->createVertex(1);

        $graph->createEdgeDirected($v0, $v1)->setCapacity(10);

        // 0 -[10/10]-> 1
        $alg = new AlgorithmMaxFlowEdmondsKarp($v0, $v1);

        $this->assertEquals(10, $alg->getFlowMax());
    }

    public function testEdgesMultiplePaths()
    {
        // 0 -[0/5]---------> 1
        // |                  ^
        // |                  |
        // \-[0/7]-> 2 -[0/9]-/
        $graph = new Graph();
        $v0 = $graph->createVertex(0);
        $v1 = $graph->createVertex(1);
        $v2 = $graph->createVertex(2);

        $graph->createEdgeDirected($v0, $v1)->setCapacity(5);
        $graph->createEdgeDirected($v0, $v2)->setCapacity(7);
        $graph->createEdgeDirected($v2, $v1)->setCapacity(9);

        // 0 -[5/5]---------> 1
        // |                  ^
        // |                  |
        // \-[7/7]-> 2 -[7/9]-/
        $alg = new AlgorithmMaxFlowEdmondsKarp($v0, $v1);

        $this->assertEquals(12, $alg->getFlowMax());
    }

    public function testEdgesMultiplePathsTwo()
    {
        // 0 -[0/5]---------> 1-[0/10]-> 3
        // |                  ^          |
        // |                  |          |
        // \-[0/7]-> 2 -[0/9]-/          |
        //           ^                   |
        //           \---[0/2]-----------/
        $graph = new Graph();
        $v0 = $graph->createVertex(0);
        $v1 = $graph->createVertex(1);
        $v2 = $graph->createVertex(2);
        $v3 = $graph->createVertex(3);

        $graph->createEdgeDirected($v0, $v1)->setCapacity(5);
        $graph->createEdgeDirected($v0, $v2)->setCapacity(7);
        $graph->createEdgeDirected($v2, $v1)->setCapacity(9);
        $graph->createEdgeDirected($v1, $v3)->setCapacity(10);
        $graph->createEdgeDirected($v3, $v2)->setCapacity(2);

        $alg = new AlgorithmMaxFlowEdmondsKarp($v0, $v3);

        $this->assertEquals(10, $alg->getFlowMax());

        $alg = new AlgorithmMaxFlowEdmondsKarp($v0, $v2);

        $this->assertEquals(9, $alg->getFlowMax());
    }

    public function testEdgesMultiplePathsTree()
    {
        $graph = new Graph();
        $v0 = $graph->createVertex(0);
        $v1 = $graph->createVertex(1);
        $v2 = $graph->createVertex(2);
        $v3 = $graph->createVertex(3);

        $graph->createEdgeDirected($v0, $v1)->setCapacity(4);
        $graph->createEdgeDirected($v0, $v2)->setCapacity(2);
        $graph->createEdgeDirected($v1, $v2)->setCapacity(3);
        $graph->createEdgeDirected($v1, $v3)->setCapacity(1);
        $graph->createEdgeDirected($v2, $v3)->setCapacity(6);

        $alg = new AlgorithmMaxFlowEdmondsKarp($v0, $v3);

        $this->assertEquals(6, $alg->getFlowMax());
    }

//     public function testEdgesParallel(){
//         $graph = new Graph();
//         $v0 = $graph->createVertex(0);
//         $v1 = $graph->createVertex(1);

//         $graph->createEdgeDirected($v0, $v1)->setCapacity(3.4);
//         $graph->createEdgeDirected($v0, $v1)->setCapacity(6.6);

//         $alg = new AlgorithmMaxFlowEdmondsKarp($v0, $v1);

//         $this->assertEquals(10, $alg->getFlowMax());
//     }

    public function testEdgesUndirected()
    {
        // 0 -[0/7]- 1
        $graph = new Graph();
        $v0 = $graph->createVertex(0);
        $v1 = $graph->createVertex(1);

        $graph->createEdgeUndirected($v1, $v0)->setCapacity(7);

        // 0 -[7/7]- 1
        $alg = new AlgorithmMaxFlowEdmondsKarp($v0, $v1);

        $this->setExpectedException('UnexpectedValueException');
        $this->assertEquals(7, $alg->getFlowMax());
    }

    /**
     * run algorithm with bigger graph and check result against known result (will take several seconds)
     */
//     public function testKnownResultBig(){

//         $graph = $this->readGraph('G_1_2.txt');

//         $alg = new AlgorithmMaxFlowEdmondsKarp($graph->getVertex(0), $graph->getVertex(4));

//         $this->assertEquals(0.735802, $alg->getFlowMax());
//     }

    public function testInvalidFlowToOtherGraph()
    {
        $graph1 = new Graph();
        $vg1 = $graph1->createVertex(1);

        $graph2 = new Graph();
        $vg2 = $graph2->createVertex(2);

        $this->setExpectedException('InvalidArgumentException');
        new AlgorithmMaxFlowEdmondsKarp($vg1, $vg2);
    }

    public function testInvalidFlowToSelf()
    {
        $graph = new Graph();
        $v1 = $graph->createVertex(1);

        $this->setExpectedException('InvalidArgumentException');
        new AlgorithmMaxFlowEdmondsKarp($v1, $v1);
    }

}


================================================
FILE: tests/MaximumMatching/FlowTest.php
================================================
<?php

namespace Graphp\Tests\Algorithms\MaximumMatching;

use Graphp\Algorithms\MaximumMatching\Flow;
use Graphp\Graph\Graph;
use Graphp\Tests\Algorithms\TestCase;

class FlowTest extends TestCase
{
//     /**
//      * run algorithm with small graph and check result against known result
//      */
//     public function testKnownResult()
//     {
//         $loader = new EdgeListBipartit(PATH_DATA . 'Matching_100_100.txt');
//         $loader->setEnableDirectedEdges(false);
//         $graph = $loader->createGraph();

//         $alg = new Flow($graph);
//         $this->assertEquals(100, $alg->getNumberOfMatches());
//     }

    public function testSingleEdge()
    {
        $graph = new Graph();
        $edge = $graph->createEdgeUndirected($graph->createVertex(0)->setGroup(0), $graph->createVertex(1)->setGroup(1));

        $alg = new Flow($graph);
        // correct number of edges
        $this->assertEquals(1, $alg->getNumberOfMatches());
        // actual edge instance returned
        $this->assertEquals(array($edge), $alg->getEdges()->getVector());

        // check
        $flowgraph = $alg->createGraph();
        $this->assertInstanceOf('Graphp\Graph\Graph', $flowgraph);
    }

    /**
     * expect exception for directed edges
     */
    public function testInvalidDirected()
    {
        $graph = new Graph();
        $graph->createEdgeDirected($graph->createVertex(0)->setGroup(0), $graph->createVertex(1)->setGroup(1));

        $alg = new Flow($graph);

        $this->setExpectedException('UnexpectedValueException');
        $alg->getNumberOfMatches();
    }

    /**
     * expect exception for non-bipartit graphs
     */
    public function testInvalidBipartit()
    {
        $graph = new Graph();
        $graph->createEdgeUndirected($graph->createVertex(0)->setGroup(1), $graph->createVertex(1)->setGroup(1));

        $alg = new Flow($graph);

        $this->setExpectedException('UnexpectedValueException');
        $alg->getNumberOfMatches();
    }
}


================================================
FILE: tests/MinimumCostFlow/BaseMcfTest.php
================================================
<?php

namespace Graphp\Tests\Algorithms\MinimumCostFlow;

use Graphp\Algorithms\MinimumCostFlow\Base;
use Graphp\Graph\Graph;
use Graphp\Tests\Algorithms\TestCase;

abstract class BaseMcfTest extends TestCase
{
    /**
     *
     * @param Graph $graph
     * @return Base
     */
    abstract protected function createAlgorithm(Graph $graph);

    public function testNull()
    {
        $graph = new Graph();

        $alg = $this->createAlgorithm($graph);
        $this->assertEquals(0, $alg->getWeightFlow());
    }

    public function testSingleIntermediary()
    {
        $graph = new Graph();
        $graph->createVertex(1);

        $alg = $this->createAlgorithm($graph);
        $this->assertEquals(0, $alg->getWeightFlow());
    }

    public function testSimpleEdge()
    {
        // 1(+2) -[0/2/2]-> 2(-2)
        $graph = new Graph();
        $v1 = $graph->createVertex(1)->setBalance(2);
        $v2 = $graph->createVertex(2)->setBalance(-2);
        $graph->createEdgeDirected($v1, $v2)->setWeight(2)->setCapacity(2);

        $alg = $this->createAlgorithm($graph);
        $this->assertEquals(4, $alg->getWeightFlow()); // 2x2
    }

    public function testMultipleSinks()
    {
        // 1(+2) -[0/2/2]-> 2(-1)
        //       -[0/4/-5]-> 3(-1)
        $graph = new Graph();
        $v1 = $graph->createVertex(1)->setBalance(2);
        $v2 = $graph->createVertex(2)->setBalance(-1);
        $v3 = $graph->createVertex(3)->setBalance(-1);
        $graph->createEdgeDirected($v1, $v2)->setWeight(2)->setCapacity(2);
        $graph->createEdgeDirected($v1, $v3)->setWeight(-5)->setCapacity(4);

        $alg = $this->createAlgorithm($graph);
        $this->assertEquals(-3, $alg->getWeightFlow()); // 1*2 + 1*-5
    }

    public function testIntermediaryVertices()
    {
        // 1(+2) -[0/1/4]-> 2 -[0/6/-2]-> 4(-2)
        //       -[0/4/5]-> 3 -[0/6/8]->
        $graph = new Graph();
        $v1 = $graph->createVertex(1)->setBalance(2);
        $v2 = $graph->createVertex(2);
        $v3 = $graph->createVertex(3);
        $v4 = $graph->createVertex(4)->setBalance(-2);
        $graph->createEdgeDirected($v1, $v2)->setWeight(4)->setCapacity(1);
        $graph->createEdgeDirected($v2, $v4)->setWeight(-2)->setCapacity(6);
        $graph->createEdgeDirected($v1, $v3)->setWeight(5)->setCapacity(4);
        $graph->createEdgeDirected($v3, $v4)->setWeight(8)->setCapacity(6);

        $alg = $this->createAlgorithm($graph);
        $this->assertEquals(15, $alg->getWeightFlow()); // 1*4 + 1*-2 + 1*5 + 1*8
    }

    public function testEdgeCapacities()
    {
        // 1(+2) -[0/3/4]-> 2 -[0/4/5]-> 3 ->[0/6/-2]-> 4(-2)
        $graph = new Graph();
        $v1 = $graph->createVertex(1)->setBalance(2);
        $v2 = $graph->createVertex(2);
        $v3 = $graph->createVertex(3);
        $v4 = $graph->createVertex(4)->setBalance(-2);
        $graph->createEdgeDirected($v1, $v2)->setWeight(4)->setCapacity(3);
        $graph->createEdgeDirected($v2, $v3)->setWeight(5)->setCapacity(4);
        $graph->createEdgeDirected($v3, $v4)->setWeight(-2)->setCapacity(6);

        $alg = $this->createAlgorithm($graph);
        $this->assertEquals(14, $alg->getWeightFlow()); // 2*4 + 2*5 + 2*-2
    }

    public function testEdgeFlows()
    {
        // 1(+4) ---[3/4/2]---> 2 ---[3/3/3]---> 4(-4)
        //  |                   |                  ^
        //  |                [0/2/1]               |
        //  |                   ↓                  |
        //  \-------[1/2/2]---> 3 ---[1/5/1]-------/
        $graph = new Graph();
        $v1 = $graph->createVertex(1)->setBalance(4);
        $v2 = $graph->createVertex(2);
        $v3 = $graph->createVertex(3);
        $v4 = $graph->createVertex(4)->setBalance(-4);
        $graph->createEdgeDirected($v1, $v2)->setFlow(3)->setCapacity(4)->setWeight(2);
        $graph->createEdgeDirected($v2, $v4)->setFlow(3)->setCapacity(3)->setWeight(3);
        $graph->createEdgeDirected($v1, $v3)->setFlow(1)->setCapacity(2)->setWeight(2);
        $graph->createEdgeDirected($v3, $v4)->setFlow(1)->setCapacity(5)->setWeight(1);
        $graph->createEdgeDirected($v2, $v3)->setFlow(0)->setCapacity(2)->setWeight(1);

        $alg = $this->createAlgorithm($graph);
        $this->assertEquals(14, $alg->getWeightFlow()); // 4*1 + 2*2 + 2*1 + 2*2
    }

    public function testEdgeCapacityInsufficientFails()
    {
        // 1(+2) -[0/1]-> 2(-2)
        $graph = new Graph();
        $v1 = $graph->createVertex(1)->setBalance(2);
        $v2 = $graph->createVertex(2)->setBalance(-2);
        $graph->createEdgeDirected($v1, $v2)->setCapacity(1);

        $alg = $this->createAlgorithm($graph);

        $this->setExpectedException('UnexpectedValueException');
        $alg->getWeightFlow();
    }

    public function testEdgeCapacityUnsetFails()
    {
        // 1(+2) -> 2(-2)
        $graph = new Graph();
        $v1 = $graph->createVertex(1)->setBalance(2);
        $v2 = $graph->createVertex(2)->setBalance(-2);
        $graph->createEdgeDirected($v1, $v2);

        $alg = $this->createAlgorithm($graph);

        $this->setExpectedException('UnexpectedValueException');
        $alg->getWeightFlow();
    }

    public function testIsolatedVerticesFail()
    {
        // 1(+2), 2(-2)
        $graph = new Graph();
        $graph->createVertex(1)->setBalance(2);
        $graph->createVertex(2)->setBalance(-2);

        $alg = $this->createAlgorithm($graph);

        $this->setExpectedException('UnexpectedValueException');
        $alg->getWeightFlow();
    }

    public function testUnbalancedFails()
    {
        // 1(+2) -> 2(-3)
        $graph = new Graph();
        $v1 = $graph->createVertex(1)->setBalance(2);
        $v2 = $graph->createVertex(2)->setBalance(-3);
        $graph->createEdgeDirected($v1, $v2)->setCapacity(3);

        $alg = $this->createAlgorithm($graph);

        $this->setExpectedException('UnexpectedValueException');
        $alg->getWeightFlow();
    }

    public function testUndirectedFails()
    {
        // 1(+2) -- 2(-2)
        $graph = new Graph();
        $v1 = $graph->createVertex(1)->setBalance(2);
        $v2 = $graph->createVertex(2)->setBalance(-2);
        $graph->createEdgeUndirected($v1, $v2)->setCapacity(2);

        $alg = $this->createAlgorithm($graph);

        $this->setExpectedException('UnexpectedValueException');
        $alg->getWeightFlow();
    }

    public function testUndirectedNegativeCycleFails()
    {
        // 1(+2) -[0/2/-1]- 2(-2)
        $graph = new Graph();
        $v1 = $graph->createVertex(1)->setBalance(2);
        $v2 = $graph->createVertex(2)->setBalance(-2);
        $graph->createEdgeUndirected($v1, $v2)->setCapacity(2)->setWeight(-1);

        $alg = $this->createAlgorithm($graph);

        $this->setExpectedException('UnexpectedValueException');
        $alg->getWeightFlow();
    }
}


================================================
FILE: tests/MinimumCostFlow/CycleCancellingTest.php
================================================
<?php

namespace Graphp\Tests\Algorithms\MinimumCostFlow;

use Graphp\Algorithms\MinimumCostFlow\CycleCanceling;
use Graphp\Graph\Graph;

class CycleCancellingTest extends BaseMcfTest
{
    protected function createAlgorithm(Graph $graph)
    {
        return new CycleCanceling($graph);
    }
}


================================================
FILE: tests/MinimumCostFlow/SuccessiveShortestPathTest.php
================================================
<?php

namespace Graphp\Tests\Algorithms\MinimumCostFlow;

use Graphp\Algorithms\MinimumCostFlow\SuccessiveShortestPath;
use Graphp\Graph\Graph;

class SuccessiveShortestPathTest extends BaseMcfTest
{
    protected function createAlgorithm(Graph $graph)
    {
        return new SuccessiveShortestPath($graph);
    }
}


================================================
FILE: tests/MinimumSpanningTree/BaseMstTest.php
================================================
<?php

namespace Graphp\Tests\Algorithms\MinimumSpanningTree;

use Graphp\Algorithms\MinimumSpanningTree\Base as MstBase;
use Graphp\Graph\Graph;
use Graphp\Graph\Vertex;
use Graphp\Tests\Algorithms\TestCase;

abstract class BaseMstTest extends TestCase
{
    /**
     * @param Vertex $vertex
     * @return MstBase
     */
    abstract protected function createAlg(Vertex $vertex);

    public function testIsolatedVertex()
    {
        $graph = new Graph();
        $v1 = $graph->createVertex(1);

        $alg = $this->createAlg($v1);

        $this->assertCount(0, $alg->getEdges());
        $this->assertEquals(0, $alg->getWeight());

        $graphMst = $alg->createGraph();
        $this->assertGraphEquals($graph, $graphMst);
    }

    public function testSingleEdge()
    {
        // 1 --[3]-- 2
        $graph = new Graph();
        $v1 = $graph->createVertex(1);
        $v2 = $graph->createVertex(2);
        $graph->createEdgeUndirected($v1, $v2)->setWeight(3);

        $alg = $this->createAlg($v1);

        $this->assertCount(1, $alg->getEdges());
        $this->assertEquals(3, $alg->getWeight());
        $this->assertGraphEquals($graph, $alg->createGraph());
    }

    public function testSimpleGraph()
    {
        // 1 --[6]-- 2 --[9]-- 3 --[7]-- 4 --[8]-- 5
        $graph = new Graph();
        $v1 = $graph->createVertex(1);
        $v2 = $graph->createVertex(2);
        $v3 = $graph->createVertex(3);
        $v4 = $graph->createVertex(4);
        $v5 = $graph->createVertex(5);
        $graph->createEdgeUndirected($v1, $v2)->setWeight(6);
        $graph->createEdgeUndirected($v2, $v3)->setWeight(9);
        $graph->createEdgeUndirected($v3, $v4)->setWeight(7);
        $graph->createEdgeUndirected($v4, $v5)->setWeight(8);

        $alg = $this->createAlg($v1);

        $graphMst = $alg->createGraph();
        $this->assertGraphEquals($graph, $graphMst);
    }

    public function testFindingCheapestEdge()
    {
        //   /--[4]--\
        //  /         \
        // 1 ---[3]--- 2
        //  \         /
        //   \--[5]--/
        $graph = new Graph();
        $v1 = $graph->createVertex(1);
        $v2 = $graph->createVertex(2);
        $graph->createEdgeUndirected($v1, $v2)->setWeight(4);
        $graph->createEdgeUndirected($v1, $v2)->setWeight(3);
        $graph->createEdgeUndirected($v1, $v2)->setWeight(5);

        $alg = $this->createAlg($v1);
        $edges = $alg->getEdges();

        $this->assertCount(1, $edges);
        $this->assertEquals(3, $edges->getEdgeFirst()->getWeight());
        $this->assertEquals(3, $alg->getWeight());
    }

    public function testFindingCheapestTree()
    {
        // 1 --[4]-- 2 --[5]-- 3
        //  \                 /
        //   \-------[6]-----/
        $graph = new Graph();
        $v1 = $graph->createVertex(1);
        $v2 = $graph->createVertex(2);
        $v3 = $graph->createVertex(3);
        $graph->createEdgeUndirected($v1, $v2)->setWeight(4);
        $graph->createEdgeUndirected($v2, $v3)->setWeight(5);
        $graph->createEdgeUndirected($v3, $v1)->setWeight(6);

        // 1 --[4]-- 2 -- [5] -- 3
        $graphExpected = new Graph();
        $ve1 = $graphExpected->createVertex(1);
        $ve2 = $graphExpected->createVertex(2);
        $ve3 = $graphExpected->createVertex(3);
        $graphExpected->createEdgeUndirected($ve1, $ve2)->setWeight(4);
        $graphExpected->createEdgeUndirected($ve2, $ve3)->setWeight(5);

        $alg = $this->createAlg($v1);
        $this->assertCount(2, $alg->getEdges());
        $this->assertEquals(9, $alg->getWeight());
        $this->assertGraphEquals($graphExpected, $alg->createGraph());
    }

    public function testMixedGraphDirectionIsIgnored()
    {
        // 1 --[6]-> 2 --[7]-- 3 --[8]-- 4 <-[9]-- 5
        $graph = new Graph();
        $v1 = $graph->createVertex(1);
        $v2 = $graph->createVertex(2);
        $v3 = $graph->createVertex(3);
        $v4 = $graph->createVertex(4);
        $v5 = $graph->createVertex(5);
        $graph->createEdgeDirected($v1, $v2)->setWeight(6);
        $graph->createEdgeUndirected($v2, $v3)->setWeight(7);
        $graph->createEdgeUndirected($v4, $v3)->setWeight(8);
        $graph->createEdgeDirected($v5, $v4)->setWeight(9);

        $alg = $this->createAlg($v1);

        $this->assertCount(4, $alg->getEdges());
        $this->assertEquals(30, $alg->getWeight());
        $this->assertGraphEquals($graph, $alg->createGraph());
    }

    public function testMultipleComponentsFail()
    {
        // 1 --[1]-- 2, 3 --[1]-- 4
        $gra
Download .txt
gitextract_dqkgq7nx/

├── .gitattributes
├── .github/
│   └── workflows/
│       └── ci.yml
├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── README.md
├── composer.json
├── phpunit.xml.dist
├── phpunit.xml.legacy
├── src/
│   ├── Base.php
│   ├── BaseDual.php
│   ├── BaseGraph.php
│   ├── BaseVertex.php
│   ├── Bipartit.php
│   ├── Complete.php
│   ├── ConnectedComponents.php
│   ├── Degree.php
│   ├── DetectNegativeCycle.php
│   ├── Directed.php
│   ├── Eulerian.php
│   ├── Flow.php
│   ├── Groups.php
│   ├── Loop.php
│   ├── MaxFlow/
│   │   └── EdmondsKarp.php
│   ├── MaximumMatching/
│   │   ├── Base.php
│   │   └── Flow.php
│   ├── MinimumCostFlow/
│   │   ├── Base.php
│   │   ├── CycleCanceling.php
│   │   └── SuccessiveShortestPath.php
│   ├── MinimumSpanningTree/
│   │   ├── Base.php
│   │   ├── Kruskal.php
│   │   └── Prim.php
│   ├── Parallel.php
│   ├── Property/
│   │   ├── GraphProperty.php
│   │   └── WalkProperty.php
│   ├── ResidualGraph.php
│   ├── Search/
│   │   ├── Base.php
│   │   ├── BreadthFirst.php
│   │   └── DepthFirst.php
│   ├── ShortestPath/
│   │   ├── Base.php
│   │   ├── BreadthFirst.php
│   │   ├── Dijkstra.php
│   │   └── MooreBellmanFord.php
│   ├── Symmetric.php
│   ├── TopologicalSort.php
│   ├── TransposeGraph.php
│   ├── TravelingSalesmanProblem/
│   │   ├── Base.php
│   │   ├── Bruteforce.php
│   │   ├── MinimumSpanningTree.php
│   │   └── NearestNeighbor.php
│   ├── Tree/
│   │   ├── Base.php
│   │   ├── BaseDirected.php
│   │   ├── InTree.php
│   │   ├── OutTree.php
│   │   └── Undirected.php
│   └── Weight.php
└── tests/
    ├── BipartitTest.php
    ├── CompleteTest.php
    ├── ConnectedComponentsTest.php
    ├── DegreeTest.php
    ├── DetectNegativeCycleTest.php
    ├── DirectedTest.php
    ├── EulerianTest.php
    ├── FlowTest.php
    ├── GroupsTest.php
    ├── LoopTest.php
    ├── MaxFlow/
    │   └── EdmondsKarpTest.php
    ├── MaximumMatching/
    │   └── FlowTest.php
    ├── MinimumCostFlow/
    │   ├── BaseMcfTest.php
    │   ├── CycleCancellingTest.php
    │   └── SuccessiveShortestPathTest.php
    ├── MinimumSpanningTree/
    │   ├── BaseMstTest.php
    │   ├── KruskalTest.php
    │   └── PrimTest.php
    ├── ParallelTest.php
    ├── Property/
    │   ├── PropertyGraphTest.php
    │   └── WalkPropertyTest.php
    ├── ResidualGraphTest.php
    ├── Search/
    │   └── BreadthFirstTest.php
    ├── ShortestPath/
    │   ├── BaseShortestPathTest.php
    │   ├── BreadthFirstTest.php
    │   ├── DijkstraTest.php
    │   └── MooreBellmanFordTest.php
    ├── SymmetricTest.php
    ├── TestCase.php
    ├── TopologicalSortTest.php
    ├── TravelingSalesmanProblem/
    │   └── BruteforceTest.php
    ├── Tree/
    │   ├── BaseDirectedTest.php
    │   ├── InTreeTest.php
    │   ├── OutTreeTest.php
    │   └── UndirectedTest.php
    └── WeightTest.php
Download .txt
SYMBOL INDEX (451 symbols across 83 files)

FILE: src/Base.php
  class Base (line 8) | abstract class Base{ }

FILE: src/BaseDual.php
  class BaseDual (line 15) | abstract class BaseDual extends Base
    method __construct (line 29) | public function __construct(DualAggregate $graphOrWalk)

FILE: src/BaseGraph.php
  class BaseGraph (line 12) | abstract class BaseGraph extends Base
    method __construct (line 26) | public function __construct(Graph $graph)

FILE: src/BaseVertex.php
  class BaseVertex (line 12) | abstract class BaseVertex extends Base
    method __construct (line 26) | public function __construct(Vertex $vertex)

FILE: src/Bipartit.php
  class Bipartit (line 8) | class Bipartit extends BaseGraph
    method isBipartit (line 16) | public function isBipartit()
    method isBipartitGroups (line 33) | public function isBipartitGroups()
    method getColors (line 47) | public function getColors()
    method getColorVertices (line 88) | public function getColorVertices()
    method createGraphGroups (line 109) | public function createGraphGroups()

FILE: src/Complete.php
  class Complete (line 14) | class Complete extends BaseGraph
    method isComplete (line 23) | public function isComplete()

FILE: src/ConnectedComponents.php
  class ConnectedComponents (line 18) | class ConnectedComponents extends BaseGraph
    method createGraphComponentVertex (line 29) | public function createGraphComponentVertex(Vertex $vertex)
    method createSearch (line 43) | private function createSearch(Vertex $vertex)
    method isSingle (line 69) | public function isSingle()
    method getNumberOfComponents (line 91) | public function getNumberOfComponents()
    method createGraphsComponents (line 124) | public function createGraphsComponents()

FILE: src/Degree.php
  class Degree (line 18) | class Degree extends BaseGraph
    method getDegree (line 29) | public function getDegree()
    method getDegreeMin (line 54) | public function getDegreeMin()
    method getDegreeMax (line 67) | public function getDegreeMax()
    method isRegular (line 78) | public function isRegular()
    method isBalanced (line 100) | public function isBalanced()
    method isVertexSource (line 119) | public function isVertexSource(Vertex $vertex)
    method isVertexSink (line 139) | public function isVertexSink(Vertex $vertex)
    method getDegreeVertex (line 163) | public function getDegreeVertex(Vertex $vertex)
    method isVertexIsolated (line 174) | public function isVertexIsolated(Vertex $vertex)
    method getDegreeInVertex (line 187) | public function getDegreeInVertex($vertex)
    method getDegreeOutVertex (line 207) | public function getDegreeOutVertex(Vertex $vertex)

FILE: src/DetectNegativeCycle.php
  class DetectNegativeCycle (line 11) | class DetectNegativeCycle extends BaseGraph
    method hasCycleNegative (line 19) | public function hasCycleNegative()
    method getCycleNegative (line 39) | public function getCycleNegative()
    method createGraph (line 75) | public function createGraph()

FILE: src/Directed.php
  class Directed (line 14) | class Directed extends BaseDual
    method hasDirected (line 24) | public function hasDirected()
    method hasUndirected (line 43) | public function hasUndirected()
    method isMixed (line 61) | public function isMixed()

FILE: src/Eulerian.php
  class Eulerian (line 5) | class Eulerian extends BaseGraph
    method hasCycle (line 16) | public function hasCycle()

FILE: src/Flow.php
  class Flow (line 18) | class Flow extends BaseDual
    method hasFlow (line 26) | public function hasFlow()
    method getFlowVertex (line 53) | public function getFlowVertex(Vertex $vertex)
    method getBalance (line 76) | public function getBalance()
    method isBalancedFlow (line 103) | public function isBalancedFlow()

FILE: src/Groups.php
  class Groups (line 8) | class Groups extends BaseGraph
    method getNumberOfGroups (line 16) | public function getNumberOfGroups()
    method isBipartit (line 29) | public function isBipartit()
    method getGroups (line 58) | public function getGroups()
    method getVerticesGroup (line 76) | public function getVerticesGroup($group)

FILE: src/Loop.php
  class Loop (line 15) | class Loop extends BaseDual
    method hasLoop (line 23) | public function hasLoop()
    method hasLoopVertex (line 40) | public function hasLoopVertex(Vertex $vertex)

FILE: src/MaxFlow/EdmondsKarp.php
  class EdmondsKarp (line 17) | class EdmondsKarp extends Base
    method __construct (line 33) | public function __construct(Vertex $startVertex, Vertex $destinationVe...
    method createGraph (line 51) | public function createGraph()
    method getFlowMax (line 109) | public function getFlowMax()

FILE: src/MaximumMatching/Base.php
  class Base (line 10) | abstract class Base extends BaseGraph
    method getNumberOfMatches (line 19) | public function getNumberOfMatches()
    method createGraph (line 31) | public function createGraph()
    method getEdges (line 41) | abstract public function getEdges();

FILE: src/MaximumMatching/Flow.php
  class Flow (line 12) | class Flow extends Base
    method getEdges (line 14) | public function getEdges()

FILE: src/MinimumCostFlow/Base.php
  class Base (line 13) | abstract class Base extends BaseGraph
    method checkBalance (line 21) | protected function checkBalance()
    method addFlow (line 45) | protected function addFlow(Graph $resultGraph, Edges $clonedEdges, $ne...
    method getWeightFlow (line 69) | public function getWeightFlow()
    method createGraph (line 82) | abstract public function createGraph();

FILE: src/MinimumCostFlow/CycleCanceling.php
  class CycleCanceling (line 12) | class CycleCanceling extends Base
    method createGraph (line 14) | public function createGraph()

FILE: src/MinimumCostFlow/SuccessiveShortestPath.php
  class SuccessiveShortestPath (line 15) | class SuccessiveShortestPath extends Base
    method createGraph (line 23) | public function createGraph()
    method getVertexSource (line 121) | private function getVertexSource(Graph $graph)
    method getVertexSink (line 137) | private function getVertexSink(Vertex $source)
    method addBalance (line 150) | private function addBalance(Vertex $vertex, $balance)

FILE: src/MinimumSpanningTree/Base.php
  class Base (line 32) | abstract class Base extends AlgorithmBase
    method createGraph (line 42) | public function createGraph()
    method getEdges (line 52) | abstract public function getEdges();
    method getGraph (line 59) | abstract protected function getGraph();
    method getWeight (line 66) | public function getWeight()
    method addEdgesSorted (line 79) | protected function addEdgesSorted(Edges $edges, SplPriorityQueue $sort...

FILE: src/MinimumSpanningTree/Kruskal.php
  class Kruskal (line 11) | class Kruskal extends Base
    method __construct (line 18) | public function __construct(Graph $inputGraph)
    method getGraph (line 23) | protected function getGraph()
    method getEdges (line 31) | public function getEdges()

FILE: src/MinimumSpanningTree/Prim.php
  class Prim (line 11) | class Prim extends Base
    method __construct (line 18) | public function __construct(Vertex $startVertex)
    method getEdges (line 26) | public function getEdges()
    method getGraph (line 72) | protected function getGraph()

FILE: src/Parallel.php
  class Parallel (line 18) | class Parallel extends BaseGraph
    method hasEdgeParallel (line 26) | public function hasEdgeParallel()
    method hasEdgeParallelEdge (line 44) | public function hasEdgeParallelEdge(Edge $edge)
    method getEdgesParallelEdge (line 55) | public function getEdgesParallelEdge(Edge $edge)

FILE: src/Property/GraphProperty.php
  class GraphProperty (line 12) | class GraphProperty extends BaseGraph
    method isEdgeless (line 22) | public function isEdgeless()
    method isNull (line 37) | public function isNull()
    method isTrivial (line 47) | public function isTrivial()

FILE: src/Property/WalkProperty.php
  class WalkProperty (line 14) | class WalkProperty extends BaseAlgorithm
    method __construct (line 28) | public function __construct(Walk $walk)
    method isCycle (line 61) | public function isCycle()
    method isCircuit (line 101) | public function isCircuit()
    method isPath (line 115) | public function isPath()
    method hasCycle (line 139) | public function hasCycle()
    method isLoop (line 162) | public function isLoop()
    method hasLoop (line 182) | public function hasLoop()
    method isDigon (line 213) | public function isDigon()
    method isTriangle (line 237) | public function isTriangle()
    method isSimple (line 294) | public function isSimple()
    method isHamiltonian (line 315) | public function isHamiltonian()
    method isEulerian (line 333) | public function isEulerian()
    method hasArrayDuplicates (line 344) | private function hasArrayDuplicates($array)
    method isArrayContentsEqual (line 367) | private function isArrayContentsEqual($a, $b)

FILE: src/ResidualGraph.php
  class ResidualGraph (line 10) | class ResidualGraph extends BaseGraph
    method setKeepNullCapacity (line 15) | public function setKeepNullCapacity($toggle)
    method setMergeParallelEdges (line 22) | public function setMergeParallelEdges($toggle)
    method createGraph (line 38) | public function createGraph()
    method mergeParallelEdges (line 89) | private function mergeParallelEdges(Edge $newEdge)

FILE: src/Search/Base.php
  class Base (line 10) | abstract class Base extends BaseVertex
    method setDirection (line 26) | public function setDirection($direction)
    method getVerticesAdjacent (line 36) | protected function getVerticesAdjacent(Vertex $vertex)
    method getVertices (line 52) | abstract public function getVertices();

FILE: src/Search/BreadthFirst.php
  class BreadthFirst (line 7) | class BreadthFirst extends Base
    method getVertices (line 13) | public function getVertices($maxDepth = PHP_INT_MAX)

FILE: src/Search/DepthFirst.php
  class DepthFirst (line 7) | class DepthFirst extends Base
    method getVertices (line 14) | public function getVertices()

FILE: src/ShortestPath/Base.php
  class Base (line 54) | abstract class Base extends BaseVertex
    method getWalkTo (line 65) | public function getWalkTo(Vertex $endVertex)
    method getEdgesTo (line 79) | public function getEdgesTo(Vertex $endVertex)
    method getEdgesToInternal (line 93) | protected function getEdgesToInternal(Vertex $endVertex, $edges)
    method sumEdges (line 125) | private function sumEdges(Edges $edges)
    method getVertices (line 141) | public function getVertices()
    method hasVertex (line 161) | public function hasVertex(Vertex $vertex)
    method getDistanceMap (line 179) | public function getDistanceMap()
    method getDistance (line 202) | public function getDistance(Vertex $endVertex)
    method createGraph (line 249) | public function createGraph()
    method getEdgesCheapestPredecesor (line 263) | protected function getEdgesCheapestPredecesor(array $predecessor)
    method getEdges (line 286) | abstract public function getEdges();

FILE: src/ShortestPath/BreadthFirst.php
  class BreadthFirst (line 22) | class BreadthFirst extends Base
    method getDistance (line 32) | public function getDistance(Vertex $endVertex)
    method getEdgesMap (line 42) | public function getEdgesMap()
    method getEdgesTo (line 73) | public function getEdgesTo(Vertex $endVertex)
    method getDistanceMap (line 91) | public function getDistanceMap()
    method getVertices (line 107) | public function getVertices()
    method getEdges (line 118) | public function getEdges()

FILE: src/ShortestPath/Dijkstra.php
  class Dijkstra (line 21) | class Dijkstra extends Base
    method getEdges (line 29) | public function getEdges()

FILE: src/ShortestPath/MooreBellmanFord.php
  class MooreBellmanFord (line 21) | class MooreBellmanFord extends Base
    method bigStep (line 29) | private function bigStep(Edges $edges, array &$totalCostOfCheapestPath...
    method getEdges (line 68) | public function getEdges()
    method getCycleNegative (line 111) | public function getCycleNegative()

FILE: src/Symmetric.php
  class Symmetric (line 15) | class Symmetric extends BaseGraph
    method isSymmetric (line 26) | public function isSymmetric()

FILE: src/TopologicalSort.php
  class TopologicalSort (line 15) | class TopologicalSort extends BaseGraph
    method getVertices (line 24) | public function getVertices()

FILE: src/TransposeGraph.php
  class TransposeGraph (line 9) | class TransposeGraph extends BaseGraph
    method createGraph (line 20) | public function createGraph()

FILE: src/TravelingSalesmanProblem/Base.php
  class Base (line 11) | abstract class Base extends AlgorithmBase
    method createGraph (line 22) | public function createGraph()
    method getGraph (line 32) | abstract protected function getGraph();
    method getVertexStart (line 39) | abstract protected function getVertexStart();
    method getCycle (line 49) | public function getCycle()
    method getWeight (line 54) | public function getWeight()
    method getEdges (line 69) | abstract public function getEdges();

FILE: src/TravelingSalesmanProblem/Bruteforce.php
  class Bruteforce (line 13) | class Bruteforce extends Base
    method __construct (line 63) | public function __construct(Graph $graph)
    method setUpperLimit (line 77) | public function setUpperLimit($limit)
    method setUpperLimitMst (line 90) | public function setUpperLimitMst()
    method getVertexStart (line 98) | protected function getVertexStart()
    method getGraph (line 104) | protected function getGraph()
    method getEdges (line 115) | public function getEdges()
    method step (line 148) | private function step(Vertex $vertex, $totalWeight, array $visitedVert...
    method sumEdges (line 211) | private function sumEdges(array $edges)

FILE: src/TravelingSalesmanProblem/MinimumSpanningTree.php
  class MinimumSpanningTree (line 10) | class MinimumSpanningTree extends Base
    method __construct (line 17) | public function __construct(Graph $inputGraph)
    method getVertexStart (line 22) | protected function getVertexStart()
    method getGraph (line 27) | protected function getGraph()
    method getEdges (line 35) | public function getEdges()

FILE: src/TravelingSalesmanProblem/NearestNeighbor.php
  class NearestNeighbor (line 10) | class NearestNeighbor extends Base
    method __construct (line 17) | public function __construct(Vertex $startVertex)
    method getVertexStart (line 22) | protected function getVertexStart()
    method getGraph (line 27) | protected function getGraph()
    method getEdges (line 35) | public function getEdges()

FILE: src/Tree/Base.php
  class Base (line 38) | abstract class Base extends BaseGraph
    method __construct (line 45) | public function __construct(Graph $graph)
    method isTree (line 57) | abstract public function isTree();
    method isVertexLeaf (line 67) | abstract public function isVertexLeaf(Vertex $vertex);
    method isVertexInternal (line 77) | abstract public function isVertexInternal(Vertex $vertex);
    method getVerticesLeaf (line 86) | public function getVerticesLeaf()
    method getVerticesInternal (line 98) | public function getVerticesInternal()

FILE: src/Tree/BaseDirected.php
  class BaseDirected (line 52) | abstract class BaseDirected extends Tree
    method getVertexRoot (line 62) | public function getVertexRoot()
    method isTree (line 79) | public function isTree()
    method getVertexParent (line 108) | public function getVertexParent(Vertex $vertex)
    method getVerticesChildren (line 128) | abstract public function getVerticesChildren(Vertex $vertex);
    method getVerticesParent (line 140) | abstract protected function getVerticesParent(Vertex $vertex);
    method isVertexPossibleRoot (line 149) | protected function isVertexPossibleRoot(Vertex $vertex)
    method isVertexLeaf (line 161) | public function isVertexLeaf(Vertex $vertex)
    method isVertexInternal (line 175) | public function isVertexInternal(Vertex $vertex)
    method getDegree (line 188) | public function getDegree()
    method getDepthVertex (line 215) | public function getDepthVertex(Vertex $vertex)
    method getHeight (line 237) | public function getHeight()
    method getHeightVertex (line 252) | public function getHeightVertex(Vertex $vertex)
    method getVerticesSubtree (line 275) | public function getVerticesSubtree(Vertex $vertex)
    method getVerticesSubtreeRecursive (line 292) | private function getVerticesSubtreeRecursive(Vertex $vertex, array &$v...
    method getVerticesDescendant (line 315) | public function getVerticesDescendant(Vertex $vertex)

FILE: src/Tree/InTree.php
  class InTree (line 23) | class InTree extends DirectedTree
    method getVerticesChildren (line 25) | public function getVerticesChildren(Vertex $vertex)
    method getVerticesParent (line 35) | protected function getVerticesParent(Vertex $vertex)

FILE: src/Tree/OutTree.php
  class OutTree (line 23) | class OutTree extends DirectedTree
    method getVerticesChildren (line 25) | public function getVerticesChildren(Vertex $vertex)
    method getVerticesParent (line 35) | protected function getVerticesParent(Vertex $vertex)

FILE: src/Tree/Undirected.php
  class Undirected (line 41) | class Undirected extends Tree
    method isTree (line 51) | public function isTree()
    method isVertexLeaf (line 77) | public function isVertexLeaf(Vertex $vertex)
    method isVertexInternal (line 89) | public function isVertexInternal(Vertex $vertex)
    method getVerticesSubtreeRecursive (line 104) | private function getVerticesSubtreeRecursive(Vertex $vertex, array &$v...
    method getVerticesNeighbor (line 132) | private function getVerticesNeighbor(Vertex $vertex)

FILE: src/Weight.php
  class Weight (line 16) | class Weight extends BaseDual
    method isWeighted (line 27) | public function isWeighted()
    method getWeight (line 51) | public function getWeight()
    method getWeightMin (line 75) | public function getWeightMin()
    method getWeightFlow (line 96) | public function getWeightFlow()

FILE: tests/BipartitTest.php
  class BipartitTest (line 8) | class BipartitTest extends TestCase
    method testGraphEmpty (line 10) | public function testGraphEmpty()
    method testGraphPairIsBipartit (line 21) | public function testGraphPairIsBipartit()
    method testGraphPairBipartitGroups (line 43) | public function testGraphPairBipartitGroups(AlgorithmBipartit $alg)
    method testGraphTriangleCycleIsNotBipartit (line 57) | public function testGraphTriangleCycleIsNotBipartit()
    method testGraphTriangleCycleColorsInvalid (line 80) | public function testGraphTriangleCycleColorsInvalid(AlgorithmBipartit ...
    method testGraphTriangleCycleColorVerticesInvalid (line 91) | public function testGraphTriangleCycleColorVerticesInvalid(AlgorithmBi...

FILE: tests/CompleteTest.php
  class CompleteTest (line 8) | class CompleteTest extends TestCase
    method testGraphEmptyK0 (line 10) | public function testGraphEmptyK0()
    method testGraphSingleTrivialK1 (line 19) | public function testGraphSingleTrivialK1()
    method testGraphSimplePairK2 (line 29) | public function testGraphSimplePairK2()
    method testGraphSingleDirectedIsNotComplete (line 40) | public function testGraphSingleDirectedIsNotComplete()
    method testAdditionalEdgesToNotAffectCompleteness (line 51) | public function testAdditionalEdgesToNotAffectCompleteness()

FILE: tests/ConnectedComponentsTest.php
  class ConnectedComponentsTest (line 8) | class ConnectedComponentsTest extends TestCase
    method testNullGraph (line 10) | public function testNullGraph()
    method testGraphSingleTrivial (line 21) | public function testGraphSingleTrivial()
    method testGraphEdgeDirections (line 37) | public function testGraphEdgeDirections()
    method testComponents (line 57) | public function testComponents()
    method testInvalidVertexPassedToAlgorithm (line 86) | public function testInvalidVertexPassedToAlgorithm()

FILE: tests/DegreeTest.php
  class DegreeTest (line 10) | class DegreeTest extends TestCase
    method testGraphEmpty (line 12) | public function testGraphEmpty()
    method testGraphIsolated (line 37) | public function testGraphIsolated()
    method testGraphIrregular (line 52) | public function testGraphIrregular()

FILE: tests/DetectNegativeCycleTest.php
  class DetectNegativeCycleTest (line 8) | class DetectNegativeCycleTest extends TestCase
    method testNullGraph (line 10) | public function testNullGraph()
    method testNullGraphHasNoCycle (line 26) | public function testNullGraphHasNoCycle(DetectNegativeCycle $alg)
    method testNullGraphHasNoCycleGraph (line 37) | public function testNullGraphHasNoCycleGraph(DetectNegativeCycle $alg)
    method testNegativeLoop (line 43) | public function testNegativeLoop()
    method testNegativeCycle (line 62) | public function testNegativeCycle()
    method testNegativeUndirectedIsNegativeCycle (line 83) | public function testNegativeUndirectedIsNegativeCycle()
    method testNegativeCycleSubgraph (line 101) | public function testNegativeCycleSubgraph()
    method testNegativeComponents (line 128) | public function testNegativeComponents()

FILE: tests/DirectedTest.php
  class DirectedTest (line 8) | class DirectedTest extends TestCase
    method testGraphEmpty (line 10) | public function testGraphEmpty()
    method testGraphUndirected (line 21) | public function testGraphUndirected()
    method testGraphDirected (line 34) | public function testGraphDirected()
    method testGraphMixed (line 47) | public function testGraphMixed()

FILE: tests/EulerianTest.php
  class EulerianTest (line 8) | class EulerianTest extends TestCase
    method testGraphEmpty (line 10) | public function testGraphEmpty()
    method testGraphPairHasNoCycle (line 19) | public function testGraphPairHasNoCycle()
    method testGraphTriangleCycleIsNotBipartit (line 32) | public function testGraphTriangleCycleIsNotBipartit()

FILE: tests/FlowTest.php
  class FlowTest (line 8) | class FlowTest extends TestCase
    method testGraphEmpty (line 10) | public function testGraphEmpty()
    method testEdgeWithZeroFlowIsConsideredFlow (line 23) | public function testEdgeWithZeroFlowIsConsideredFlow()
    method testGraphSimple (line 41) | public function testGraphSimple(Graph $graph)
    method testGraphWithUnweightedEdges (line 60) | public function testGraphWithUnweightedEdges(Graph $graph)
    method testGraphBalance (line 72) | public function testGraphBalance()
    method testVertexWithUndirectedEdgeHasInvalidFlow (line 85) | public function testVertexWithUndirectedEdgeHasInvalidFlow()

FILE: tests/GroupsTest.php
  class GroupsTest (line 8) | class GroupsTest extends TestCase
    method testGraphEmpty (line 10) | public function testGraphEmpty()
    method testGraphPairIsBipartit (line 24) | public function testGraphPairIsBipartit()
    method testGraphTriangleCycleIsNotBipartit (line 43) | public function testGraphTriangleCycleIsNotBipartit()

FILE: tests/LoopTest.php
  class LoopTest (line 8) | class LoopTest extends TestCase
    method testGraphEmpty (line 10) | public function testGraphEmpty()
    method testGraphWithMixedCircuitIsNotConsideredLoop (line 19) | public function testGraphWithMixedCircuitIsNotConsideredLoop()
    method testGraphUndirectedLoop (line 36) | public function testGraphUndirectedLoop()
    method testGraphDirectedLoop (line 48) | public function testGraphDirectedLoop()

FILE: tests/MaxFlow/EdmondsKarpTest.php
  class EdmondsKarpTest (line 9) | class EdmondsKarpTest extends TestCase
    method testEdgeDirected (line 11) | public function testEdgeDirected()
    method testEdgesMultiplePaths (line 26) | public function testEdgesMultiplePaths()
    method testEdgesMultiplePathsTwo (line 50) | public function testEdgesMultiplePathsTwo()
    method testEdgesMultiplePathsTree (line 79) | public function testEdgesMultiplePathsTree()
    method testEdgesUndirected (line 111) | public function testEdgesUndirected()
    method testInvalidFlowToOtherGraph (line 139) | public function testInvalidFlowToOtherGraph()
    method testInvalidFlowToSelf (line 151) | public function testInvalidFlowToSelf()

FILE: tests/MaximumMatching/FlowTest.php
  class FlowTest (line 9) | class FlowTest extends TestCase
    method testSingleEdge (line 24) | public function testSingleEdge()
    method testInvalidDirected (line 43) | public function testInvalidDirected()
    method testInvalidBipartit (line 57) | public function testInvalidBipartit()

FILE: tests/MinimumCostFlow/BaseMcfTest.php
  class BaseMcfTest (line 9) | abstract class BaseMcfTest extends TestCase
    method createAlgorithm (line 16) | abstract protected function createAlgorithm(Graph $graph);
    method testNull (line 18) | public function testNull()
    method testSingleIntermediary (line 26) | public function testSingleIntermediary()
    method testSimpleEdge (line 35) | public function testSimpleEdge()
    method testMultipleSinks (line 47) | public function testMultipleSinks()
    method testIntermediaryVertices (line 62) | public function testIntermediaryVertices()
    method testEdgeCapacities (line 80) | public function testEdgeCapacities()
    method testEdgeFlows (line 96) | public function testEdgeFlows()
    method testEdgeCapacityInsufficientFails (line 118) | public function testEdgeCapacityInsufficientFails()
    method testEdgeCapacityUnsetFails (line 132) | public function testEdgeCapacityUnsetFails()
    method testIsolatedVerticesFail (line 146) | public function testIsolatedVerticesFail()
    method testUnbalancedFails (line 159) | public function testUnbalancedFails()
    method testUndirectedFails (line 173) | public function testUndirectedFails()
    method testUndirectedNegativeCycleFails (line 187) | public function testUndirectedNegativeCycleFails()

FILE: tests/MinimumCostFlow/CycleCancellingTest.php
  class CycleCancellingTest (line 8) | class CycleCancellingTest extends BaseMcfTest
    method createAlgorithm (line 10) | protected function createAlgorithm(Graph $graph)

FILE: tests/MinimumCostFlow/SuccessiveShortestPathTest.php
  class SuccessiveShortestPathTest (line 8) | class SuccessiveShortestPathTest extends BaseMcfTest
    method createAlgorithm (line 10) | protected function createAlgorithm(Graph $graph)

FILE: tests/MinimumSpanningTree/BaseMstTest.php
  class BaseMstTest (line 10) | abstract class BaseMstTest extends TestCase
    method createAlg (line 16) | abstract protected function createAlg(Vertex $vertex);
    method testIsolatedVertex (line 18) | public function testIsolatedVertex()
    method testSingleEdge (line 32) | public function testSingleEdge()
    method testSimpleGraph (line 47) | public function testSimpleGraph()
    method testFindingCheapestEdge (line 67) | public function testFindingCheapestEdge()
    method testFindingCheapestTree (line 89) | public function testFindingCheapestTree()
    method testMixedGraphDirectionIsIgnored (line 116) | public function testMixedGraphDirectionIsIgnored()
    method testMultipleComponentsFail (line 137) | public function testMultipleComponentsFail()
    method testMultipleIsolatedVerticesFormMultipleComponentsFail (line 154) | public function testMultipleIsolatedVerticesFormMultipleComponentsFail()

FILE: tests/MinimumSpanningTree/KruskalTest.php
  class KruskalTest (line 9) | class KruskalTest extends BaseMstTest
    method createAlg (line 11) | protected function createAlg(Vertex $vertex)
    method testNullGraphIsNotConsideredToBeConnected (line 16) | public function testNullGraphIsNotConsideredToBeConnected()

FILE: tests/MinimumSpanningTree/PrimTest.php
  class PrimTest (line 8) | class PrimTest extends BaseMstTest
    method createAlg (line 10) | protected function createAlg(Vertex $vertex)

FILE: tests/ParallelTest.php
  class ParallelTest (line 8) | class ParallelTest extends TestCase
    method testGraphEmpty (line 10) | public function testGraphEmpty()
    method testDirectedCycleIsNotConsideredParallel (line 19) | public function testDirectedCycleIsNotConsideredParallel()
    method testDirectedParallelEdge (line 36) | public function testDirectedParallelEdge()
    method testMixedParallelEdge (line 53) | public function testMixedParallelEdge()
    method testMixedParallelEdgesMultiple (line 70) | public function testMixedParallelEdgesMultiple()

FILE: tests/Property/PropertyGraphTest.php
  class PropertyGraphTest (line 9) | class PropertyGraphTest extends TestCase
    method testEmptyIsEdgeless (line 11) | public function testEmptyIsEdgeless()
    method testSingleVertexIsTrivial (line 22) | public function testSingleVertexIsTrivial()

FILE: tests/Property/WalkPropertyTest.php
  class WalkPropertyTest (line 10) | class WalkPropertyTest extends TestCase
    method testTrivialGraph (line 12) | public function testTrivialGraph()
    method testLoop (line 37) | public function testLoop()
    method testCycle (line 61) | public function testCycle()
    method testCircuit (line 86) | public function testCircuit()
    method testNonCircuit (line 107) | public function testNonCircuit()
    method testDigon (line 129) | public function testDigon()
    method testTriangle (line 145) | public function testTriangle()
    method testSimplePathWithinGraph (line 163) | public function testSimplePathWithinGraph()

FILE: tests/ResidualGraphTest.php
  class ResidualGraphTest (line 8) | class ResidualGraphTest extends TestCase
    method testGraphEmpty (line 10) | public function testGraphEmpty()
    method testEdgeUnused (line 23) | public function testEdgeUnused()
    method testEdgeUsed (line 40) | public function testEdgeUsed()
    method testEdgePartial (line 62) | public function testEdgePartial()
    method testResidualGraphCanOptionallyKeepNullCapacityForEdgeWithZeroFlow (line 90) | public function testResidualGraphCanOptionallyKeepNullCapacityForEdgeW...
    method testResidualGraphCanOptionallyKeepNullCapacityForEdgeWithZeroCapacityRemaining (line 114) | public function testResidualGraphCanOptionallyKeepNullCapacityForEdgeW...
    method testParallelEdgesCanBeMerged (line 138) | public function testParallelEdgesCanBeMerged()
    method testInvalidUndirected (line 168) | public function testInvalidUndirected()
    method testInvalidNoFlow (line 184) | public function testInvalidNoFlow()
    method testInvalidNoCapacity (line 199) | public function testInvalidNoCapacity()

FILE: tests/Search/BreadthFirstTest.php
  class BreadthFirstTest (line 9) | class BreadthFirstTest extends TestCase
    method providerMaxDepth (line 11) | public function providerMaxDepth()
    method testMaxDepth (line 44) | public function testMaxDepth(array $edges, $subject, $maxDepth, array ...

FILE: tests/ShortestPath/BaseShortestPathTest.php
  class BaseShortestPathTest (line 10) | abstract class BaseShortestPathTest extends TestCase
    method createAlg (line 17) | abstract protected function createAlg(Vertex $vertex);
    method testGraphParallelNegative (line 19) | abstract public function testGraphParallelNegative();
    method testGraphTrivial (line 21) | public function testGraphTrivial()
    method testGraphSingleLoop (line 41) | public function testGraphSingleLoop()
    method testGraphCycle (line 61) | public function testGraphCycle()
    method testIsolatedVertexIsNotReachable (line 88) | public function testIsolatedVertexIsNotReachable()
    method testSeparateGraphsAreNotReachable (line 103) | public function testSeparateGraphsAreNotReachable()
    method testGraphUnweighted (line 118) | public function testGraphUnweighted()
    method testGraphTwoComponents (line 136) | public function testGraphTwoComponents()
    method getExpectedWeight (line 160) | protected function getExpectedWeight($edges)

FILE: tests/ShortestPath/BreadthFirstTest.php
  class BreadthFirstTest (line 9) | class BreadthFirstTest extends BaseShortestPathTest
    method createAlg (line 11) | protected function createAlg(Vertex $vertex)
    method testGraphParallelNegative (line 16) | public function testGraphParallelNegative()
    method getExpectedWeight (line 37) | protected function getExpectedWeight($edges)

FILE: tests/ShortestPath/DijkstraTest.php
  class DijkstraTest (line 9) | class DijkstraTest extends BaseShortestPathTest
    method createAlg (line 11) | protected function createAlg(Vertex $vertex)
    method testGraphParallelNegative (line 16) | public function testGraphParallelNegative()

FILE: tests/ShortestPath/MooreBellmanFordTest.php
  class MooreBellmanFordTest (line 9) | class MooreBellmanFordTest extends BaseShortestPathTest
    method createAlg (line 11) | protected function createAlg(Vertex $vertex)
    method testGraphParallelNegative (line 16) | public function testGraphParallelNegative()
    method testNoNegativeCycle (line 45) | public function testNoNegativeCycle(MooreBellmanFord $alg)
    method testUndirectedNegativeWeightIsCycle (line 51) | public function testUndirectedNegativeWeightIsCycle()
    method testLoopNegativeWeightIsCycle (line 66) | public function testLoopNegativeWeightIsCycle()
    method testNegativeComponentHasCycle (line 80) | public function testNegativeComponentHasCycle()

FILE: tests/SymmetricTest.php
  class SymmetricTest (line 8) | class SymmetricTest extends TestCase
    method testGraphEmpty (line 10) | public function testGraphEmpty()
    method testGraphIsolated (line 19) | public function testGraphIsolated()
    method testGraphSingleArcIsNotSymmetricr (line 30) | public function testGraphSingleArcIsNotSymmetricr()
    method testGraphAntiparallelIsSymmetricr (line 41) | public function testGraphAntiparallelIsSymmetricr()
    method testGraphSingleUndirectedIsSymmetricr (line 53) | public function testGraphSingleUndirectedIsSymmetricr()

FILE: tests/TestCase.php
  class TestCase (line 11) | class TestCase extends BaseTestCase
    method assertGraphEquals (line 13) | protected function assertGraphEquals(Graph $expected, Graph $actual)
    method assertVertexEquals (line 60) | protected function assertVertexEquals(Vertex $expected, Vertex $actual)
    method assertEdgeEquals (line 65) | protected function assertEdgeEquals(Edge $expected, Edge $actual)
    method getVertexDump (line 70) | private function getVertexDump(Vertex $vertex)
    method getEdgeDump (line 82) | private function getEdgeDump(Edge $edge)
    method setExpectedException (line 99) | public function setExpectedException($exception, $exceptionMessage = '...

FILE: tests/TopologicalSortTest.php
  class TopologicalSortTest (line 8) | class TopologicalSortTest extends TestCase
    method testGraphEmpty (line 10) | public function testGraphEmpty()
    method testGraphIsolated (line 20) | public function testGraphIsolated()
    method testGraphSimple (line 31) | public function testGraphSimple()
    method testFailUndirected (line 41) | public function testFailUndirected()
    method testFailLoop (line 52) | public function testFailLoop()
    method testFailCycle (line 63) | public function testFailCycle()

FILE: tests/TravelingSalesmanProblem/BruteforceTest.php
  class BruteforceTest (line 9) | class BruteforceTest extends TestCase
    method testGetWeightReturnsExpectedWeightForSimpleCycle (line 11) | public function testGetWeightReturnsExpectedWeightForSimpleCycle()
    method testSetUpperLimitMstSetsExactLimitForSimpleCycle (line 26) | public function testSetUpperLimitMstSetsExactLimitForSimpleCycle()

FILE: tests/Tree/BaseDirectedTest.php
  class BaseDirectedTest (line 10) | abstract class BaseDirectedTest extends TestCase
    method createTreeAlg (line 17) | abstract protected function createTreeAlg(Graph $graph);
    method createGraphNonTree (line 22) | abstract protected function createGraphNonTree();
    method createGraphTree (line 27) | abstract protected function createGraphTree();
    method createGraphParallelEdge (line 32) | abstract protected function createGraphParallelEdge();
    method testNullGraph (line 34) | public function testNullGraph()
    method testEmptyGraphDoesNotHaveRootVertex (line 50) | public function testEmptyGraphDoesNotHaveRootVertex(BaseDirected $tree)
    method testEmptyGraphDoesNotHaveDegree (line 60) | public function testEmptyGraphDoesNotHaveDegree(BaseDirected $tree)
    method testEmptyGraphDoesNotHaveHeight (line 70) | public function testEmptyGraphDoesNotHaveHeight(BaseDirected $tree)
    method testGraphTree (line 76) | public function testGraphTree()
    method testGraphTreeRootDoesNotHaveParent (line 115) | public function testGraphTreeRootDoesNotHaveParent(BaseDirected $tree)
    method testNonTree (line 123) | public function testNonTree()
    method testNonTreeVertexHasMoreThanOneParent (line 132) | public function testNonTreeVertexHasMoreThanOneParent()
    method testGraphWithParallelEdgeIsNotTree (line 142) | public function testGraphWithParallelEdgeIsNotTree()
    method testGraphWithLoopIsNotTree (line 151) | public function testGraphWithLoopIsNotTree()
    method testGraphWithLoopCanNotGetSubgraph (line 162) | public function testGraphWithLoopCanNotGetSubgraph()
    method testGraphWithUndirectedEdgeIsNotTree (line 174) | public function testGraphWithUndirectedEdgeIsNotTree()
    method testGraphWithMixedEdgesIsNotTree (line 185) | public function testGraphWithMixedEdgesIsNotTree()

FILE: tests/Tree/InTreeTest.php
  class InTreeTest (line 8) | class InTreeTest extends BaseDirectedTest
    method createGraphTree (line 10) | protected function createGraphTree()
    method createTreeAlg (line 25) | protected function createTreeAlg(Graph $graph)
    method createGraphNonTree (line 30) | protected function createGraphNonTree()
    method createGraphParallelEdge (line 41) | protected function createGraphParallelEdge()

FILE: tests/Tree/OutTreeTest.php
  class OutTreeTest (line 8) | class OutTreeTest extends BaseDirectedTest
    method createGraphTree (line 10) | protected function createGraphTree()
    method createTreeAlg (line 25) | protected function createTreeAlg(Graph $graph)
    method createGraphNonTree (line 30) | protected function createGraphNonTree()
    method createGraphParallelEdge (line 41) | protected function createGraphParallelEdge()

FILE: tests/Tree/UndirectedTest.php
  class UndirectedTest (line 9) | class UndirectedTest extends TestCase
    method createTree (line 11) | protected function createTree(Graph $graph)
    method testNullGraph (line 16) | public function testNullGraph()
    method testGraphTrivial (line 27) | public function testGraphTrivial()
    method testGraphSimplePair (line 38) | public function testGraphSimplePair()
    method testGraphSimpleLine (line 50) | public function testGraphSimpleLine()
    method testGraphPairParallelIsNotTree (line 63) | public function testGraphPairParallelIsNotTree()
    method testGraphLoopIsNotTree (line 74) | public function testGraphLoopIsNotTree()
    method testGraphCycleIsNotTree (line 84) | public function testGraphCycleIsNotTree()
    method testGraphDirectedIsNotTree (line 96) | public function testGraphDirectedIsNotTree()
    method testGraphMixedIsNotTree (line 106) | public function testGraphMixedIsNotTree()

FILE: tests/WeightTest.php
  class WeightTest (line 8) | class WeightTest extends TestCase
    method testGraphEmpty (line 10) | public function testGraphEmpty()
    method testGraphSimple (line 29) | public function testGraphSimple(Graph $graph)
    method testGraphWithUnweightedEdges (line 49) | public function testGraphWithUnweightedEdges(Graph $graph)
Condensed preview — 92 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (259K chars).
[
  {
    "path": ".gitattributes",
    "chars": 177,
    "preview": "/.gitattributes export-ignore\n/.github/workflows/ export-ignore\n/.gitignore export-ignore\n/phpunit.xml.dist export-ignor"
  },
  {
    "path": ".github/workflows/ci.yml",
    "chars": 652,
    "preview": "name: CI\n\non:\n  push:\n  pull_request:\n\njobs:\n  PHPUnit:\n    runs-on: ubuntu-20.04\n    strategy:\n      matrix:\n        ph"
  },
  {
    "path": ".gitignore",
    "chars": 23,
    "preview": "/vendor\n/composer.lock\n"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 1158,
    "preview": "# Changelog\n\n## 0.8.2 (2020-02-20)\n\n*   Feature: Add max depth parameter to breadth first search.\n    (#27 by @phyrwork)"
  },
  {
    "path": "LICENSE",
    "chars": 1081,
    "preview": "The MIT License (MIT)\n\nCopyright (c) 2015 Christian Lück\n\nPermission is hereby granted, free of charge, to any person ob"
  },
  {
    "path": "README.md",
    "chars": 1640,
    "preview": "# graphp/algorithms\n\n[![CI status](https://github.com/graphp/algorithms/workflows/CI/badge.svg)](https://github.com/grap"
  },
  {
    "path": "composer.json",
    "chars": 802,
    "preview": "{\n    \"name\": \"graphp/algorithms\",\n    \"description\": \"Common mathematical graph algorithms implemented in PHP\",\n    \"ke"
  },
  {
    "path": "phpunit.xml.dist",
    "chars": 621,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<!-- PHPUnit configuration file with new format for PHPUnit 9.3+ -->\n<phpunit xm"
  },
  {
    "path": "phpunit.xml.legacy",
    "chars": 597,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<!-- PHPUnit configuration file with old format for PHPUnit 9.2 or older-->\n<php"
  },
  {
    "path": "src/Base.php",
    "chars": 83,
    "preview": "<?php\n\nnamespace Graphp\\Algorithms;\n\n/**\n * @deprecated\n */\nabstract class Base{ }\n"
  },
  {
    "path": "src/BaseDual.php",
    "chars": 655,
    "preview": "<?php\n\nnamespace Graphp\\Algorithms;\n\nuse Graphp\\Graph\\Graph;\nuse Graphp\\Graph\\Set\\DualAggregate;\nuse Graphp\\Graph\\Walk;\n"
  },
  {
    "path": "src/BaseGraph.php",
    "chars": 490,
    "preview": "<?php\n\nnamespace Graphp\\Algorithms;\n\nuse Graphp\\Graph\\Graph;\n\n/**\n * Abstract base class for algorithms that operate on "
  },
  {
    "path": "src/BaseVertex.php",
    "chars": 503,
    "preview": "<?php\n\nnamespace Graphp\\Algorithms;\n\nuse Graphp\\Graph\\Vertex;\n\n/**\n * Abstract base class for algorithms that operate on"
  },
  {
    "path": "src/Bipartit.php",
    "chars": 3426,
    "preview": "<?php\n\nnamespace Graphp\\Algorithms;\n\nuse Graphp\\Graph\\Exception\\UnexpectedValueException;\nuse Graphp\\Graph\\Graph;\n\nclass"
  },
  {
    "path": "src/Complete.php",
    "chars": 1066,
    "preview": "<?php\n\nnamespace Graphp\\Algorithms;\n\n/**\n * Basic algorithms for working with complete graphs\n *\n * A complete graph is "
  },
  {
    "path": "src/ConnectedComponents.php",
    "chars": 4856,
    "preview": "<?php\n\nnamespace Graphp\\Algorithms;\n\nuse Graphp\\Algorithms\\Search\\BreadthFirst as SearchBreadthFirst;\nuse Graphp\\Graph\\E"
  },
  {
    "path": "src/Degree.php",
    "chars": 5907,
    "preview": "<?php\n\nnamespace Graphp\\Algorithms;\n\nuse Graphp\\Graph\\Exception\\UnderflowException;\nuse Graphp\\Graph\\Exception\\Unexpecte"
  },
  {
    "path": "src/DetectNegativeCycle.php",
    "chars": 2534,
    "preview": "<?php\n\nnamespace Graphp\\Algorithms;\n\nuse Graphp\\Algorithms\\ShortestPath\\MooreBellmanFord as SpMooreBellmanFord;\nuse Grap"
  },
  {
    "path": "src/Directed.php",
    "chars": 1651,
    "preview": "<?php\n\nnamespace Graphp\\Algorithms;\n\nuse Graphp\\Graph\\EdgeDirected;\nuse Graphp\\Graph\\EdgeUndirected;\n\n/**\n * Basic algor"
  },
  {
    "path": "src/Eulerian.php",
    "chars": 840,
    "preview": "<?php\n\nnamespace Graphp\\Algorithms;\n\nclass Eulerian extends BaseGraph\n{\n    /**\n     * check whether this graph has an e"
  },
  {
    "path": "src/Flow.php",
    "chars": 3585,
    "preview": "<?php\n\nnamespace Graphp\\Algorithms;\n\nuse Graphp\\Graph\\EdgeDirected;\nuse Graphp\\Graph\\Exception\\UnexpectedValueException;"
  },
  {
    "path": "src/Groups.php",
    "chars": 2206,
    "preview": "<?php\n\nnamespace Graphp\\Algorithms;\n\nuse Graphp\\Graph\\Set\\Vertices;\nuse Graphp\\Graph\\Vertex;\n\nclass Groups extends BaseG"
  },
  {
    "path": "src/Loop.php",
    "chars": 1060,
    "preview": "<?php\n\nnamespace Graphp\\Algorithms;\n\nuse Graphp\\Graph\\Vertex;\n\n/**\n * Basic algorithms for working with loop edges\n *\n *"
  },
  {
    "path": "src/MaxFlow/EdmondsKarp.php",
    "chars": 4084,
    "preview": "<?php\n\nnamespace Graphp\\Algorithms\\MaxFlow;\n\nuse Graphp\\Algorithms\\Base;\nuse Graphp\\Algorithms\\ResidualGraph;\nuse Graphp"
  },
  {
    "path": "src/MaximumMatching/Base.php",
    "chars": 993,
    "preview": "<?php\n\nnamespace Graphp\\Algorithms\\MaximumMatching;\n\nuse Graphp\\Algorithms\\BaseGraph;\nuse Graphp\\Graph\\Exception\\Unexpec"
  },
  {
    "path": "src/MaximumMatching/Flow.php",
    "chars": 2962,
    "preview": "<?php\n\nnamespace Graphp\\Algorithms\\MaximumMatching;\n\nuse Graphp\\Algorithms\\Directed;\nuse Graphp\\Algorithms\\Groups;\nuse G"
  },
  {
    "path": "src/MinimumCostFlow/Base.php",
    "chars": 2641,
    "preview": "<?php\n\nnamespace Graphp\\Algorithms\\MinimumCostFlow;\n\nuse Graphp\\Algorithms\\BaseGraph;\nuse Graphp\\Algorithms\\Weight as Al"
  },
  {
    "path": "src/MinimumCostFlow/CycleCanceling.php",
    "chars": 2838,
    "preview": "<?php\n\nnamespace Graphp\\Algorithms\\MinimumCostFlow;\n\nuse Graphp\\Algorithms\\DetectNegativeCycle;\nuse Graphp\\Algorithms\\Ma"
  },
  {
    "path": "src/MinimumCostFlow/SuccessiveShortestPath.php",
    "chars": 5621,
    "preview": "<?php\n\nnamespace Graphp\\Algorithms\\MinimumCostFlow;\n\nuse Graphp\\Algorithms\\ResidualGraph;\nuse Graphp\\Algorithms\\Shortest"
  },
  {
    "path": "src/MinimumSpanningTree/Base.php",
    "chars": 2767,
    "preview": "<?php\n\nnamespace Graphp\\Algorithms\\MinimumSpanningTree;\n\nuse Graphp\\Algorithms\\Base as AlgorithmBase;\nuse Graphp\\Graph\\E"
  },
  {
    "path": "src/MinimumSpanningTree/Kruskal.php",
    "chars": 4267,
    "preview": "<?php\n\nnamespace Graphp\\Algorithms\\MinimumSpanningTree;\n\nuse Graphp\\Graph\\Edge;\nuse Graphp\\Graph\\Exception\\UnexpectedVal"
  },
  {
    "path": "src/MinimumSpanningTree/Prim.php",
    "chars": 2337,
    "preview": "<?php\n\nnamespace Graphp\\Algorithms\\MinimumSpanningTree;\n\nuse Graphp\\Graph\\Edge;\nuse Graphp\\Graph\\Exception\\UnexpectedVal"
  },
  {
    "path": "src/Parallel.php",
    "chars": 2080,
    "preview": "<?php\n\nnamespace Graphp\\Algorithms;\n\nuse Graphp\\Graph\\Edge;\nuse Graphp\\Graph\\EdgeDirected as EdgeDirected;\nuse Graphp\\Gr"
  },
  {
    "path": "src/Property/GraphProperty.php",
    "chars": 1321,
    "preview": "<?php\n\nnamespace Graphp\\Algorithms\\Property;\n\nuse Graphp\\Algorithms\\BaseGraph;\n\n/**\n * Simple algorithms for working wit"
  },
  {
    "path": "src/Property/WalkProperty.php",
    "chars": 10821,
    "preview": "<?php\n\nnamespace Graphp\\Algorithms\\Property;\n\nuse Graphp\\Algorithms\\Base as BaseAlgorithm;\nuse Graphp\\Algorithms\\Loop as"
  },
  {
    "path": "src/ResidualGraph.php",
    "chars": 3240,
    "preview": "<?php\n\nnamespace Graphp\\Algorithms;\n\nuse Graphp\\Graph\\Edge;\nuse Graphp\\Graph\\EdgeDirected;\nuse Graphp\\Graph\\Exception\\Un"
  },
  {
    "path": "src/Search/Base.php",
    "chars": 1487,
    "preview": "<?php\n\nnamespace Graphp\\Algorithms\\Search;\n\nuse Graphp\\Algorithms\\BaseVertex;\nuse Graphp\\Graph\\Exception\\InvalidArgument"
  },
  {
    "path": "src/Search/BreadthFirst.php",
    "chars": 1630,
    "preview": "<?php\n\nnamespace Graphp\\Algorithms\\Search;\n\nuse Graphp\\Graph\\Set\\Vertices;\n\nclass BreadthFirst extends Base\n{\n    /**\n  "
  },
  {
    "path": "src/Search/DepthFirst.php",
    "chars": 712,
    "preview": "<?php\n\nnamespace Graphp\\Algorithms\\Search;\n\nuse Graphp\\Graph\\Set\\Vertices;\n\nclass DepthFirst extends Base\n{\n    /**\n    "
  },
  {
    "path": "src/ShortestPath/Base.php",
    "chars": 8949,
    "preview": "<?php\n\nnamespace Graphp\\Algorithms\\ShortestPath;\n\nuse Graphp\\Algorithms\\BaseVertex;\nuse Graphp\\Graph\\Edge;\nuse Graphp\\Gr"
  },
  {
    "path": "src/ShortestPath/BreadthFirst.php",
    "chars": 3690,
    "preview": "<?php\n\nnamespace Graphp\\Algorithms\\ShortestPath;\n\nuse Graphp\\Graph\\Vertex;\nuse Graphp\\Graph\\Exception\\OutOfBoundsExcepti"
  },
  {
    "path": "src/ShortestPath/Dijkstra.php",
    "chars": 5244,
    "preview": "<?php\n\nnamespace Graphp\\Algorithms\\ShortestPath;\n\nuse Graphp\\Graph\\Exception\\UnexpectedValueException;\nuse Graphp\\Graph\\"
  },
  {
    "path": "src/ShortestPath/MooreBellmanFord.php",
    "chars": 4519,
    "preview": "<?php\n\nnamespace Graphp\\Algorithms\\ShortestPath;\n\nuse Graphp\\Graph\\Exception\\NegativeCycleException;\nuse Graphp\\Graph\\Ex"
  },
  {
    "path": "src/Symmetric.php",
    "chars": 1199,
    "preview": "<?php\n\nnamespace Graphp\\Algorithms;\n\nuse Graphp\\Graph\\EdgeDirected;\n\n/**\n * Basic algorithms for working with symmetric "
  },
  {
    "path": "src/TopologicalSort.php",
    "chars": 2569,
    "preview": "<?php\n\nnamespace Graphp\\Algorithms;\n\nuse Graphp\\Graph\\Exception\\UnexpectedValueException;\nuse Graphp\\Graph\\Set\\Vertices;"
  },
  {
    "path": "src/TransposeGraph.php",
    "chars": 865,
    "preview": "<?php\n\nnamespace Graphp\\Algorithms;\n\nuse Graphp\\Graph\\EdgeDirected;\nuse Graphp\\Graph\\Exception\\UnexpectedValueException;"
  },
  {
    "path": "src/TravelingSalesmanProblem/Base.php",
    "chars": 1604,
    "preview": "<?php\n\nnamespace Graphp\\Algorithms\\TravelingSalesmanProblem;\n\nuse Graphp\\Algorithms\\Base as AlgorithmBase;\nuse Graphp\\Gr"
  },
  {
    "path": "src/TravelingSalesmanProblem/Bruteforce.php",
    "chars": 5920,
    "preview": "<?php\n\nnamespace Graphp\\Algorithms\\TravelingSalesmanProblem;\n\nuse Graphp\\Algorithms\\TravelingSalesmanProblem\\MinimumSpan"
  },
  {
    "path": "src/TravelingSalesmanProblem/MinimumSpanningTree.php",
    "chars": 2295,
    "preview": "<?php\n\nnamespace Graphp\\Algorithms\\TravelingSalesmanProblem;\n\nuse Graphp\\Algorithms\\MinimumSpanningTree\\Kruskal as MstKr"
  },
  {
    "path": "src/TravelingSalesmanProblem/NearestNeighbor.php",
    "chars": 2399,
    "preview": "<?php\n\nnamespace Graphp\\Algorithms\\TravelingSalesmanProblem;\n\nuse Graphp\\Graph\\Exception\\UnexpectedValueException;\nuse G"
  },
  {
    "path": "src/Tree/Base.php",
    "chars": 2881,
    "preview": "<?php\n\nnamespace Graphp\\Algorithms\\Tree;\n\nuse Graphp\\Algorithms\\BaseGraph;\nuse Graphp\\Algorithms\\Degree;\nuse Graphp\\Grap"
  },
  {
    "path": "src/Tree/BaseDirected.php",
    "chars": 10524,
    "preview": "<?php\n\nnamespace Graphp\\Algorithms\\Tree;\n\nuse Graphp\\Algorithms\\Tree\\Base as Tree;\nuse Graphp\\Graph\\Exception\\UnderflowE"
  },
  {
    "path": "src/Tree/InTree.php",
    "chars": 1051,
    "preview": "<?php\n\nnamespace Graphp\\Algorithms\\Tree;\n\nuse Graphp\\Algorithms\\Tree\\BaseDirected as DirectedTree;\nuse Graphp\\Graph\\Exce"
  },
  {
    "path": "src/Tree/OutTree.php",
    "chars": 1083,
    "preview": "<?php\n\nnamespace Graphp\\Algorithms\\Tree;\n\nuse Graphp\\Algorithms\\Tree\\BaseDirected as DirectedTree;\nuse Graphp\\Graph\\Exce"
  },
  {
    "path": "src/Tree/Undirected.php",
    "chars": 4429,
    "preview": "<?php\n\nnamespace Graphp\\Algorithms\\Tree;\n\nuse Graphp\\Algorithms\\Tree\\Base as Tree;\nuse Graphp\\Graph\\EdgeUndirected;\nuse "
  },
  {
    "path": "src/Weight.php",
    "chars": 2885,
    "preview": "<?php\n\nnamespace Graphp\\Algorithms;\n\nuse Graphp\\Graph\\Graph;\n\n/**\n * Basic algorithms for working with the (total) weigh"
  },
  {
    "path": "tests/BipartitTest.php",
    "chars": 2731,
    "preview": "<?php\n\nnamespace Graphp\\Tests\\Algorithms;\n\nuse Graphp\\Algorithms\\Bipartit as AlgorithmBipartit;\nuse Graphp\\Graph\\Graph;\n"
  },
  {
    "path": "tests/CompleteTest.php",
    "chars": 1748,
    "preview": "<?php\n\nnamespace Graphp\\Tests\\Algorithms;\n\nuse Graphp\\Algorithms\\Complete as AlgorithmComplete;\nuse Graphp\\Graph\\Graph;\n"
  },
  {
    "path": "tests/ConnectedComponentsTest.php",
    "chars": 2986,
    "preview": "<?php\n\nnamespace Graphp\\Tests\\Algorithms;\n\nuse Graphp\\Algorithms\\ConnectedComponents as AlgorithmConnected;\nuse Graphp\\G"
  },
  {
    "path": "tests/DegreeTest.php",
    "chars": 3041,
    "preview": "<?php\n\nnamespace Graphp\\Tests\\Algorithms;\n\nuse Graphp\\Algorithms\\Degree as AlgorithmDegree;\nuse Graphp\\Graph\\Exception\\U"
  },
  {
    "path": "tests/DetectNegativeCycleTest.php",
    "chars": 4563,
    "preview": "<?php\n\nnamespace Graphp\\Tests\\Algorithms;\n\nuse Graphp\\Algorithms\\DetectNegativeCycle;\nuse Graphp\\Graph\\Graph;\n\nclass Det"
  },
  {
    "path": "tests/DirectedTest.php",
    "chars": 1659,
    "preview": "<?php\n\nnamespace Graphp\\Tests\\Algorithms;\n\nuse Graphp\\Algorithms\\Directed as AlgorithmDirected;\nuse Graphp\\Graph\\Graph;\n"
  },
  {
    "path": "tests/EulerianTest.php",
    "chars": 1144,
    "preview": "<?php\n\nnamespace Graphp\\Tests\\Algorithms;\n\nuse Graphp\\Algorithms\\Eulerian as AlgorithmEulerian;\nuse Graphp\\Graph\\Graph;\n"
  },
  {
    "path": "tests/FlowTest.php",
    "chars": 2713,
    "preview": "<?php\n\nnamespace Graphp\\Tests\\Algorithms;\n\nuse Graphp\\Algorithms\\Flow as AlgorithmFlow;\nuse Graphp\\Graph\\Graph;\n\nclass F"
  },
  {
    "path": "tests/GroupsTest.php",
    "chars": 1934,
    "preview": "<?php\n\nnamespace Graphp\\Tests\\Algorithms;\n\nuse Graphp\\Algorithms\\Groups as AlgorithmGroups;\nuse Graphp\\Graph\\Graph;\n\ncla"
  },
  {
    "path": "tests/LoopTest.php",
    "chars": 1506,
    "preview": "<?php\n\nnamespace Graphp\\Tests\\Algorithms;\n\nuse Graphp\\Algorithms\\Loop as AlgorithmLoop;\nuse Graphp\\Graph\\Graph;\n\nclass L"
  },
  {
    "path": "tests/MaxFlow/EdmondsKarpTest.php",
    "chars": 4926,
    "preview": "<?php\n\nnamespace Graphp\\Tests\\Algorithms\\MaxFlow;\n\nuse Graphp\\Algorithms\\MaxFlow\\EdmondsKarp as AlgorithmMaxFlowEdmondsK"
  },
  {
    "path": "tests/MaximumMatching/FlowTest.php",
    "chars": 2002,
    "preview": "<?php\n\nnamespace Graphp\\Tests\\Algorithms\\MaximumMatching;\n\nuse Graphp\\Algorithms\\MaximumMatching\\Flow;\nuse Graphp\\Graph\\"
  },
  {
    "path": "tests/MinimumCostFlow/BaseMcfTest.php",
    "chars": 6875,
    "preview": "<?php\n\nnamespace Graphp\\Tests\\Algorithms\\MinimumCostFlow;\n\nuse Graphp\\Algorithms\\MinimumCostFlow\\Base;\nuse Graphp\\Graph\\"
  },
  {
    "path": "tests/MinimumCostFlow/CycleCancellingTest.php",
    "chars": 296,
    "preview": "<?php\n\nnamespace Graphp\\Tests\\Algorithms\\MinimumCostFlow;\n\nuse Graphp\\Algorithms\\MinimumCostFlow\\CycleCanceling;\nuse Gra"
  },
  {
    "path": "tests/MinimumCostFlow/SuccessiveShortestPathTest.php",
    "chars": 319,
    "preview": "<?php\n\nnamespace Graphp\\Tests\\Algorithms\\MinimumCostFlow;\n\nuse Graphp\\Algorithms\\MinimumCostFlow\\SuccessiveShortestPath;"
  },
  {
    "path": "tests/MinimumSpanningTree/BaseMstTest.php",
    "chars": 5339,
    "preview": "<?php\n\nnamespace Graphp\\Tests\\Algorithms\\MinimumSpanningTree;\n\nuse Graphp\\Algorithms\\MinimumSpanningTree\\Base as MstBase"
  },
  {
    "path": "tests/MinimumSpanningTree/KruskalTest.php",
    "chars": 552,
    "preview": "<?php\n\nnamespace Graphp\\Tests\\Algorithms\\MinimumSpanningTree;\n\nuse Graphp\\Algorithms\\MinimumSpanningTree\\Kruskal;\nuse Gr"
  },
  {
    "path": "tests/MinimumSpanningTree/PrimTest.php",
    "chars": 271,
    "preview": "<?php\n\nnamespace Graphp\\Tests\\Algorithms\\MinimumSpanningTree;\n\nuse Graphp\\Algorithms\\MinimumSpanningTree\\Prim;\nuse Graph"
  },
  {
    "path": "tests/ParallelTest.php",
    "chars": 3398,
    "preview": "<?php\n\nnamespace Graphp\\Tests\\Algorithms;\n\nuse Graphp\\Algorithms\\Parallel as AlgorithmParallel;\nuse Graphp\\Graph\\Graph;\n"
  },
  {
    "path": "tests/Property/PropertyGraphTest.php",
    "chars": 776,
    "preview": "<?php\n\nnamespace Graphp\\Tests\\Algorithms\\Property;\n\nuse Graphp\\Algorithms\\Property\\GraphProperty;\nuse Graphp\\Graph\\Graph"
  },
  {
    "path": "tests/Property/WalkPropertyTest.php",
    "chars": 5468,
    "preview": "<?php\n\nnamespace Graphp\\Tests\\Algorithms\\Property;\n\nuse Graphp\\Algorithms\\Property\\WalkProperty;\nuse Graphp\\Graph\\Graph;"
  },
  {
    "path": "tests/ResidualGraphTest.php",
    "chars": 6888,
    "preview": "<?php\n\nnamespace Graphp\\Tests\\Algorithms;\n\nuse Graphp\\Algorithms\\ResidualGraph;\nuse Graphp\\Graph\\Graph;\n\nclass ResidualG"
  },
  {
    "path": "tests/Search/BreadthFirstTest.php",
    "chars": 1769,
    "preview": "<?php\n\nnamespace Graphp\\Tests\\Algorithms\\Search;\n\nuse Graphp\\Algorithms\\Search\\BreadthFirst;\nuse Graphp\\Graph\\Graph;\nuse"
  },
  {
    "path": "tests/ShortestPath/BaseShortestPathTest.php",
    "chars": 5693,
    "preview": "<?php\n\nnamespace Graphp\\Tests\\Algorithms\\ShortestPath;\n\nuse Graphp\\Algorithms\\ShortestPath\\Base as ShortestPathAlg;\nuse "
  },
  {
    "path": "tests/ShortestPath/BreadthFirstTest.php",
    "chars": 1233,
    "preview": "<?php\n\nnamespace Graphp\\Tests\\Algorithms\\ShortestPath;\n\nuse Graphp\\Algorithms\\ShortestPath\\BreadthFirst;\nuse Graphp\\Grap"
  },
  {
    "path": "tests/ShortestPath/DijkstraTest.php",
    "chars": 793,
    "preview": "<?php\n\nnamespace Graphp\\Tests\\Algorithms\\ShortestPath;\n\nuse Graphp\\Algorithms\\ShortestPath\\Dijkstra;\nuse Graphp\\Graph\\Gr"
  },
  {
    "path": "tests/ShortestPath/MooreBellmanFordTest.php",
    "chars": 3229,
    "preview": "<?php\n\nnamespace Graphp\\Tests\\Algorithms\\ShortestPath;\n\nuse Graphp\\Algorithms\\ShortestPath\\MooreBellmanFord;\nuse Graphp\\"
  },
  {
    "path": "tests/SymmetricTest.php",
    "chars": 1578,
    "preview": "<?php\n\nnamespace Graphp\\Tests\\Algorithms;\n\nuse Graphp\\Algorithms\\Symmetric as AlgorithmSymmetric;\nuse Graphp\\Graph\\Graph"
  },
  {
    "path": "tests/TestCase.php",
    "chars": 4046,
    "preview": "<?php\n\nnamespace Graphp\\Tests\\Algorithms;\n\nuse Graphp\\Graph\\Edge;\nuse Graphp\\Graph\\EdgeDirected;\nuse Graphp\\Graph\\Graph;"
  },
  {
    "path": "tests/TopologicalSortTest.php",
    "chars": 2051,
    "preview": "<?php\n\nnamespace Graphp\\Tests\\Algorithms;\n\nuse Graphp\\Algorithms\\TopologicalSort;\nuse Graphp\\Graph\\Graph;\n\nclass Topolog"
  },
  {
    "path": "tests/TravelingSalesmanProblem/BruteforceTest.php",
    "chars": 1346,
    "preview": "<?php\n\nnamespace Graphp\\Tests\\Algorithms\\TravelingSalesmanProblem;\n\nuse Graphp\\Algorithms\\TravelingSalesmanProblem\\Brute"
  },
  {
    "path": "tests/Tree/BaseDirectedTest.php",
    "chars": 5863,
    "preview": "<?php\n\nnamespace Graphp\\Tests\\Algorithms\\Tree;\n\nuse Graphp\\Algorithms\\Tree\\BaseDirected;\nuse Graphp\\Graph\\Graph;\nuse Gra"
  },
  {
    "path": "tests/Tree/InTreeTest.php",
    "chars": 1339,
    "preview": "<?php\n\nnamespace Graphp\\Tests\\Algorithms\\Tree;\n\nuse Graphp\\Algorithms\\Tree\\InTree;\nuse Graphp\\Graph\\Graph;\n\nclass InTree"
  },
  {
    "path": "tests/Tree/OutTreeTest.php",
    "chars": 1342,
    "preview": "<?php\n\nnamespace Graphp\\Tests\\Algorithms\\Tree;\n\nuse Graphp\\Algorithms\\Tree\\OutTree;\nuse Graphp\\Graph\\Graph;\n\nclass OutTr"
  },
  {
    "path": "tests/Tree/UndirectedTest.php",
    "chars": 3873,
    "preview": "<?php\n\nnamespace Graphp\\Tests\\Algorithms\\Tree;\n\nuse Graphp\\Algorithms\\Tree\\Undirected;\nuse Graphp\\Graph\\Graph;\nuse Graph"
  },
  {
    "path": "tests/WeightTest.php",
    "chars": 1580,
    "preview": "<?php\n\nnamespace Graphp\\Tests\\Algorithms;\n\nuse Graphp\\Algorithms\\Weight as AlgorithmWeight;\nuse Graphp\\Graph\\Graph;\n\ncla"
  }
]

About this extraction

This page contains the full source code of the graphp/algorithms GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 92 files (238.7 KB), approximately 63.8k tokens, and a symbol index with 451 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!