Completed
Branch master (d31b6a)
by Bingo
05:22 queued 01:30
created

GraphUtils::vertexHasPredecessors()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
eloc 1
c 0
b 0
f 0
dl 0
loc 3
ccs 0
cts 3
cp 0
rs 10
cc 1
nc 1
nop 2
crap 2
1
<?php
2
3
namespace graphp\graph;
4
5
use InvalidArgumentException;
6
use graphp\edge\EdgeInterface;
7
use graphp\vertex\VertexInterface;
8
9
/**
10
 * Class GraphUtils
11
 *
12
 * @package graphp\graph
13
 */
14
class GraphUtils
15
{
16
    /**
17
     * Add the speficied source and target vertices to the graph
18
     *
19
     * @param GraphInterface $graph - the graph
20
     * @param VertexInterface $vertex - the source vertex
21
     * @param VertexInterface $targetVertex - the target vertex
22
     * @param float $weight - the weight of the edge being added
23
     *
24
     * @return EdgeInterface
25
     */
26
    public static function addEdgeWithVertices(
27
        GraphInterface $graph,
28
        VertexInterface $sourceVertex,
29
        VertexInterface $targetVertex,
30
        ?float $weight = null
31
    ): EdgeInterface {
32
        $graph->addVertex($sourceVertex);
33
        $graph->addVertex($targetVertex);
34
        return $graph->addEdge($sourceVertex, $targetVertex, $weight);
0 ignored issues
show
Bug introduced by
It seems like $weight can also be of type double; however, parameter $edge of graphp\graph\GraphInterface::addEdge() does only seem to accept graphp\edge\EdgeInterface|null, 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

34
        return $graph->addEdge($sourceVertex, $targetVertex, /** @scrutinizer ignore-type */ $weight);
Loading history...
Bug Best Practice introduced by
The expression return $graph->addEdge($...$targetVertex, $weight) could return the type null which is incompatible with the type-hinted return graphp\edge\EdgeInterface. Consider adding an additional type-check to rule them out.
Loading history...
35
    }
36
37
    /**
38
     * Add all the vertices and all the edges of the source graph to the target graph.
39
     * Return true, if the target graph was changed
40
     *
41
     * @param GraphInterface $targetGraph - the target graph
42
     * @param GraphInterface $sourceGraph - the source graph
43
     *
44
     * @return bool
45
     */
46
    public static function addGraph(GraphInterface $targetGraph, GraphInterface $sourceGraph): bool
47
    {
48
        $modified = self::addAllVertices($targetGraph, $sourceGraph->vertexSet());
0 ignored issues
show
Bug introduced by
$sourceGraph->vertexSet() of type graphp\vertex\VertexSet is incompatible with the type array expected by parameter $vertices of graphp\graph\GraphUtils::addAllVertices(). ( Ignorable by Annotation )

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

48
        $modified = self::addAllVertices($targetGraph, /** @scrutinizer ignore-type */ $sourceGraph->vertexSet());
Loading history...
49
        $modified = $modified || self::addAllEdges($targetGraph, $sourceGraph, $sourceGraph->edgeSet());
0 ignored issues
show
Bug Best Practice introduced by
In this branch, the function will implicitly return null which is incompatible with the type-hinted return boolean. Consider adding a return statement or allowing null as return value.

For hinted functions/methods where all return statements with the correct type are only reachable via conditions, ?null? gets implicitly returned which may be incompatible with the hinted type. Let?s take a look at an example:

interface ReturnsInt {
    public function returnsIntHinted(): int;
}

class MyClass implements ReturnsInt {
    public function returnsIntHinted(): int
    {
        if (foo()) {
            return 123;
        }
        // here: null is implicitly returned
    }
}
Loading history...
Unused Code introduced by
The assignment to $modified is dead and can be removed.
Loading history...
Bug introduced by
$sourceGraph->edgeSet() of type graphp\edge\EdgeSet is incompatible with the type array expected by parameter $edges of graphp\graph\GraphUtils::addAllEdges(). ( Ignorable by Annotation )

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

49
        $modified = $modified || self::addAllEdges($targetGraph, $sourceGraph, /** @scrutinizer ignore-type */ $sourceGraph->edgeSet());
Loading history...
50
    }
51
52
    /**
53
     * Add all vertices to the specified graph.
54
     * Return true, if the graph was modified
55
     *
56
     * @param GraphInterface $graph - the target graph
57
     * @param array $vertices - the vertices
58
     *
59
     * @return bool
60
     */
61
    public static function addAllVertices(GraphInterface $graph, array $vertices = []): bool
62
    {
63
        $modified = false;
64
        foreach ($vertices as $vertex) {
65
            $modified = $modified || $graph->addVertex($vertex);
66
        }
67
        return $modified;
68
    }
69
70
    /**
71
     * Add all edges of the source graph to the target graph.
72
     * Return true, if the graph was modified
73
     *
74
     * @param GraphInterface $targetGraph - the target graph
75
     * @param GraphInterface $sourceGraph - the source graph
76
     * @param array $edges - the edges to add
77
     *
78
     * @return bool
79
     */
80
    public static function addAllEdges(
81
        GraphInterface $targetGraph,
82
        GraphInterface $sourceGraph,
83
        array $edges = []
84
    ): bool {
85
        $modified = false;
86
        foreach ($edges as $edge) {
87
            $sourceVertex = $sourceGraph->getEdgeSource($edge);
88
            $targetVertex = $sourceGraph->getEdgeTarget($edge);
89
            $targetGraph->addVertex($sourceVertex);
90
            $targetGraph->addVertex($targetVertex);
91
            $modified = $modified || $targetGraph->addEdge($sourceVertex, $targetVertex, $edge);
92
        }
93
        return $modified;
94
    }
95
96
    /**
97
     * Add all the vertices and all the edges of the source graph to the target graph in the reversed order
98
     *
99
     * @param GraphInterface $targetGraph - the target graph
100
     * @param GraphInterface $sourceGraph - the source graph
101
     */
102
    public static function addGraphReversed(GraphInterface $targetGraph, GraphInterface $sourceGraph): void
103
    {
104
        if (!$sourceGraph->getType()->isDirected() || !$targetGraph->getType()->isDirected()) {
0 ignored issues
show
Bug introduced by
The method getType() does not exist on graphp\graph\GraphInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to graphp\graph\GraphInterface. ( Ignorable by Annotation )

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

104
        if (!$sourceGraph->/** @scrutinizer ignore-call */ getType()->isDirected() || !$targetGraph->getType()->isDirected()) {
Loading history...
105
            throw new InvalidArgumentException("graph must be directed");
106
        }
107
108
        self::addAllVertices($targetGraph, $sourceGraph->vertexSet());
0 ignored issues
show
Bug introduced by
$sourceGraph->vertexSet() of type graphp\vertex\VertexSet is incompatible with the type array expected by parameter $vertices of graphp\graph\GraphUtils::addAllVertices(). ( Ignorable by Annotation )

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

108
        self::addAllVertices($targetGraph, /** @scrutinizer ignore-type */ $sourceGraph->vertexSet());
Loading history...
109
110
        $edges = $sourceGraph->edgeSet();
111
        foreach ($edges as $edge) {
112
            $targetGraph->addEdge($sourceGraph->getEdgeTarget($edge), $sourceGraph->getEdgeSource($edge));
113
        }
114
    }
115
116
    /**
117
     * Get a list of vertices that are the neighbors of a specified vertex.
118
     *
119
     * @param GraphInterface $graph - the graph
120
     * @param VertexInterface $vertex - the vertex
121
     *
122
     * @return array
123
     */
124
    public static function neighborsOf(GraphInterface $graph, VertexInterface $vertex): array
125
    {
126
        $neighbors = [];
127
        $edges = $graph->edgesOf($vertex);
128
        foreach ($edges as $edge) {
129
            $neighbors[] = self::getOppositeVertex($graph, $edge, $vertex);
130
        }
131
        return $neighbors;
132
    }
133
134
    /**
135
     * Get a list of vertices that are the direct predecessors of a specified vertex.
136
     *
137
     * @param GraphInterface $graph - the graph
138
     * @param VertexInterface $vertex - the vertex
139
     *
140
     * @return array
141
     */
142
    public static function predecessorsOf(GraphInterface $graph, VertexInterface $vertex): array
143
    {
144
        $neighbors = [];
145
        $edges = $graph->incomingEdgesOf($vertex);
146
        foreach ($edges as $edge) {
147
            $neighbors[] = self::getOppositeVertex($graph, $edge, $vertex);
148
        }
149
        return $neighbors;
150
    }
151
152
    /**
153
     * Get a list of vertices that are the direct successors of a specified vertex.
154
     *
155
     * @param GraphInterface $graph - the graph
156
     * @param VertexInterface $vertex - the vertex
157
     *
158
     * @return array
159
     */
160
    public static function successorsOf(GraphInterface $graph, VertexInterface $vertex): array
161
    {
162
        $neighbors = [];
163
        $edges = $graph->outgoingEdgesOf($vertex);
164
        foreach ($edges as $edge) {
165
            $neighbors[] = self::getOppositeVertex($graph, $edge, $vertex);
166
        }
167
        return $neighbors;
168
    }
169
170
    /**
171
     * Test whether an edge is incident to a vertex.
172
     *
173
     * @param GraphInterface $graph - the graph
174
     * @param EdgeInterface $edge - the edge
175
     * @param VertexInterface $vertex - the vertex
176
     *
177
     * @return bool
178
     */
179
    public static function testIncidence(GraphInterface $graph, EdgeInterface $edge, VertexInterface $vertex): bool
180
    {
181
        return $graph->getEdgeSource($edge)->equals($vertex)
182
               || $graph->getEdgeTarget($edge)->equals($vertex);
183
    }
184
185
    /**
186
     * Get the vertex opposite another vertex across an edge.
187
     *
188
     * @param g graph containing e and v
189
     * @param e edge in g
0 ignored issues
show
Bug introduced by
The type graphp\graph\edge was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
190
     * @param v vertex in g
0 ignored issues
show
Bug introduced by
The type graphp\graph\vertex was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
191
     * @param <V> the graph vertex type
192
     * @param <E> the graph edge type
0 ignored issues
show
Bug introduced by
The type graphp\graph\the was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
193
     *
194
     * @return vertex opposite to v across e
195
     *
196
     * @throws InvalidArgumentException
197
     */
198
    public static function getOppositeVertex(
199
        GraphInterface $graph,
200
        EdgeInterface $edge,
201
        VertexInterface $vertex
202
    ): VertexInterface {
203
        $source = $graph->getEdgeSource($edge);
204
        $target = $graph->getEdgeTarget($edge);
205
        if ($vertex->equals($source)) {
206
            return $target;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $target returns the type graphp\vertex\VertexInterface which is incompatible with the documented return type graphp\graph\vertex.
Loading history...
207
        } elseif ($vertex->equals($target)) {
208
            return $source;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $source returns the type graphp\vertex\VertexInterface which is incompatible with the documented return type graphp\graph\vertex.
Loading history...
209
        } else {
210
            throw new InvalidArgumentException("no such vertex: " + $vertex);
211
        }
212
    }
213
214
    /**
215
     * Remove the given vertices from the given graph. If the vertex to be removed has one or more
216
     * predecessors, the predecessors will be connected directly to the successors of the vertex to
217
     * be removed.
218
     *
219
     * @param GraphInterface $graph - the graph
220
     * @param VertexInterface $vertex - the vertex to be removed
221
     * @param mixed $vertices - the remaining vertices
222
     *
223
     * @return bool
224
     */
225
    public static function removeVertexAndPreserveConnectivity(
226
        GraphInterface $graph,
227
        VertexInterface $vertex,
228
        ...$vertices
229
    ): bool {
230
        if (!$graph->containsVertex($vertex)) {
231
            return false;
232
        }
233
234
        $vertices[] = $vertex;
235
        foreach ($vertices as $vertex) {
236
            if (self::vertexHasPredecessors($graph, $vertex)) {
237
                $predecessors = self::predecessorsOf($graph, $vertex);
238
                $successors = self::successorListOf($graph, $vertex);
0 ignored issues
show
Bug introduced by
The method successorListOf() does not exist on graphp\graph\GraphUtils. Did you maybe mean successorsOf()? ( Ignorable by Annotation )

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

238
                /** @scrutinizer ignore-call */ 
239
                $successors = self::successorListOf($graph, $vertex);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
239
240
                foreach ($predecessors as $predecessor) {
241
                    self::addOutgoingEdges($graph, $predecessor, $successors);
242
                }
243
            }
244
            $graph->removeVertex($vertex);
245
        }
246
247
        return true;
248
    }
249
250
    /**
251
     * Add edges from one source vertex to multiple target vertices
252
     *
253
     * @param GraphInterface $graph - the graph
254
     * @param VertexInterface $vertex - the source vertex
255
     * @param mixed $vertices - the target vertices
256
     */
257
    public static function addOutgoingEdges(GraphInterface $graph, VertexInterface $vertex, ...$vertices): void
258
    {
259
        if (!$graph->containsVertex($vertex)) {
260
            $graph->addVertex($vertex);
261
        }
262
        foreach ($vertices as $target) {
263
            if (!$graph->containsVertex($target)) {
264
                $graph->addVertex($target);
265
            }
266
            $graph->addEdge($vertex, $target);
267
        }
268
    }
269
270
    /**
271
     * Add edges from multiple source vertices to one target vertex
272
     *
273
     * @param GraphInterface $graph - the graph
274
     * @param VertexInterface $vertex - the target vertex
275
     * @param mixed $vertices - the source vertices
276
     */
277
    public static function addIncomingEdges(GraphInterface $graph, VertexInterface $vertex, ...$vertices): void
278
    {
279
        if (!$graph->containsVertex($vertex)) {
280
            $graph->addVertex($vertex);
281
        }
282
        foreach ($vertices as $source) {
283
            if (!$graph->containsVertex($source)) {
284
                $graph->addVertex($source);
285
            }
286
            $graph->addEdge($source, $vertex);
287
        }
288
    }
289
290
    /**
291
     * Check if the vertex has direct successors
292
     *
293
     * @param GraphInterface $graph - the graph
294
     * @param VertexInterface $vertex - the vertex
295
     *
296
     * @return bool
297
     */
298
    public static function vertexHasSuccessors(GraphInterface $graph, VertexInterface $vertex): bool
299
    {
300
        return count($graph->outgoingEdgesOf($vertex)) > 0;
301
    }
302
303
    /**
304
     * Check if the vertex has direct predecessors
305
     *
306
     * @param GraphInterface $graph - the graph
307
     * @param VertexInterface $vertex - the vertex
308
     *
309
     * @return bool
310
     */
311
    public static function vertexHasPredecessors(GraphInterface $graph, VertexInterface $vertex): bool
312
    {
313
        return count($graph->incomingEdgesOf($vertex)) > 0;
314
    }
315
}
316