Completed
Push — master ( 8dd355...6a91d0 )
by Pierre
03:39
created

Floydwarshall::getDistance()   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 2
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 8
    public function __construct(array $weightedMatrix, array $nodenames = [])
62
    {
63 8
        $this->reset();
64 8
        $this->weights = $weightedMatrix;
65 8
        $this->nodeCount = count($this->weights);
66 8
        if (!empty($nodenames) && $this->nodeCount == count($nodenames)) {
67 8
            $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 distance between two nodes identified by row/col couple.
143
     * if one member of couple is undefined infinite value is returned.
144
     *
145
     * @return float
146
     */
147 1
    public function getDistance(int $i, int $j): float
148
    {
149 1
        return (isset($this->dist[$i][$j])) ? $this->dist[$i][$j] : self::INFINITE;
150
    }
151
152
    /**
153
     * return precedence matrix
154
     *
155
     * @return array
156
     */
157 1
    public function getPrecedences(): array
158
    {
159 1
        return $this->pred;
160
    }
161
162
    /**
163
     * reset
164
     *
165
     * @return Floydwarshall
166
     */
167 1
    protected function reset(): Floydwarshall
168
    {
169 1
        $this->path = [];
170 1
        $this->dist = [[]];
171 1
        $this->pred = [[]];
172 1
        return $this;
173
    }
174
175
    /**
176
     * populate graph nodes to
177
     * set distances matrix from weights
178
     * and set matrix precedences
179
     *
180
     * @return Floydwarshall
181
     */
182 2
    protected function populate(): Floydwarshall
183
    {
184 2
        for ($i = 0; $i < $this->nodeCount; $i++) {
185 2
            for ($j = 0; $j < $this->nodeCount; $j++) {
186 2
                if ($i == $j) {
187 2
                    $this->dist[$i][$j] = 0;
188 2
                } elseif (isset($this->weights[$i][$j]) && $this->weights[$i][$j] > 0) {
189 2
                    $this->dist[$i][$j] = $this->weights[$i][$j];
190
                } else {
191 2
                    $this->dist[$i][$j] = self::INFINITE;
192
                }
193 2
                $this->pred[$i][$j] = $i;
194
            }
195
        }
196 2
        return $this;
197
    }
198
}
199