Skip to content

Commit

Permalink
Add Graph::withoutVertex() and Graph::withoutEdge() instead of destroy()
Browse files Browse the repository at this point in the history
Destroying a Vertex or Edge will no longer leave an object in an invalid
state but will instead return a new graph without the given entity. Also
add Graph::withoutVertices() and Graph::withoutEdges() to remove
multiple entities at once.

Accordingly, Walk::isValid() has been removed as it would always return
true.
  • Loading branch information
clue committed Jan 20, 2020
1 parent a4f65ca commit ed01aef
Show file tree
Hide file tree
Showing 8 changed files with 220 additions and 216 deletions.
15 changes: 0 additions & 15 deletions src/Edge.php
Expand Up @@ -106,21 +106,6 @@ public function getGraph()
// @codeCoverageIgnoreEnd
}

/**
* destroy edge and remove reference from vertices and graph
*
* @uses Graph::removeEdge()
* @uses Vertex::removeEdge()
* @return void
*/
public function destroy()
{
$this->getGraph()->removeEdge($this);
foreach ($this->getVertices() as $vertex) {
$vertex->removeEdge($this);
}
}

/**
* do NOT allow cloning of objects
*
Expand Down
131 changes: 96 additions & 35 deletions src/Graph.php
Expand Up @@ -88,67 +88,128 @@ public function createEdgeDirected(Vertex $source, Vertex $target, array $attrib
}

/**
* adds a new Vertex to the Graph (MUST NOT be called manually!)
* Returns a copy of this graph without the given vertex
*
* @param Vertex $vertex instance of the new Vertex
* @return void
* @internal
* @see self::createVertex() instead!
* If this vertex was not found in this graph, the returned graph will be
* identical.
*
* @param Vertex $vertex
* @return self
*/
public function addVertex(Vertex $vertex)
public function withoutVertex(Vertex $vertex)
{
$this->vertices[] = $vertex;
return $this->withoutVertices(new Vertices(array($vertex)));
}

/**
* adds a new Edge to the Graph (MUST NOT be called manually!)
* Returns a copy of this graph without the given vertices
*
* @param Edge $edge instance of the new Edge
* @return void
* @internal
* @see Graph::createEdgeUndirected() instead!
* If any of the given vertices can not be found in this graph, they will
* silently be ignored. If neither of the vertices can be found in this graph,
* the returned graph will be identical.
*
* @param Vertices $vertices
* @return self
*/
public function addEdge(Edge $edge)
public function withoutVertices(Vertices $vertices)
{
$this->edges []= $edge;
// keep copy of original vertices and edges and temporarily remove all $vertices and their adjacent edges
$originalEdges = $this->edges;
$originalVertices = $this->vertices;
foreach ($vertices as $vertex) {
if (($key = \array_search($vertex, $this->vertices, true)) !== false) {
unset($this->vertices[$key]);
foreach ($vertex->getEdges() as $edge) {
if (($key = \array_search($edge, $this->edges, true)) !== false) {
unset($this->edges[$key]);
}
}
}
}

// no vertices matched => return graph as-is
if (\count($this->vertices) === \count($originalVertices)) {
return $this;
}

// clone graph with vertices/edges temporarily removed, then restore
$clone = clone $this;
$this->edges= $originalEdges;
$this->vertices = $originalVertices;

return $clone;
}

/**
* remove the given edge from list of connected edges (MUST NOT be called manually!)
* Returns a copy of this graph without the given edge
*
* @param Edge $edge
* @return void
* @throws InvalidArgumentException if given edge does not exist (should not ever happen)
* @internal
* @see Edge::destroy() instead!
* If this edge was not found in this graph, the returned graph will be
* identical.
*
* @param Edge $edge
* @return self
*/
public function withoutEdge(Edge $edge)
{
return $this->withoutEdges(new Edges(array($edge)));
}

