Passed
Push — master ( 411d0d...8dd355 )
by Pierre
03:33
created

Floydwarshall::path()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 12
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 10
c 0
b 0
f 0
nc 2
nop 3
dl 0
loc 12
ccs 11
cts 11
cp 1
crap 2
rs 9.9332
1
<?php
2
3
namespace App\Component\Math\Graph\Path;
4
5
/**
6
 * Weighted graph path finder using Floydwarshall method
7
 * @author Pierre Fromager <[email protected]>
8
 */
9
class Floydwarshall
10
{
11
12
    /**
13
     * max nodes
14
     */
15
    const INFINITE = 66666;
16
17
    /**
18
     * Distances array
19
     * @var array
20
     */
21
    protected $dist;
22
23
    /**
24
     * Precedence matrix
25
     * @var array
26
     */
27
    protected $pred;
28
29
    /**
30
     * Weights array
31
     * @var array
32
     */
33
    protected $weights;
34
35
    /**
36
     * Number of nodes
37
     * @var integer
38
     */
39
    protected $nodeCount;
40
41
    /**
42
     * Node names list
43
     * @var array
44
     */
45
    protected $nodenames;
46
47
    /**
48
     * path.
49
     * @var array
50
     */
51
    protected $path;
52
53
    /**
54
     * instanciate
55
     * @param array $weightedMatrix graph weighted square matrice.
56
     * @param array $nodenames nodes names as array.
57
     */
58 7
    public function __construct(array $weightedMatrix, array $nodenames = [])
59
    {
60 7
        $this->reset();
61 7
        $this->weights = $weightedMatrix;
62 7
        $this->nodeCount = count($this->weights);
63 7
        if (!empty($nodenames) && $this->nodeCount == count($nodenames)) {
64 7
            $this->nodenames = $nodenames;
65
        }
66
    }
67
68
    /**
69
     * populate from square matrix then calculate distance and precedence
70
     */
71 5
    public function process(): Floydwarshall
72
    {
73 5
        $this->reset();
74 5
        $this->populate();
75 5
        for ($k = 0; $k < $this->nodeCount; $k++) {
76 5
            for ($i = 0; $i < $this->nodeCount; $i++) {
77 5
                for ($j = 0; $j < $this->nodeCount; $j++) {
78 5
                    $nextDist = $this->dist[$i][$k] + $this->dist[$k][$j];
79 5
                    if ($this->dist[$i][$j] > $nextDist) {
80 5
                        $this->dist[$i][$j] = $nextDist;
81 5
                        $this->pred[$i][$j] = $this->pred[$k][$j];
82
                    }
83
                }
84
            }
85
        }
86 5
        return $this;
87
    }
88
89
    /**
90
     * return path
91
     *
92
     * @param string $src
93
     * @param string $dst
94
     * @param boolean $withNames
95
     * @return array
96
     */
97 1
    public function path(string $src, string $dst, bool $withNames = false): array
98
    {
99 1
        $srcIdx = array_search($src, $this->nodenames);
100 1
        $dstIdx = array_search($dst, $this->nodenames);
101 1
        $this->path = [];
102 1
        $this->searchPath($srcIdx, $dstIdx);
0 ignored issues
show
Bug introduced by
It seems like $srcIdx can also be of type false and string; however, parameter $i of App\Component\Math\Graph...dwarshall::searchPath() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

102
        $this->searchPath(/** @scrutinizer ignore-type */ $srcIdx, $dstIdx);
Loading history...
Bug introduced by
It seems like $dstIdx can also be of type false and string; however, parameter $j of App\Component\Math\Graph...dwarshall::searchPath() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

102
        $this->searchPath($srcIdx, /** @scrutinizer ignore-type */ $dstIdx);
Loading history...
103 1
        $path = ($withNames)
104 1
            ? array_map(function ($v) {
105 1
                return $this->nodenames[$v];
106 1
            }, $this->path)
107 1
            : $this->path;
108 1
        return $path;
109
    }
110
111
    /**
112
     * recursive search for node path
113
     *
114
     * @param integer $i
115
     * @param integer $j
116
     * @return Floydwarshall
117
     */
118 2
    protected function searchPath(int $i, int $j): Floydwarshall
119
    {
120 2
        if ($i != $j) {
121 2
            $pred = $this->pred[$i][$j];
122 2
            $this->searchPath($i, $pred);
123
        }
124 2
        $this->path[] = $j;
125 2
        return $this;
126
    }
127
128
    /**
129
     * return distance matrix
130
     *
131
     * @return array
132
     */
133 1
    public function getDistances(): array
134
    {
135 1
        return $this->dist;
136
    }
137
138
    /**
139
     * return precedence matrix
140
     *
141
     * @return array
142
     */
143 1
    public function getPrecedences(): array
144
    {
145 1
        return $this->pred;
146
    }
147
148
    /**
149
     * reset
150
     *
151
     * @return Floydwarshall
152
     */
153 1
    protected function reset(): Floydwarshall
154
    {
155 1
        $this->path = [];
156 1
        $this->dist = [[]];
157 1
        $this->pred = [[]];
158 1
        return $this;
159
    }
160
161
    /**
162
     * populate graph nodes to
163
     * set distances matrix from weights
164
     * and set matrix precedences
165
     *
166
     * @return Floydwarshall
167
     */
168 2
    protected function populate(): Floydwarshall
169
    {
170 2
        for ($i = 0; $i < $this->nodeCount; $i++) {
171 2
            for ($j = 0; $j < $this->nodeCount; $j++) {
172 2
                if ($i == $j) {
173 2
                    $this->dist[$i][$j] = 0;
174 2
                } elseif (isset($this->weights[$i][$j]) && $this->weights[$i][$j] > 0) {
175 2
                    $this->dist[$i][$j] = $this->weights[$i][$j];
176
                } else {
177 2
                    $this->dist[$i][$j] = self::INFINITE;
178
                }
179 2
                $this->pred[$i][$j] = $i;
180
            }
181
        }
182 2
        return $this;
183
    }
184
}
185