Issues (34)

src/Graph/Specifics/UndirectedSpecifics.php (2 issues)

1
<?php
2
3
namespace Graphp\Graph\Specifics;
4
5
use Graphp\GraphInterface;
6
use Graphp\Edge\EdgeInterface;
7
use Graphp\Edge\EdgeSetFactoryInterface;
8
use Graphp\Edge\EdgeArraySetFactory;
9
use Graphp\Edge\EdgeSet;
10
use Graphp\Vertex\VertexInterface;
11
use Graphp\Vertex\VertexMap;
12
use Graphp\Vertex\VertexSet;
13
14
/**
15
 * Class UndirectedSpecifics
16
 *
17
 * @package Graphp\Graph\Specifics
18
 */
19
class UndirectedSpecifics implements SpecificsInterface
20
{
21
    /**
22
     * The graph
23
     *
24
     * @var GraphInterface
25
     */
26
    protected $graph;
27
28
    /**
29
     * The vertex map
30
     *
31
     * @var VertexMap
32
     */
33
    protected $vertexMap;
34
35
    /**
36
     * The edge set factory
37
     *
38
     * @var EdgeSetFactoryInterface
39
     */
40
    protected $edgeSetFactory;
41
42
    /**
43
     * Construct a new undirected specifics
44
     *
45
     * @param GraphInterface $graph - the graph for which these specifics are for
46
     * @param EdgeSetFactoryInterface $edgeSetFactory - the edge set factory, used by the graph
47
     */
48 12
    public function __construct(GraphInterface $graph, ?EdgeSetFactoryInterface $edgeSetFactory = null)
49
    {
50 12
        $this->graph = $graph;
51 12
        $this->vertexMap = new VertexMap();
52 12
        $this->edgeSetFactory = $edgeSetFactory ?? new EdgeArraySetFactory();
53 12
    }
54
55
    /**
56
     * Add a vertex
57
     *
58
     * @param VertexInterface $vertex - vertex to be added
59
     */
60 11
    public function addVertex(VertexInterface $vertex): void
61
    {
62 11
        $this->vertexMap->put($vertex);
63 11
    }
64
65
    /**
66
     * Remove a vertex
67
     *
68
     * @param VertexInterface $vertex - vertex to be removed
69
     */
70 1
    public function removeVertex(VertexInterface $vertex): void
71
    {
72 1
        $this->vertexMap->remove($vertex);
73 1
    }
74
75
    /**
76
     * Get the vertex set
77
     *
78
     * @return VertexSet
79
     */
80 10
    public function getVertexSet(): VertexSet
81
    {
82 10
        return $this->vertexMap->keySet();
83
    }
84
85
    /**
86
     * Get all edges connecting the source vertex to the target vertex
87
     *
88
     * @param VertexInterface $sourceVertex - source vertex
89
     * @param VertexInterface $targetVertex - target vertex
90
     *
91
     * @return EdgeSet
92
     */
93
    public function getAllEdges(VertexInterface $sourceVertex, VertexInterface $targetVertex): EdgeSet
94
    {
95
        $edges = new EdgeSet();
96
        
97
        if (
98
            $this->graph->containsVertex($sourceVertex)
99
            && $this->graph->containsVertex($targetVertex)
100
        ) {
101
            $edges = $this->getEdgeContainer($sourceVertex)->getEdges();
102
            
103
            foreach ($edges as $edge) {
104
                $equals = $this->isEqualStraightOrInverted($sourceVertex, $targetVertex, $edge);
105
                
106
                if ($equals) {
107
                    $edges[] = $edge;
108
                }
109
            }
110
        }
111
        
112
        return $edges;
113
    }
114
115
    /**
116
     * Get an edge connecting the source vertex to the target vertex
117
     *
118
     * @param VertexInterface $sourceVertex - source vertex
119
     * @param VertexInterface $targetVertex - target vertex
120
     *
121
     * @return null|EdgeInterface
122
     */
123 7
    public function getEdge(VertexInterface $sourceVertex, VertexInterface $targetVertex): ?EdgeInterface
124
    {
125
        if (
126 7
            $this->graph->containsVertex($sourceVertex)
127 7
            && $this->graph->containsVertex($targetVertex)
128
        ) {
129 7
            $edges = $this->getEdgeContainer($sourceVertex)->getEdges();
130
            
131 7
            foreach ($edges as $edge) {
132 6
                $equals = $this->isEqualStraightOrInverted($sourceVertex, $targetVertex, $edge);
133
                
134 6
                if ($equals) {
135 6
                    return $edge;
136
                }
137
            }
138
        }
139
        
140 6
        return null;
141
    }
142
143
    /**
144
     * Get all edges touching the specified vertex
145
     *
146
     * @param VertexInterface $vertex - the vertex for which a set of touching edges is to be returned
147
     *
148
     * @return EdgeSet
149
     */
150 1
    public function edgesOf(VertexInterface $vertex): EdgeSet
151
    {
152 1
        return $this->getEdgeContainer($vertex)->getEdges();
153
    }
154
155
    /**
156
     * Add the specified edge to the edge containers of its source and target vertices.
157
     *
158
     * @param EdgeInterface $edge - the edge to be added
159
     */
160 8
    public function addEdgeToTouchingVertices(EdgeInterface $edge): void
161
    {
162 8
        $sourceVertex = $this->graph->getEdgeSource($edge);
163 8
        $targetVertex = $this->graph->getEdgeTarget($edge);
164
        
165 8
        $this->getEdgeContainer($sourceVertex)->addEdge($edge);
166
        
167 8
        if (!$sourceVertex->equals($targetVertex)) {
168 7
            $this->getEdgeContainer($targetVertex)->addEdge($edge);
169
        }
170 8
    }
171
172
    /**
173
     * Remove the specified edge from the edge containers of its source and target vertices.
174
     *
175
     * @param EdgeInterface $edge - the edge to be removed
176
     */
177
    public function removeEdgeFromTouchingVertices(EdgeInterface $edge): void
178
    {
179
        $sourceVertex = $this->graph->getEdgeSource($edge);
180
        $targetVertex = $this->graph->getEdgeTarget($edge);
181
        
182
        $this->getEdgeContainer($sourceVertex)->removeEdge($edge);
183
        
184
        if (!$sourceVertex->equals($targetVertex)) {
185
            $this->getEdgeContainer($targetVertex)->removeEdge($edge);
186
        }
187
    }
188
189
    /**
190
     * Get all edges outgoing from the specified vertex
191
     *
192
     * @param VertexInterface $vertex - the vertex for which the list of outgoing edges to be returned
193
     *
194
     * @return EdgeSet
195
     */
196 4
    public function outgoingEdgesOf(VertexInterface $vertex): EdgeSet
197
    {
198 4
        return $this->getEdgeContainer($vertex)->getEdges();
199
    }
200
201
    /**
202
     * Get all edges incoming into the specified vertex
203
     *
204
     * @param VertexInterface $vertex - the vertex for which the list of incoming edges to be returned
205
     *
206
     * @return EdgeSet
207
     */
208 1
    public function incomingEdgesOf(VertexInterface $vertex): EdgeSet
209
    {
210 1
        return $this->getEdgeContainer($vertex)->getEdges();
211
    }
212
213
    /**
214
     * Get the degree of the specified vertex
215
     *
216
     * @param VertexInterface $vertex - the vertex whose degree is to be calculated
217
     *
218
     * @return int
219
     */
220 1
    public function degreeOf(VertexInterface $vertex): int
221
    {
222 1
        if ($this->graph->getType()->isAllowingSelfLoops()) {
223 1
            $degree = 0;
224 1
            $edges = $this->getEdgeContainer($vertex)->getEdges();
225
226 1
            foreach ($edges as $edge) {
227
                //if it is a loop, then count twice
228 1
                if ($this->graph->getEdgeSource($edge)->equals($this->graph->getEdgeTarget($edge))) {
229
                    $degree += 2;
230
                } else {
231 1
                    $degree += 1;
232
                }
233
            }
234
235 1
            return $degree;
236
        } else {
237
            $this->getEdgeContainer($vertex)->edgeCount();
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 integer. 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...
238
        }
239
    }
240
241
    /**
242
     * Get the "in degree" of the specified vertex
243
     *
244
     * @param VertexInterface $vertex - the vertex whose in degree is to be calculated
245
     *
246
     * @return int
247
     */
248 1
    public function inDegreeOf(VertexInterface $vertex): int
249
    {
250 1
        return $this->degreeOf($vertex);
251
    }
252
253
    /**
254
     * Get the "out degree" of the specified vertex
255
     *
256
     * @param VertexInterface $vertex - the vertex whose out degree is to be calculated
257
     *
258
     * @return int
259
     */
260 1
    public function outDegreeOf(VertexInterface $vertex): int
261
    {
262 1
        return $this->degreeOf($vertex);
263
    }
264
265
    /**
266
     * Get the edge container for the specified vertex.
267
     *
268
     * @param VertexInterface $vertex - the vertex
269
     *
270
     * @return DirectedEdgeContainer
271
     */
272 9
    public function getEdgeContainer(VertexInterface $vertex): UndirectedEdgeContainer
273
    {
274 9
        $ec = $this->vertexMap->get($vertex);
275
        
276 9
        if (is_null($ec)) {
277 9
            $ec = new UndirectedEdgeContainer($this->edgeSetFactory, $vertex);
278 9
            $this->vertexMap->put($vertex, $ec);
279
        }
280
        
281 9
        return $ec;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $ec could return the type Graphp\Edge\EdgeContainerInterface which includes types incompatible with the type-hinted return Graphp\Graph\Specifics\UndirectedEdgeContainer. Consider adding an additional type-check to rule them out.
Loading history...
282
    }
283
284
    /**
285
     * Check if both vertices are touching the edge
286
     *
287
     * @param VertexInterface $sourceVertex - source vertex
288
     * @param VertexInterface $targetVertex - target vertex
289
     * @param EdgeInterface $edge - the edge
290
     *
291
     * @return bool
292
     */
293 6
    private function isEqualStraightOrInverted(
294
        VertexInterface $sourceVertex,
295
        VertexInterface $targetVertex,
296
        EdgeInterface $edge
297
    ): bool {
298 6
        $straigt = $sourceVertex->equals($this->graph->getEdgeSource($edge))
299 6
                   && $targetVertex->equals($this->graph->getEdgeTarget($edge));
300
                   
301 6
        $inverted = $targetVertex->equals($this->graph->getEdgeSource($edge))
302 6
                   && $sourceVertex->equals($this->graph->getEdgeTarget($edge));
303
                   
304 6
        return $straigt || $inverted;
305
    }
306
}
307