/**
* Returns a copy of this graph without the given edges
*
* If any of the given edges can not be found in this graph, they will
* silently be ignored. If neither of the edges can be found in this graph,
* the returned graph will be identical.
*
* @param Edges $edges
* @return self
*/
public function removeEdge(Edge $edge)
public function withoutEdges(Edges $edges)
{
$key = \array_search($edge, $this->edges, true);
if ($key === false) {
throw new InvalidArgumentException('Invalid Edge does not exist in this Graph');
// keep copy of original edges and temporarily remove all $edges
$original = $this->edges;
foreach ($edges as $edge) {
if (($key = \array_search($edge, $this->edges, true)) !== false) {
unset($this->edges[$key]);
}
}

// no edges matched => return graph as-is
if (\count($this->edges) === \count($original)) {
return $this;
}

unset($this->edges[$key]);
// clone graph with edges temporarily removed, then restore
$clone = clone $this;
$this->edges = $original;

return $clone;
}

/**
* remove the given vertex from list of known vertices (MUST NOT be called manually!)
* adds a new Vertex to the Graph (MUST NOT be called manually!)
*
* @param Vertex $vertex
* @param Vertex $vertex instance of the new Vertex
* @return void
* @throws InvalidArgumentException if given vertex does not exist (should not ever happen)
* @internal
* @see Vertex::destroy() instead!
* @see self::createVertex() instead!
*/
public function removeVertex(Vertex $vertex)
public function addVertex(Vertex $vertex)
{
$key = \array_search($vertex, $this->vertices, true);
if ($key === false) {
throw new InvalidArgumentException('Invalid Vertex does not exist in this Graph');
}
$this->vertices[] = $vertex;
}

unset($this->vertices[$key]);
/**
* adds a new Edge to the Graph (MUST NOT be called manually!)
*
* @param Edge $edge instance of the new Edge
* @return void
* @internal
* @see Graph::createEdgeUndirected() instead!
*/
public function addEdge(Edge $edge)
{
$this->edges []= $edge;
}

/**
Expand Down
33 changes: 0 additions & 33 deletions src/Vertex.php
Expand Up @@ -3,7 +3,6 @@
namespace Graphp\Graph;

use Graphp\Graph\Exception\BadMethodCallException;
use Graphp\Graph\Exception\InvalidArgumentException;
use Graphp\Graph\Set\Edges;
use Graphp\Graph\Set\EdgesAggregate;
use Graphp\Graph\Set\Vertices;
Expand Down Expand Up @@ -58,24 +57,6 @@ public function addEdge(Edge $edge)
$this->edges[] = $edge;
}

/**
* remove the given edge from list of connected edges (MUST NOT be called manually)
*
* @param Edge $edge
* @return void
* @throws InvalidArgumentException if given edge does not exist
* @internal
* @see Edge::destroy() instead!
*/
public function removeEdge(Edge $edge)
{
$id = \array_search($edge, $this->edges, true);
if ($id === false) {
throw new InvalidArgumentException('Given edge does NOT exist');
}
unset($this->edges[$id]);
}

/**
* check whether this vertex has a direct edge to given $vertex
*
Expand Down Expand Up @@ -256,20 +237,6 @@ public function getVerticesEdgeFrom()
return new Vertices($ret);
}

/**
* destroy vertex and all edges connected to it and remove reference from graph
*
* @uses Edge::destroy()
* @uses Graph::removeVertex()
*/
public function destroy()
{
foreach ($this->getEdges()->getEdgesDistinct() as $edge) {
$edge->destroy();
}
$this->graph->removeVertex($this);
}

/**
* do NOT allow cloning of objects
*
Expand Down
29 changes: 0 additions & 29 deletions src/Walk.php
Expand Up @@ -200,33 +200,4 @@ public function getAlternatingSequence()

return $ret;
}

/**
* check to make sure this walk is still valid (i.e. source graph still contains all vertices and edges)
*
* @return bool
* @uses Walk::getGraph()
* @uses Graph::getVertices()
* @uses Graph::getEdges()
*/
public function isValid()
{
$vertices = \iterator_to_array($this->getGraph()->getVertices(), false);

// check source graph contains all vertices
foreach ($this->getVertices() as $vertex) {
if (!\in_array($vertex, $vertices, true)) {
return false;
}
}
$edges = $this->getGraph()->getEdges()->getVector();
// check source graph contains all edges
foreach ($this->edges as $edge) {
if (!\in_array($edge, $edges, true)) {
return false;
}
}

return true;
}
}
12 changes: 0 additions & 12 deletions tests/EdgeTest.php
Expand Up @@ -104,18 +104,6 @@ public function testLoop()
$this->assertSame($this->v1, $edge->getVertexToFrom($this->v1));
}

public function testRemoveWithLoop()
{
$edge = $this->createEdgeLoop();

$this->assertEquals(array($this->edge, $edge), $this->graph->getEdges()->getVector());

$edge->destroy();

$this->assertEquals(array($this->edge), $this->graph->getEdges()->getVector());
$this->assertEquals(array($this->v1, $this->v2), $this->graph->getVertices()->getVector());
}

protected function createEntity()
{
return $this->createEdge();
Expand Down

0 comments on commit ed01aef

Please sign in to comment.