Completed
Branch master (66b2c3)
by Bingo
07:00 queued 02:56
created

AbstractGraph::removeVertex()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 2

Importance

Changes 0
Metric Value
eloc 6
c 0
b 0
f 0
dl 0
loc 9
ccs 7
cts 7
cp 1
rs 10
cc 2
nc 2
nop 1
crap 2
1
<?php
2
3
namespace graphp\graph;
4
5
use InvalidArgumentException;
6
use graphp\graph\specifics\SpecificsInterface;
7
use graphp\graph\specifics\UndirectedSpecifics;
8
use graphp\graph\specifics\DirectedSpecifics;
9
use graphp\edge\EdgeInterface;
10
use graphp\edge\EdgeSet;
11
use graphp\edge\specifics\EdgeSpecificsInterface;
12
use graphp\edge\specifics\UniformEdgeSpecifics;
13
use graphp\edge\specifics\WeightedEdgeSpecifics;
14
use graphp\vertex\VertexInterface;
15
use graphp\vertex\VertexSet;
16
use graphp\util\SupplierInterface;
17
18
/**
19
 * Class AbstractGraph
20
 *
21
 * @package graphp\graph
22
 */
23
class AbstractGraph implements GraphInterface
24
{
25
    /**
26
     * The vertex supplier
27
     *
28
     * @var SupplierInterface
29
     */
30
    private $vertexSupplier;
31
32
    /**
33
     * The edge supplier
34
     *
35
     * @var SupplierInterface
36
     */
37
    private $edgeSupplier;
38
39
    /**
40
     * The graph type
41
     *
42
     * @var GraphTypeInterface
43
     */
44
    private $type;
45
46
    /**
47
     * The graph specifics
48
     *
49
     * @var SpecificsInterface
50
     */
51
    private $specifics;
52
53
    /**
54
     * The edge specifics
55
     *
56
     * @var EdgeSpecificsInterface
57
     */
58
    private $edgeSpecifics;
59
60
    /**
61
     * Construct a new graph
62
     *
63
     * @param SupplierInterface $vertexSupplier - the vertex supplier
64
     * @param SupplierInterface $edgeSupplier - the edge supplier
65
     * @param GraphTypeInterface $type - the graph type
66
     */
67 14
    protected function __construct(
68
        ?SupplierInterface $vertexSupplier = null,
69
        ?SupplierInterface $edgeSupplier = null,
70
        ?GraphTypeInterface $type = null
71
    ) {
72 14
        $this->vertexSupplier = $vertexSupplier;
73 14
        $this->edgeSupplier = $edgeSupplier;
74 14
        $this->type = $type;
75 14
        $this->specifics = $this->createSpecifics($type->isDirected());
0 ignored issues
show
Bug introduced by
The method isDirected() does not exist on null. ( Ignorable by Annotation )

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

75
        $this->specifics = $this->createSpecifics($type->/** @scrutinizer ignore-call */ isDirected());

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...
76 14
        $this->edgeSpecifics = $this->createEdgeSpecifics($type->isWeighted());
77 14
    }
78
79
    /**
80
     * Get the graph specifics
81
     *
82
     * @param bool $isDirected - is directed graph?
83
     *
84
     * @return SpecificsInterface
85
     */
86 14
    public function createSpecifics(bool $isDirected): SpecificsInterface
87
    {
88 14
        return $isDirected ? new DirectedSpecifics($this) : new UndirectedSpecifics($this);
89
    }
90
91
    /**
92
     * Get the edge specifics
93
     *
94
     * @param bool $isWeighted - is weighted?
95
     *
96
     * @return EdgeSpecificsInterface
97
     */
98 14
    public function createEdgeSpecifics(bool $isWeighted): EdgeSpecificsInterface
99
    {
100 14
        return $isWeighted ? new WeightedEdgeSpecifics($this) : new UniformEdgeSpecifics($this);
0 ignored issues
show
Unused Code introduced by
The call to graphp\edge\specifics\We...pecifics::__construct() has too many arguments starting with $this. ( Ignorable by Annotation )

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

100
        return $isWeighted ? /** @scrutinizer ignore-call */ new WeightedEdgeSpecifics($this) : new UniformEdgeSpecifics($this);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
Unused Code introduced by
The call to graphp\edge\specifics\Un...pecifics::__construct() has too many arguments starting with $this. ( Ignorable by Annotation )

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

100
        return $isWeighted ? new WeightedEdgeSpecifics($this) : /** @scrutinizer ignore-call */ new UniformEdgeSpecifics($this);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
101
    }
102
103
    /**
104
     * Get all edges connecting the source vertext to the target vertex
105
     *
106
     * @return EdgeSet
107
     */
108 1
    public function getAllEdges(VertexInterface $sourceVertex, VertexInterface $targetVertex): EdgeSet
109
    {
110 1
        return $this->specifics->getAllEdges($sourceVertex, $targetVertex);
111
    }
112
113
    /**
114
     * Get an edge connecting the source vertext to the target vertex
115
     *
116
     * @return null|EdgeInterface
117
     */
118 11
    public function getEdge(VertexInterface $sourceVertex, VertexInterface $targetVertex): ?EdgeInterface
119
    {
120 11
        return $this->specifics->getEdge($sourceVertex, $targetVertex);
121
    }
122
123
    /**
124
     * Get the vertex supplier that the graph uses whenever it needs to create new vertices
125
     *
126
     * @return null|SupplierInterface
127
     */
128 2
    public function getVertexSupplier(): ?SupplierInterface
129
    {
130 2
        return $this->vertexSupplier;
131
    }
132
133
    /**
134
     * Get the edge supplier that the graph uses whenever it needs to create new edges
135
     *
136
     * @return SupplierInterface
137
     */
138 4
    public function getEdgeSupplier(): SupplierInterface
139
    {
140 4
        return $this->edgeSupplier;
141
    }
142
143
    /**
144
     * Get the graph type
145
     *
146
     * @return GraphTypeInterface
147
     */
148 13
    public function getType(): GraphTypeInterface
149
    {
150 13
        return $this->type;
151
    }
152
153
    /**
154
     * Create a new edge in the graph. Return the newly created edge if added to the graph.
155
     *
156
     * @return EdgeInterface
157
     *
158
     * @throws InvalidArgumentException
159
     */
160 11
    public function addEdge(
161
        VertexInterface $sourceVertex,
162
        VertexInterface $targetVertex,
163
        ?EdgeInterface $edge = null
164
    ): ?EdgeInterface {
165 11
        $this->assertVertexExists($sourceVertex);
166 11
        $this->assertVertexExists($targetVertex);
167
        
168 11
        if (!$this->getType()->isAllowingMultipleEdges() && $this->containsEdge($sourceVertex, $targetVertex)) {
169 2
            return null;
170
        }
171
        
172 11
        if (!$this->getType()->isAllowingSelfLoops() && $sourceVertex->equals($targetVertex)) {
173
            throw new InvalidArgumentException("loops are not allowed");
174
        }
175
        
176 11
        $edgeSupplier = $this->edgeSupplier->get();
177
178 11
        if ($this->edgeSpecifics->add($edgeSupplier, $sourceVertex, $targetVertex)) {
179 11
            $this->specifics->addEdgeToTouchingVertices($edgeSupplier);
180 11
            return $edgeSupplier;
181
        }
182
                
183
        return null;
184
    }
185
186
    /**
187
     * Create and return a new vertex in the graph.
188
     *
189
     * @return null|VertexInterface
190
     */
191 12
    public function addVertex(VertexInterface $vertex): ?VertexInterface
192
    {
193 12
        if (!$this->containsVertex($vertex)) {
194 12
            $this->specifics->addVertex($vertex);
195 12
            return $vertex;
196
        }
197
        
198 2
        return null;
199
    }
200
201
    /**
202
     * Check if the graph contains the given edge, specified either by two vertices or by the edge itself
203
     *
204
     * @param VertexInterface $sourceVertex - the source vertex
205
     * @param VertexInterface $targetVertex - the target vertex
206
     * @param EdgeInterface $edge - the edge
207
     *
208
     * @return bool
209
     */
210 11
    public function containsEdge(
211
        ?VertexInterface $sourceVertex = null,
212
        ?VertexInterface $targetVertex = null,
213
        ?EdgeInterface $edge = null
214
    ): bool {
215 11
        if (!is_null($sourceVertex) && !is_null($targetVertex)) {
216 11
            return !is_null($this->getEdge($sourceVertex, $targetVertex));
217
        }
218 2
        return $this->edgeSpecifics->containsEdge($edge);
0 ignored issues
show
Bug introduced by
It seems like $edge can also be of type null; however, parameter $edge of graphp\edge\specifics\Ed...terface::containsEdge() does only seem to accept graphp\edge\EdgeInterface, 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

218
        return $this->edgeSpecifics->containsEdge(/** @scrutinizer ignore-type */ $edge);
Loading history...
219
    }
220
221
    /**
222
     * Check if the graph contains the given vertex
223
     *
224
     * @return bool
225
     */
226 12
    public function containsVertex(VertexInterface $vertex): bool
227
    {
228 12
        return $this->specifics->getVertexSet()->contains($vertex);
229
    }
230
231
    /**
232
     * Get a set of all edges touching the specified vertex
233
     *
234
     * @param VertexInterface - the vertex
0 ignored issues
show
Documentation Bug introduced by
The doc comment - at position 0 could not be parsed: Unknown type name '-' at position 0 in -.
Loading history...
235
     *
236
     * @return EdgeSet
237
     */
238 6
    public function edgesOf(VertexInterface $vertex): EdgeSet
239
    {
240 6
        $this->assertVertexExists($vertex);
241 6
        return $this->specifics->edgesOf($vertex);
242
    }
243
244
    /**
245
     * Get a set of all edges incoming into the specified vertex
246
     *
247
     * @param VertexInterface $vertex - the vertex
248
     *
249
     * @return EdgeSet
250
     */
251 3
    public function incomingEdgesOf(VertexInterface $vertex): EdgeSet
252
    {
253 3
        $this->assertVertexExists($vertex);
254 3
        return $this->specifics->incomingEdgesOf($vertex);
255
    }
256
257
    /**
258
     * Get a set of all edges outgoing from the specified vertex
259
     *
260
     * @param VertexInterface $vertex - the vertex
261
     *
262
     * @return EdgeSet
263
     */
264 3
    public function outgoingEdgesOf(VertexInterface $vertex): EdgeSet
265
    {
266 3
        $this->assertVertexExists($vertex);
267 3
        return $this->specifics->outgoingEdgesOf($vertex);
268
    }
269
270
    /**
271
     * Remove all edges specified by two vertices or the the list of edges themselves.
272
     * Return true, if graph was changed
273
     *
274
     * @param VertexInterface $vertex - the source vertex
275
     * @param VertexInterface $vertex - the target vertex
276
     * @param EdgeSet $edges - the edges
277
     *
278
     * @return bool
279
     */
280 3
    public function removeAllEdges(
281
        ?VertexInterface $sourceVertex = null,
282
        ?VertexInterface $targetVertex = null,
283
        ?EdgeSet $edges = null
284
    ): bool {
285 3
        $changed = false;
286 3
        if (!is_null($sourceVertex) && !is_null($targetVertex)) {
287 1
            $edge = $this->getEdge($sourceVertex, $targetVertex);
288 1
            if (!is_null($edge)) {
289 1
                $this->specifics->removeEdgeFromTouchingVertices($edge);
290 1
                $this->edgeSpecifics->remove($edge);
291 1
                return true;
292
            }
293
        } else {
294 2
            foreach ($edges as $edge) {
295 1
                if ($this->containsEdge(null, null, $edge)) {
296 1
                    $this->specifics->removeEdgeFromTouchingVertices($edge);
297 1
                    $this->edgeSpecifics->remove($edge);
298 1
                    $changed = true;
299
                }
300
            }
301
        }
302 2
        return $changed;
303
    }
304
305
    /**
306
     * Remove all specified vertices contained in the graph.
307
     * Return true, if graph was changed
308
     *
309
     * @param VertexSet $vertices - the vertices
310
     *
311
     * @return bool
312
     */
313 1
    public function removeAllVertices(VertexSet $vertices): bool
314
    {
315 1
        $changed = false;
316 1
        foreach ($vertices as $vertex) {
317 1
            $changed = $this->removeVertex($vertex);
318
        }
319 1
        return $changed;
320
    }
321
322
    /**
323
     * Remove the specifeid edge from the graph.
324
     * Return the edge, if it was removed
325
     *
326
     * @param VertexInterface $sourceVertex - the source vertex
327
     * @param VertexInterface $targetVertex - the target vertex
328
     * @param EdgeInterface $edge - the edge
329
     *
330
     * @return null|EdgeInterface
331
     */
332 1
    public function removeEdge(
333
        ?VertexInterface $sourceVertex = null,
334
        ?VertexInterface $targetVertex = null,
335
        ?EdgeInterface $edge = null
336
    ): ?EdgeInterface {
337 1
        if (!is_null($sourceVertex) && !is_null($targetVertex)) {
338 1
            $edge = $this->getEdge($sourceVertex, $targetVertex);
339 1
            if (!is_null($edge)) {
340 1
                $this->specifics->removeEdgeFromTouchingVertices($edge);
341 1
                $this->edgeSpecifics->remove($edge);
342 1
                return $edge;
343
            }
344 1
        } elseif (!is_null($edge)) {
345 1
            $this->specifics->removeEdgeFromTouchingVertices($edge);
346 1
            $this->edgeSpecifics->remove($edge);
347 1
            return $edge;
348
        }
349
        return null;
350
    }
351
352
    /**
353
     * Remove the specifeid vertex from the graph.
354
     * Return the true, if it was removed
355
     *
356
     * @param VertexInterface $vertex - the vertex
357
     *
358
     * @return bool
359
     */
360 2
    public function removeVertex(VertexInterface $vertex): bool
361
    {
362 2
        if ($this->containsVertex($vertex)) {
363 2
            $edges = $this->edgesOf($vertex);
364 2
            $this->removeAllEdges(null, null, $edges);
365 2
            $this->specifics->removeVertex($vertex);
366 2
            return true;
367
        }
368 1
        return false;
369
    }
370
371
    /**
372
     * Get the set of edges contained in the graph
373
     *
374
     * @return EdgeSet
375
     */
376 6
    public function edgeSet(): EdgeSet
377
    {
378 6
        return $this->edgeSpecifics->getEdgeSet();
379
    }
380
381
    /**
382
     * Get the set of vertices contained in the graph
383
     *
384
     * @return VertexSet
385
     */
386 4
    public function vertexSet(): VertexSet
387
    {
388 4
        return $this->specifics->getVertexSet();
389
    }
390
391
    /**
392
     * Get the edge source vertex
393
     *
394
     * @param EdgeInterface $edge - the edge
395
     *
396
     * @return VertexInterface
397
     */
398 11
    public function getEdgeSource(EdgeInterface $edge): VertexInterface
399
    {
400 11
        return $this->edgeSpecifics->getEdgeSource($edge);
401
    }
402
403
    /**
404
     * Get the edge target vertex
405
     *
406
     * @param EdgeInterface $edge - the edge
407
     *
408
     * @return VertexInterface
409
     */
410 11
    public function getEdgeTarget(EdgeInterface $edge): VertexInterface
411
    {
412 11
        return $this->edgeSpecifics->getEdgeTarget($edge);
413
    }
414
415
    /**
416
     * Get the edge weight
417
     *
418
     * @param EdgeInterface $edge - the edge
419
     *
420
     * @return float
421
     */
422
    public function getEdgeWeight(EdgeInterface $edge): float
423
    {
424
        return $this->edgeSpecifics->getEdgeWeight($edge);
425
    }
426
427
    /**
428
     * Set the edge weight
429
     *
430
     * @param EdgeInterface $edge - the edge
431
     * @param float $weight - the edge weight
432
     */
433
    public function setEdgeWeight(EdgeInterface $edge, float $weight): void
434
    {
435
        $this->edgeSpecifics->setEdgeWeight($edge, $weight);
436
    }
437
438
    /**
439
     * Assert the the vertex exists
440
     *
441
     * @param VertexInterface - the vertex
0 ignored issues
show
Documentation Bug introduced by
The doc comment - at position 0 could not be parsed: Unknown type name '-' at position 0 in -.
Loading history...
442
     *
443
     * @return bool
444
     *
445
     * @throws InvalidArgumentException
446
     */
447 12
    private function assertVertexExists(VertexInterface $vertex): bool
448
    {
449 12
        if ($this->containsVertex($vertex)) {
450 12
            return true;
451
        } else {
452
            throw new InvalidArgumentException("no such vertex in graph");
453
        }
454
    }
455
}
456