Floydwarshall   A
last analyzed

Complexity

Total Complexity 25

Size/Duplication

Total Lines 196
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 56
c 2
b 0
f 0
dl 0
loc 196
ccs 56
cts 56
cp 1
rs 10
wmc 25

10 Methods

Rating   Name   Duplication   Size   Complexity  
A path() 0 12 2
A populate() 0 15 6
A getDistance() 0 3 2
A process() 0 16 5
A reset() 0 6 1
A __construct() 0 7 3
A getDistances() 0 3 1
A searchPath() 0 8 2
A nodeName() 0 3 2
A getPrecedences() 0 3 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace App\Component\Math\Graph\Path;
6
7
/**
8
 * Weighted graph path finder using Floydwarshall method.
9
 * Constructor expects a weighted square matrix
10
 * and a nodes name collection to indentify nodes by name if required.
11
 *
12
 * @author Pierre Fromager <[email protected]>
13
 */
14
class Floydwarshall
15
{
16
17
    /**
18
     * max nodes
19
     */
20
    const INFINITE = 66666;
21
22
    /**
23
     * Distances array
24
     * @var array
25
     */
26
    private $dist;
27
28
    /**
29
     * Precedence matrix
30
     * @var array
31
     */
32
    private $pred;
33
34
    /**
35
     * Weights array
36
     * @var array
37
     */
38
    private $weights;
39
40
    /**
41
     * Number of nodes
42
     * @var integer
43
     */
44
    private $nodeCount;
45
46
    /**
47
     * Node names list
48
     * @var array
49
     */
50
    private $nodeNames;
51
52
    /**
53
     * path
54
     * @var array
55
     */
56
    private $path;
57
58
    /**
59
     * instanciate
60
     * @param array $weightedMatrix graph weighted square matrice.
61
     * @param array $nodeNames nodes names as array.
62
     */
63 9
    public function __construct(array $weightedMatrix, array $nodeNames = [])
64
    {
65 9
        $this->reset();
66 9
        $this->weights = $weightedMatrix;
67 9
        $this->nodeCount = count($this->weights);
68 9
        if (!empty($nodeNames) && $this->nodeCount == count($nodeNames)) {
69 9
            $this->nodeNames = $nodeNames;
70
        }
71
    }
72
73
    /**
74
     * populate from square matrix then calculate distance and precedence
75
     */
76 6
    public function process(): Floydwarshall
77
    {
78 6
        $this->reset();
79 6
        $this->populate();
80 6
        for ($k = 0; $k < $this->nodeCount; $k++) {
81 6
            for ($i = 0; $i < $this->nodeCount; $i++) {
82 6
                for ($j = 0; $j < $this->nodeCount; $j++) {
83 6
                    $nextDist = $this->dist[$i][$k] + $this->dist[$k][$j];
84 6
                    if ($this->dist[$i][$j] > $nextDist) {
85 6
                        $this->dist[$i][$j] = $nextDist;
86 6
                        $this->pred[$i][$j] = $this->pred[$k][$j];
87
                    }
88
                }
89
            }
90
        }
91 6
        return $this;
92
    }
93
94
    /**
95
     * return path
96
     *
97
     * @param string $src
98
     * @param string $dst
99
     * @param boolean $withNames
100
     * @return array
101
     */
102 1
    public function path(string $src, string $dst, bool $withNames = false): array
103
    {
104 1
        $srcIdx = array_search($src, $this->nodeNames);
105 1
        $dstIdx = array_search($dst, $this->nodeNames);
106 1
        $this->path = [];
107 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

107
        $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

107
        $this->searchPath($srcIdx, /** @scrutinizer ignore-type */ $dstIdx);
Loading history...
108 1
        $path = ($withNames)
109
            ? array_map(function ($v) {
110 1
                return $this->nodeNames[$v];
111 1
            }, $this->path)
112 1
            : $this->path;
113 1
        return $path;
114
    }
115
116
    /**
117
     * recursive search for node path
118
     *
119
     * @param integer $i
120
     * @param integer $j
121
     * @return Floydwarshall
122
     */
123 2
    protected function searchPath(int $i, int $j): Floydwarshall
124
    {
125 2
        if ($i != $j) {
126 2
            $pred = $this->pred[$i][$j];
127 2
            $this->searchPath($i, $pred);
128
        }
129 2
        $this->path[] = $j;
130 2
        return $this;
131
    }
132
133
    /**
134
     * return distance matrix
135
     *
136
     * @return array
137
     */
138 1
    public function getDistances(): array
139
    {
140 1
        return $this->dist;
141
    }
142
143
    /**
144
     * return node name from row integer
145
     *
146
     * @param integer $i
147
     * @return string
148
     */
149 1
    public function nodeName(int $i): string
150
    {
151 1
        return (isset($this->nodeNames[$i])) ? $this->nodeNames[$i] : '';
152
    }
153
154
    /**
155
     * return distance between two nodes identified by row/col couple.
156
     * if one member of couple is undefined infinite value is returned.
157
     *
158
     * @return float
159
     */
160 1
    public function getDistance(int $i, int $j): float
161
    {
162 1
        return (isset($this->dist[$i][$j])) ? $this->dist[$i][$j] : self::INFINITE;
163
    }
164
165
    /**
166
     * return precedence matrix
167
     *
168
     * @return array
169
     */
170 1
    public function getPrecedences(): array
171
    {
172 1
        return $this->pred;
173
    }
174
175
    /**
176
     * reset
177
     *
178
     * @return Floydwarshall
179
     */
180 1
    protected function reset(): Floydwarshall
181
    {
182 1
        $this->path = [];
183 1
        $this->dist = [[]];
184 1
        $this->pred = [[]];
185 1
        return $this;
186
    }
187
188
    /**
189
     * populate graph nodes to
190
     * set distances matrix from weights
191
     * and set matrix precedences
192
     *
193
     * @return Floydwarshall
194
     */
195 2
    protected function populate(): Floydwarshall
196
    {
197 2
        for ($i = 0; $i < $this->nodeCount; $i++) {
198 2
            for ($j = 0; $j < $this->nodeCount; $j++) {
199 2
                if ($i == $j) {
200 2
                    $this->dist[$i][$j] = 0;
201 2
                } elseif (isset($this->weights[$i][$j]) && $this->weights[$i][$j] > 0) {
202 2
                    $this->dist[$i][$j] = $this->weights[$i][$j];
203
                } else {
204 2
                    $this->dist[$i][$j] = self::INFINITE;
205
                }
206 2
                $this->pred[$i][$j] = $i;
207
            }
208
        }
209 2
        return $this;
210
    }
211
}
212