Issues (34)

src/Graph/Specifics/DirectedSpecifics.php (1 issue)

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 DirectedSpecifics
16
 *
17
 * @package Graphp\Graph\Specifics
18
 */
19
class DirectedSpecifics 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 directed 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 31
    public function __construct(GraphInterface $graph, ?EdgeSetFactoryInterface $edgeSetFactory = null)
49
    {
50 31
        $this->graph = $graph;
51 31
        $this->vertexMap = new VertexMap();
52 31
        $this->edgeSetFactory = $edgeSetFactory ?? new EdgeArraySetFactory();
53 31
    }
54
55
    /**
56
     * Add a vertex
57
     *
58
     * @param VertexInterface $vertex - vertex to be added
59
     */
60 28
    public function addVertex(VertexInterface $vertex): void
61
    {
62 28
        $this->vertexMap->put($vertex);
63 28
    }
64
65
    /**
66
     * Remove a vertex
67
     *
68
     * @param VertexInterface $vertex - vertex to be removed
69
     */
70 4
    public function removeVertex(VertexInterface $vertex): void
71
    {
72 4
        $this->vertexMap->remove($vertex);
73 4
    }
74
75
    /**
76
     * Get the vertex set
77
     *
78
     * @return VertexSet
79
     */
80 28
    public function getVertexSet(): VertexSet
