Completed
Push — master ( e936c5...79aed4 )
by Pierre
04:11
created

Floydwarshall::nodeName()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 2

Importance

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

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

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