DirectedCycle::__construct()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 18
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 8
nc 3
nop 1
dl 0
loc 18
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace TemplesOfCode\NodesAndEdges\DFS;
4
5
use TemplesOfCode\NodesAndEdges\Graph;
6
7
/**
8
 * Class DirectedCycle
9
 * @package TemplesOfCode\NodesAndEdges\DFS
10
 */
11
class DirectedCycle extends ConnectedComponent
12
{
13
14
    /**
15
     * @var array
16
     */
17
    protected $onStack;
18
19
    /**
20
     * @var array
21
     */
22
    protected $cycle;
23
24
    /**
25
     * @var int[]
26
     */
27
    private $edgeTo;
28
29
    /**
30
     * DirectedCycle constructor.
31
     * @param Graph $graph
32
     */
33
    public function __construct(Graph $graph)
34
    {
35
        // set
36
        $vertices = $graph->getVertices();
37
        // set
38
        $this->onStack = array_fill(0, $vertices, false);
39
        // init
40
        $this->cycle = null;
41
        // set
42
        $this->graph = $graph;
43
        // set
44
        $this->marked = array_fill(0, $vertices, false);
45
        // iterate over the vertices
46
        for ($vertex = 0; $vertex < $vertices; $vertex++) {
47
            // check for visit
48
            if (!$this->marked[$vertex]) {
49
                // execute DFS logic
50
                $this->dfs($vertex);
51
            }
52
        }
53
    }
54
55
    /**
56
     * @param int $vertex
57
     */
58
    protected function dfs(int $vertex)
59
    {
60
        // mark the visit
61
        $this->marked[$vertex] = true;
62
        // set stack presence
63
        $this->onStack[$vertex] = true;
64
        // get neighbors
65
        $neighbors = $this->graph->adjacent($vertex);
66
        // iterate over the set
67
        foreach ($neighbors as $w) {
68
            // check for cycles
69
            if ($this->hasCycle()) {
70
                // cycle detected
71
                return;
72
            } elseif (!$this->marked[$w]) {
73
                // set
74
                $this->edgeTo[$w] = $vertex;
75
                // we have not visited yet
76
                $this->dfs($w);
77
            } elseif ($this->onStack[$w]) {
78
                // we are currently in a path that has visited $w
79
                $this->cycle = [];
80
                // iterate over the path
81
                for ($x = $vertex; $x != $w; $x = $this->edgeTo[$x]) {
82
                    // add to the end of the list
83
                    array_unshift($this->cycle, $x);
84
                }
85
                // stack the neighbor
86
                array_unshift($this->cycle, $w);
87
                // stack the vertex
88
                array_unshift($this->cycle, $vertex);
89
            }
90
        }
91
        // remove the stack presence
92
        $this->onStack[$vertex] = false;
93
    }
94
95
    /**
96
     * @return bool
97
     */
98
    public function hasCycle()
99
    {
100
        return !is_null($this->cycle);
101
    }
102
103
    /**
104
     * @return array|null
105
     */
106
    public function cycle()
107
    {
108
        return $this->cycle;
109
    }
110
}