81
    {
82 28
        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 8
    public function getAllEdges(VertexInterface $sourceVertex, VertexInterface $targetVertex): EdgeSet
94
    {
95 8
        $edges = new EdgeSet();
96
        
97
        if (
98 8
            $this->graph->containsVertex($sourceVertex)
99 8
            && $this->graph->containsVertex($targetVertex)
100
        ) {
101 8
            $allEdges = $this->getEdgeContainer($sourceVertex)->getOutgoing();
102
            
103 8
            foreach ($allEdges as $edge) {
104 5
                $equals = $this->graph->getEdgeTarget($edge)->equals($targetVertex);
105
                
106 5
                if ($equals && !$edges->contains($edge)) {
107 5
                    $edges[] = $edge;
108
                }
109
            }
110
        }
111
        
112 8
        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 18
    public function getEdge(VertexInterface $sourceVertex, VertexInterface $targetVertex): ?EdgeInterface
124
    {
125
        if (
126 18
            $this->graph->containsVertex($sourceVertex)
127 18
            && $this->graph->containsVertex($targetVertex)
128
        ) {
129 18
            $edges = $this->getEdgeContainer($sourceVertex)->getOutgoing();
130
            
131 18
            foreach ($edges as $edge) {
132 10
                $equals = $this->graph->getEdgeTarget($edge)->equals($targetVertex);
133
                
134 10
                if ($equals) {
135 10
                    return $edge;
136
                }
137
            }
138
        }
139
        
140 18
        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 8
    public function edgesOf(VertexInterface $vertex): EdgeSet
151
    {
152 8
        $edges = $this->getEdgeContainer($vertex)->getOutgoing()->getArrayCopy();
153 8
        $edges = new EdgeSet(array_merge($edges, $this->getEdgeContainer($vertex)->getIncoming()->getArrayCopy()));
154
        
155
        //remove only one copy of self-loop
156 8
        if ($this->graph->getType()->isAllowingSelfLoops()) {
157 8
            $loops = array_unique($this->getAllEdges($vertex, $vertex)->getArrayCopy());
158
            
159 8
            foreach ($edges as $key => $edge) {
160 6
                if (($id = array_search($edge, $loops)) !== false) {
161
                    unset($edges[$key]);
162 6
                    unset($loops[$id]);
163
                }
164
            }
165
        }
166
        
167 8
        return $edges;
168
    }
169
170
    /**
171
     * Add the specified edge to the edge containers of its source and target vertices.
172
     *
173
     * @param EdgeInterface $edge - the edge to be added
174
     */
175 22
    public function addEdgeToTouchingVertices(EdgeInterface $edge): void
176
    {
177 22
        $sourceVertex = $this->graph->getEdgeSource($edge);
178 22
        $targetVertex = $this->graph->getEdgeTarget($edge);
179
        
180 22
        $this->getEdgeContainer($sourceVertex)->addOutgoingEdge($edge);
181 22
        $this->getEdgeContainer($targetVertex)->addIncomingEdge($edge);
182 22
    }
183
184
    /**
185
     * Remove the specified edge from the edge containers of its source and target vertices.
186
     *
187
     * @param EdgeInterface $edge - the edge to be removed
188
     */
189 3
    public function removeEdgeFromTouchingVertices(EdgeInterface $edge): void
190
    {
191 3
        $sourceVertex = $this->graph->getEdgeSource($edge);
192 3
        $targetVertex = $this->graph->getEdgeTarget($edge);
193
        
194 3
        $this->getEdgeContainer($sourceVertex)->removeOutgoingEdge($edge);
195 3
        $this->getEdgeContainer($targetVertex)->removeIncomingEdge($edge);
196 3
    }
197
198
    /**
199
     * Get all edges outgoing from the specified vertex
200
     *
201
     * @param VertexInterface $vertex - the vertex for which the list of outgoing edges to be returned
202
     *
203
     * @return EdgeSet
204
     */
205 7
    public function outgoingEdgesOf(VertexInterface $vertex): EdgeSet
206
    {
207 7
        return $this->getEdgeContainer($vertex)->getOutgoing();
208
    }
209
210
    /**
211
     * Get all edges incoming into the specified vertex
212
     *
213
     * @param VertexInterface $vertex - the vertex for which the list of incoming edges to be returned
214
     *
215
     * @return EdgeSet
216
     */
217 4
    public function incomingEdgesOf(VertexInterface $vertex): EdgeSet
218
    {
219 4
        return $this->getEdgeContainer($vertex)->getIncoming();
220
    }
221
222
    /**
223
     * Get the degree of the specified vertex
224
     *
225
     * @param VertexInterface $vertex - the vertex whose degree is to be calculated
226
     *
227
     * @return int
228
     */
229 1
    public function degreeOf(VertexInterface $vertex): int
230
    {
231 1
        return $this->inDegreeOf($vertex) + $this->outDegreeOf($vertex);
232
    }
233
234
    /**
235
     * Get the "in degree" of the specified vertex
236
     *
237
     * @param VertexInterface $vertex - the vertex whose in degree is to be calculated
238
     *
239
     * @return int
240
     */
241 1
    public function inDegreeOf(VertexInterface $vertex): int
242
    {
243 1
        return count($this->getEdgeContainer($vertex)->getIncoming());
244
    }
245
246
    /**
247
     * Get the "out degree" of the specified vertex
248
     *
249
     * @param VertexInterface $vertex - the vertex whose out degree is to be calculated
250
     *
251
     * @return int
252
     */
253 1
    public function outDegreeOf(VertexInterface $vertex): int
254
    {
255 1
        return count($this->getEdgeContainer($vertex)->getOutgoing());
256
    }
257
258
    /**
259
     * Get the edge container for the specified vertex.
260
     *
261
     * @param VertexInterface $vertex - the vertex
262
     *
263
     * @return DirectedEdgeContainer
264
     */
265 24
    public function getEdgeContainer(VertexInterface $vertex): DirectedEdgeContainer
266
    {
267 24
        $ec = $this->vertexMap->get($vertex);
268
        
269 24
        if (is_null($ec)) {
270 24
            $ec = new DirectedEdgeContainer($this->edgeSetFactory, $vertex);
271 24
            $this->vertexMap->put($vertex, $ec);
272
        }
273
        
274 24
        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\DirectedEdgeContainer. Consider adding an additional type-check to rule them out.
Loading history...
275
    }
276
}
277