src/Graph/UndirectedGraph.php
<?php
/**
* Undirected graph.
*/
namespace GraphDS\Graph;
use GraphDS\Vertex\UndirectedVertex;
use GraphDS\Edge\UndirectedEdge;
use InvalidArgumentException;
/**
* Class defining an undirected graph object.
*
* @property UndirectedVertex[] $vertices
* @property UndirectedEdge[][] $edges
*/
class UndirectedGraph extends Graph
{
/**
* Constructor for UndirectedGraph object.
*/
public function __construct()
{
$this->directed = false;
}
/**
* Adds an undirected vertex to the graph.
*
* @param string $vertex ID of the vertex
*/
public function addVertex($vertex)
{
if (empty($this->vertices[$vertex])) {
$this->vertices[$vertex] = new UndirectedVertex();
}
}
/**
* Removes an undirected vertex from the graph.
*
* @param string $vertex ID of the vertex
*
* @throws InvalidArgumentException
*/
public function removeVertex($vertex)
{
if (empty($this->vertices[$vertex])) {
throw new InvalidArgumentException("Vertex $vertex does not exist.");
}
$neighbors = $this->vertices[$vertex]->getNeighbors();
foreach ($neighbors as $neighbor) {
if (($key = array_search($vertex, $this->vertices[$neighbor]->neighbors)) !== false) {
unset($this->vertices[$neighbor]->neighbors[$key]);
}
if ($this->edge($neighbor, $vertex)) {
$this->removeEdge($neighbor, $vertex);
}
}
unset($this->edges[$vertex], $this->vertices[$vertex]);
}
/**
* Returns an edge object in the graph, regardless of vertex order (undirected).
*
* @param string $vertex1 ID of first vertex
* @param string $vertex2 ID of second vertex
*
* @return UndirectedEdge|null Instance of UndirectedEdge between $vertex1 and $vertex2 or null if no edge exists
*
* @throws InvalidArgumentException
*/
public function edge($vertex1, $vertex2)
{
if (empty($this->vertices[$vertex1]) || empty($this->vertices[$vertex2])) {
throw new InvalidArgumentException('One of the vertices does not exist.');
}
if (isset($this->edges[$vertex1][$vertex2])) {
return $this->edges[$vertex1][$vertex2];
} elseif (isset($this->edges[$vertex2][$vertex1])) {
return $this->edges[$vertex2][$vertex1];
}
return null;
}
/**
* Adds an undirected edge between two undirected vertices.
*
* @param string $vertex1 ID of first vertex
* @param string $vertex2 ID of second vertex
* @param float $value The value/weight the edge should hold
*
* @throws InvalidArgumentException
*/
public function addEdge($vertex1, $vertex2, $value = null)
{
if ($vertex1 === $vertex2) {
throw new InvalidArgumentException('Cannot connect vertex to itself.');
}
if (empty($this->vertices[$vertex1]) || empty($this->vertices[$vertex2])) {
throw new InvalidArgumentException('One of the vertices does not exist.');
}
if (null === $this->edge($vertex1, $vertex2)) {
$this->edges[$vertex1][$vertex2] = new UndirectedEdge($vertex1, $vertex2, $value);
$this->vertices[$vertex1]->addNeighbor($vertex2);
$this->vertices[$vertex2]->addNeighbor($vertex1);
}
}
/**
* Removes an undirected edge between two undirected vertices.
*
* @param string $vertex1 ID of first undirected vertex
* @param string $vertex2 ID of second undirected vertex
*
* @throws InvalidArgumentException
*/
public function removeEdge($vertex1, $vertex2)
{
if (empty($this->vertices[$vertex1]) || empty($this->vertices[$vertex2])) {
throw new InvalidArgumentException('One of the vertices does not exist.');
}
if (null === $this->edge($vertex1, $vertex2)) {
throw new InvalidArgumentException("No edge between $vertex1 and $vertex2.");
}
$this->vertices[$vertex1]->removeNeighbor($vertex2);
$this->vertices[$vertex2]->removeNeighbor($vertex1);
if (isset($this->edges[$vertex1][$vertex2])) {
unset($this->edges[$vertex1][$vertex2]);
if (empty($this->edges[$vertex1])) {
unset($this->edges[$vertex1]);
}
}
if (isset($this->edges[$vertex2][$vertex1])) {
unset($this->edges[$vertex2][$vertex1]);
if (empty($this->edges[$vertex2])) {
unset($this->edges[$vertex2]);
}
}
}
}