Skip to content

Commit 9bd6d16

Browse files
committed
Move Walk property algorithms to new WalkProperty algorithm
1 parent e34939b commit 9bd6d16

File tree

1 file changed

+197
-0
lines changed

1 file changed

+197
-0
lines changed

src/Property/WalkProperty.php

Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
<?php
2+
3+
namespace Fhaculty\Graph\Algorithm\Property;
4+
5+
use Fhaculty\Graph\Walk;
6+
use Fhaculty\Graph\Algorithm\Base as BaseAlgorithm;
7+
8+
/**
9+
* Simple algorithms for working with Walk properties
10+
*
11+
* @see GraphProperty
12+
*/
13+
class WalkProperty extends BaseAlgorithm
14+
{
15+
protected $walk;
16+
17+
public function __construct(Walk $walk)
18+
{
19+
$this->walk = $walk;
20+
}
21+
22+
/**
23+
* checks whether walk is a cycle (i.e. source vertex = target vertex)
24+
*
25+
* @return bool
26+
* @link http://en.wikipedia.org/wiki/Cycle_%28graph_theory%29
27+
*/
28+
public function isCycle()
29+
{
30+
$vertices = $this->walk->getVertices();
31+
return (reset($vertices) === end($vertices));
32+
}
33+
34+
/**
35+
* checks whether walk is a path (i.e. does not contain any duplicate edges)
36+
*
37+
* @return bool
38+
* @uses self::hasArrayDuplicates()
39+
*/
40+
public function isPath()
41+
{
42+
return !$this->hasArrayDuplicates($this->walk->getEdgesSequence());
43+
}
44+
45+
/**
46+
* checks whether walk contains a cycle (i.e. contains a duplicate vertex)
47+
*
48+
* a walk that CONTAINS a cycle does not neccessarily have to BE a cycle
49+
*
50+
* @return bool
51+
* @uses self::hasArrayDuplicates()
52+
* @see self::isCycle()
53+
*/
54+
public function hasCycle()
55+
{
56+
return $this->hasArrayDuplicates($this->walk->getVerticesSequence());
57+
}
58+
59+
/**
60+
* checks whether this walk is a loop (single edge connecting vertex A with vertex A again)
61+
*
62+
* @return boolean
63+
* @uses self::isCycle()
64+
*/
65+
public function isLoop()
66+
{
67+
return ($this->walk->getNumberOfEdges() === 1 && $this->isCycle());
68+
}
69+
70+
/**
71+
* checks whether this walk is a digon (a pair of parallel edges in a multigraph or a pair of antiparallel edges in a digraph)
72+
*
73+
* a digon is a cycle connecting exactly two distinct vertices with exactly
74+
* two distinct edges.
75+
*
76+
* @return boolean
77+
* @uses self::hasArrayDuplicates()
78+
* @uses self::isCycle()
79+
*/
80+
public function isDigon()
81+
{
82+
// exactly 2 edges
83+
return ($this->walk->getNumberOfEdges() === 2 &&
84+
// no duplicate edges
85+
!$this->hasArrayDuplicates($this->walk->getEdgesSequence()) &&
86+
// exactly two distinct vertices
87+
$this->walk->getNumberOfVertices() === 2 &&
88+
// this is actually a cycle
89+
$this->isCycle());
90+
}
91+
92+
/**
93+
* checks whether this walk is a triangle (a simple cycle with exactly three distinct vertices)
94+
*
95+
* @return boolean
96+
* @uses self::isCycle()
97+
*/
98+
public function isTriangle()
99+
{
100+
// exactly 3 (implicitly distinct) edges
101+
return ($this->walk->getNumberOfEdges() === 3 &&
102+
// exactly three distinct vertices
103+
$this->walk->getNumberOfVertices() === 3 &&
104+
// this is actually a cycle
105+
$this->isCycle());
106+
}
107+
108+
/**
109+
* check whether this walk is simple
110+
*
111+
* contains no duplicate/repeated vertices (and thus no duplicate edges either)
112+
* other than the starting and ending vertices of cycles.
113+
*
114+
* @return boolean
115+
* @uses self::isCycle()
116+
* @uses self::hasArrayDuplicates()
117+
*/
118+
public function isSimple()
119+
{
120+
$vertices = $this->walk->getVerticesSequence();
121+
// ignore starting vertex for cycles as it's always the same as ending vertex
122+
if ($this->isCycle()) {
123+
unset($vertices[0]);
124+
}
125+
126+
return !$this->hasArrayDuplicates($vertices);
127+
}
128+
129+
/**
130+
* checks whether walk is hamiltonian (i.e. walk over ALL VERTICES of the graph)
131+
*
132+
* @return boolean
133+
* @see self::isEulerian() if you want to check for all EDGES instead of VERTICES
134+
* @uses self::isArrayContentsEqual()
135+
* @link http://en.wikipedia.org/wiki/Hamiltonian_path
136+
*/
137+
public function isHamiltonian()
138+
{
139+
return $this->isArrayContentsEqual($this->walk->getVerticesSequence(), $this->getGraph()->getVertices());
140+
}
141+
142+
/**
143+
* checks whether walk is eulerian (i.e. a walk over ALL EDGES of the graph)
144+
*
145+
* @return boolean
146+
* @see self::isHamiltonian() if you want to check for all VERTICES instead of EDGES
147+
* @uses self::isArrayContentsEqual()
148+
* @link http://en.wikipedia.org/wiki/Eulerian_path
149+
*/
150+
public function isEulerian()
151+
{
152+
return $this->isArrayContentsEqual($this->walk->getEdgesSequence(), $this->getGraph()->getEdges());
153+
}
154+
155+
/**
156+
* checks whether ths given array contains duplicate identical entries
157+
*
158+
* @param array $array
159+
* @return bool
160+
*/
161+
private function hasArrayDuplicates($array)
162+
{
163+
$compare = array();
164+
foreach ($array as $element) {
165+
// duplicate element found
166+
if (in_array($element, $compare, true)) {
167+
return true;
168+
} else {
169+
// add element to temporary array to check for duplicates
170+
$compare [] = $element;
171+
}
172+
}
173+
174+
return false;
175+
}
176+
177+
/**
178+
* checks whether the contents of array a equals those of array b (ignore keys and order but otherwise strict check)
179+
*
180+
* @param array $a
181+
* @param array $b
182+
* @return boolean
183+
*/
184+
private function isArrayContentsEqual($a, $b)
185+
{
186+
foreach ($b as $one) {
187+
$pos = array_search($one, $a, true);
188+
if ($pos === false) {
189+
return false;
190+
} else {
191+
unset($a[$pos]);
192+
}
193+
}
194+
195+
return $a ? false : true;
196+
}
197+
}

0 commit comments

Comments
 (0)