Completed
Branch master (7ef6dd)
by Bingo
07:42 queued 03:47
created

GraphUtils::addAllEdges()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 14
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 3

Importance

Changes 0
Metric Value
eloc 8
c 0
b 0
f 0
dl 0
loc 14
ccs 9
cts 9
cp 1
rs 10
cc 3
nc 3
nop 3
crap 3
1
<?php
2
3
namespace graphp\graph;
4
5
use InvalidArgumentException;
6
use graphp\edge\EdgeInterface;
7
use graphp\edge\EdgeSet;
8
use graphp\vertex\VertexInterface;
9
use graphp\vertex\VertexSet;
10
11
/**
12
 * Class GraphUtils
13
 *
14
 * @package graphp\graph
15
 */
16
class GraphUtils
17
{
18
    /**
19
     * Create a new edge and add it to the specified graph
20
     *
21
     * @param GraphInterface $graph - the graph
22
     * @param VertexInterface $sourceVertex - the source vertex
23
     * @param VertexInterface $targetVertex - the target vertex
24
     * @param float $weight - the weight of the edge being added
25
     *
26
     * @return EdgeInterface
27
     */
28 3
    public static function addEdge(
29
        GraphInterface $graph,
30
        VertexInterface $sourceVertex,
31
        VertexInterface $targetVertex,
32
        ?float $weight = null
33
    ): ?EdgeInterface {
34 3
        $edgeSupplier = $graph->getEdgeSupplier();
35 3
        if (is_null($edgeSupplier)) {
36
            throw new Exception("Graph contains no edge supplier");
0 ignored issues
show
Bug introduced by
The type graphp\graph\Exception was not found. Did you mean Exception? If so, make sure to prefix the type with \.
Loading history...
37
        }
38 3
        $edge = $edgeSupplier->get();
39
40 3
        if ($graph->addEdge($sourceVertex, $targetVertex, $edge)) {
41 3
            if ($graph->getType()->isWeighted()) {
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

41
            if ($graph->/** @scrutinizer ignore-call */ getType()->isWeighted()) {
Loading history...
42
                $graph->setEdgeWeight($edge, $weight);
0 ignored issues
show
Bug introduced by
It seems like $weight can also be of type null; however, parameter $weight of graphp\graph\GraphInterface::setEdgeWeight() does only seem to accept double, 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

42
                $graph->setEdgeWeight($edge, /** @scrutinizer ignore-type */ $weight);
Loading history...
43
            }
44 3
            return $edge;
45
        }
46 1
        return null;
47
    }
48
49
    /**
50
     * Add the speficied source and target vertices to the graph
51
     *
52
     * @param GraphInterface $graph - the graph
53
     * @param VertexInterface $sourceVertex - the source vertex
54
     * @param VertexInterface $targetVertex - the target vertex
55
     * @param float $weight - the weight of the edge being added
56
     *
57
     * @return EdgeInterface
58
     */
59 3
    public static function addEdgeWithVertices(
60
        GraphInterface $graph,
61
        VertexInterface $sourceVertex,
62
        VertexInterface $targetVertex,
63
        ?float $weight = null
64
    ): EdgeInterface {
65 3
        $graph->addVertex($sourceVertex);
66 3
        $graph->addVertex($targetVertex);
67 3
        return self::addEdge($graph, $sourceVertex, $targetVertex, $weight);
0 ignored issues
show
Bug Best Practice introduced by
The expression return self::addEdge($gr...$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...
68
    }
69
70
    /**
71
     * Add all the vertices and all the edges of the source graph to the target graph.
72
     * Return true, if the target graph was changed
73
     *
74
     * @param GraphInterface $targetGraph - the target graph
75
     * @param GraphInterface $sourceGraph - the source graph
76
     *
77
     * @return bool
78
     */
79 1
    public static function addGraph(GraphInterface $targetGraph, GraphInterface $sourceGraph): bool
80
    {
81 1
        $modified = self::addAllVertices($targetGraph, $sourceGraph->vertexSet());
82 1
        return self::addAllEdges($targetGraph, $sourceGraph, $sourceGraph->edgeSet()) || $modified;
83
    }
84
85
    /**
86
     * Add all vertices to the specified graph.
87
     * Return true, if the graph was modified
88
     *
89
     * @param GraphInterface $graph - the target graph
90
     * @param VertexSet $vertices - the vertices
91
     *
92
     * @return bool
93
     */
94 2
    public static function addAllVertices(GraphInterface $graph, VertexSet $vertices): bool
95
    {
96 2
        $modified = false;
97 2
        foreach ($vertices as $vertex) {
98 2
            $modified = $graph->addVertex($vertex) || $modified;
99
        }
100 2
        return $modified;
101
    }
102
103
    /**
104
     * Add all edges of the source graph to the target graph.
105
     * Return true, if the graph was modified
106
     *
107
     * @param GraphInterface $targetGraph - the target graph
108
     * @param GraphInterface $sourceGraph - the source graph
109
     * @param EdgeSet $edges - the edges to add
110
     *
111
     * @return bool
112
     */
113 1
    public static function addAllEdges(
114
        GraphInterface $targetGraph,
115
        GraphInterface $sourceGraph,
116
        EdgeSet $edges
117
    ): bool {
118 1
        $modified = false;
119 1
        foreach ($edges as $edge) {
120 1
            $sourceVertex = $sourceGraph->getEdgeSource($edge);
121 1
            $targetVertex = $sourceGraph->getEdgeTarget($edge);
122 1
            $targetGraph->addVertex($sourceVertex);
123 1
            $targetGraph->addVertex($targetVertex);
124 1
            $modified = $targetGraph->addEdge($sourceVertex, $targetVertex, $edge) || $modified;
125
        }
126 1
        return $modified;
127
    }
128
129
    /**
130
     * Add all the vertices and all the edges of the source graph to the target graph in the reversed order
131
     *
132
     * @param GraphInterface $targetGraph - the target graph
133
     * @param GraphInterface $sourceGraph - the source graph
134
     */
135 1
    public static function addGraphReversed(GraphInterface $targetGraph, GraphInterface $sourceGraph): void
136
    {
137 1
        if (!$sourceGraph->getType()->isDirected() || !$targetGraph->getType()->isDirected()) {
138
            throw new InvalidArgumentException("graph must be directed");
139
        }
140
141 1
        self::addAllVertices($targetGraph, $sourceGraph->vertexSet());
142
143 1
        $edges = $sourceGraph->edgeSet();
144 1
        foreach ($edges as $edge) {
145 1
            $targetGraph->addEdge($sourceGraph->getEdgeTarget($edge), $sourceGraph->getEdgeSource($edge));
146
        }
147 1
    }
148
149
    /**
150
     * Get a list of vertices that are the neighbors of a specified vertex.
151
     *
152
     * @param GraphInterface $graph - the graph
153
     * @param VertexInterface $vertex - the vertex
154
     *
155
     * @return array
156
     */
157 2
    public static function neighborsOf(GraphInterface $graph, VertexInterface $vertex): array
158
    {
159 2
        $neighbors = [];
160 2
        $edges = $graph->edgesOf($vertex);
161 2
        foreach ($edges as $edge) {
162 2
            $neighbors[] = self::getOppositeVertex($graph, $edge, $vertex);
163
        }
164 2
        return $neighbors;
165
    }
166
167
    /**
168
     * Get a list of vertices that are the direct predecessors of a specified vertex.
169
     *
170
     * @param GraphInterface $graph - the graph
171
     * @param VertexInterface $vertex - the vertex
172
     *
173
     * @return array
174
     */
175 2
    public static function predecessorsOf(GraphInterface $graph, VertexInterface $vertex): array
176
    {
177 2
        $neighbors = [];
178 2
        $edges = $graph->incomingEdgesOf($vertex);
179 2
        foreach ($edges as $edge) {
180 2
            $neighbors[] = self::getOppositeVertex($graph, $edge, $vertex);
181
        }
182 2
        return $neighbors;
183
    }
184
185
    /**
186
     * Get a list of vertices that are the direct successors of a specified vertex.
187
     *
188
     * @param GraphInterface $graph - the graph
189
     * @param VertexInterface $vertex - the vertex
190
     *
191
     * @return array
192
     */
193 1
    public static function successorsOf(GraphInterface $graph, VertexInterface $vertex): array
194
    {
195 1
        $neighbors = [];
196 1
        $edges = $graph->outgoingEdgesOf($vertex);
197 1
        foreach ($edges as $edge) {
198 1
            $neighbors[] = self::getOppositeVertex($graph, $edge, $vertex);
199
        }
200 1
        return $neighbors;
201
    }
202
203
    /**
204
     * Test whether an edge is incident to a vertex.
205
     *
206
     * @param GraphInterface $graph - the graph
207
     * @param EdgeInterface $edge - the edge
208
     * @param VertexInterface $vertex - the vertex
209
     *
210
     * @return bool
211
     */
212 1
    public static function testIncidence(GraphInterface $graph, EdgeInterface $edge, VertexInterface $vertex): bool
213
    {
214 1
        return $graph->getEdgeSource($edge)->equals($vertex)
215 1
               || $graph->getEdgeTarget($edge)->equals($vertex);
216
    }
217
218
    /**
219
     * Get the vertex opposite another vertex across an edge.
220
     *
221
     * @param g graph containing e and v
222
     * @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...
223
     * @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...
224
     * @param <V> the graph vertex type
225
     * @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...
226
     *
227
     * @return vertex opposite to v across e
228
     *
229
     * @throws InvalidArgumentException
230
     */
231 4
    public static function getOppositeVertex(
232
        GraphInterface $graph,
233
        EdgeInterface $edge,
234
        VertexInterface $vertex
235
    ): VertexInterface {
236 4
        $source = $graph->getEdgeSource($edge);
237 4
        $target = $graph->getEdgeTarget($edge);
238 4
        if ($vertex->equals($source)) {
239 4
            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...
240 4
        } elseif ($vertex->equals($target)) {
241 4
            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...
242
        } else {
243
            throw new InvalidArgumentException("no such vertex: " + $vertex);
244
        }
245
    }
246
247
    /**
248
     * Remove the given vertices from the given graph. If the vertex to be removed has one or more
249
     * predecessors, the predecessors will be connected directly to the successors of the vertex to
250
     * be removed.
251
     *
252
     * @param GraphInterface $graph - the graph
253
     * @param VertexInterface $vertex - the vertex to be removed
254
     * @param mixed $vertices - the remaining vertices
255
     *
256
     * @return bool
257
     */
258 1
    public static function removeVertexAndPreserveConnectivity(
259
        GraphInterface $graph,
260
        VertexInterface $vertex,
261
        ...$vertices
262
    ): bool {
263 1
        if (!$graph->containsVertex($vertex)) {
264
            return false;
265
        }
266
267 1
        $vertices[] = $vertex;
268 1
        foreach ($vertices as $vertex) {
269 1
            if (self::vertexHasPredecessors($graph, $vertex)) {
270 1
                $predecessors = self::predecessorsOf($graph, $vertex);
271 1
                $successors = self::successorsOf($graph, $vertex);
272
273 1
                foreach ($predecessors as $predecessor) {
274 1
                    self::addOutgoingEdges($graph, $predecessor, ...$successors);
275
                }
276
            }
277 1
            $graph->removeVertex($vertex);
278
        }
279
280 1
        return true;
281
    }
282
283
    /**
284
     * Add edges from one source vertex to multiple target vertices
285
     *
286
     * @param GraphInterface $graph - the graph
287
     * @param VertexInterface $vertex - the source vertex
288
     * @param mixed $vertices - the target vertices
289
     */
290 2
    public static function addOutgoingEdges(GraphInterface $graph, VertexInterface $vertex, ...$vertices): void
291
    {
292 2
        if (!$graph->containsVertex($vertex)) {
293
            $graph->addVertex($vertex);
294
        }
295 2
        foreach ($vertices as $target) {
296 2
            if (!$graph->containsVertex($target)) {
297 1
                $graph->addVertex($target);
298
            }
299 2
            $graph->addEdge($vertex, $target);
300
        }
301 2
    }
302
303
    /**
304
     * Add edges from multiple source vertices to one target vertex
305
     *
306
     * @param GraphInterface $graph - the graph
307
     * @param VertexInterface $vertex - the target vertex
308
     * @param mixed $vertices - the source vertices
309
     */
310
    public static function addIncomingEdges(GraphInterface $graph, VertexInterface $vertex, ...$vertices): void
311
    {
312
        if (!$graph->containsVertex($vertex)) {
313
            $graph->addVertex($vertex);
314
        }
315
        foreach ($vertices as $source) {
316
            if (!$graph->containsVertex($source)) {
317
                $graph->addVertex($source);
318
            }
319
            $graph->addEdge($source, $vertex);
320
        }
321
    }
322
323
    /**
324
     * Check if the vertex has direct successors
325
     *
326
     * @param GraphInterface $graph - the graph
327
     * @param VertexInterface $vertex - the vertex
328
     *
329
     * @return bool
330
     */
331 1
    public static function vertexHasSuccessors(GraphInterface $graph, VertexInterface $vertex): bool
332
    {
333 1
        return count($graph->outgoingEdgesOf($vertex)) > 0;
334
    }
335
336
    /**
337
     * Check if the vertex has direct predecessors
338
     *
339
     * @param GraphInterface $graph - the graph
340
     * @param VertexInterface $vertex - the vertex
341
     *
342
     * @return bool
343
     */
344 2
    public static function vertexHasPredecessors(GraphInterface $graph, VertexInterface $vertex): bool
345
    {
346 2
        return count($graph->incomingEdgesOf($vertex)) > 0;
347
    }
348
}
349