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

DirectedSpecifics   A

Complexity

Total Complexity 27

Size/Duplication

Total Lines 246
Duplicated Lines 0 %

Test Coverage

Coverage 80.6%

Importance

Changes 1
Bugs 0 Features 1
Metric Value
wmc 27
eloc 55
c 1
b 0
f 1
dl 0
loc 246
ccs 54
cts 67
cp 0.806
rs 10

14 Methods

Rating   Name   Duplication   Size   Complexity  
A edgesOf() 0 18 4
A degreeOf() 0 3 1
A addEdgeToTouchingVertices() 0 7 1
A incomingEdgesOf() 0 3 1
A removeEdgeFromTouchingVertices() 0 7 1
A getEdge() 0 18 5
A outDegreeOf() 0 3 1
A inDegreeOf() 0 3 1
A addVertex() 0 3 1
A getVertexSet() 0 3 1
A __construct() 0 5 1
A getEdgeContainer() 0 10 2
A outgoingEdgesOf() 0 3 1
A getAllEdges() 0 20 6
1
<?php
2
3
namespace graphp\graph\specifics;
4
5
use graphp\graph\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 4
    public function __construct(GraphInterface $graph, ?EdgeSetFactoryInterface $edgeSetFactory = null)
49
    {
50 4
        $this->graph = $graph;
51 4
        $this->vertexMap = new VertexMap();
52 4
        $this->edgeSetFactory = $edgeSetFactory ?? new EdgeArraySetFactory();
53 4
    }
54
55
    /**
56
     * Add a vertex
57
     *
58
     * @param VertexInterface $vertex - vertex to be added
59
     */
60 2
    public function addVertex(VertexInterface $vertex): void
61
    {
62 2
        $this->vertexMap->put($vertex);
63 2
    }
64
65
    /**
66
     * Get the vertex set
67
     *
68
     * @return VertexSet
69
     */
70 2
    public function getVertexSet(): VertexSet
71
    {
72 2
        return $this->vertexMap->keySet();
73
    }
74
75
    /**
76
     * Get all edges connecting the source vertex to the target vertex
77
     *
78
     * @param VertexInterface $sourceVertex - source vertex
79
     * @param VertexInterface $targetVertex - target vertex
80
     *
81
     * @return EdgeSet
82
     */
83 1
    public function getAllEdges(VertexInterface $sourceVertex, VertexInterface $targetVertex): EdgeSet
84
    {
85 1
        $edges = new EdgeSet();
86
        
87
        if (
88 1
            $this->graph->containsVertex($sourceVertex)
89 1
            && $this->graph->containsVertex($targetVertex)
90
        ) {
91 1
            $allEdges = $this->getEdgeContainer($sourceVertex)->getOutgoing();
92
            
93 1
            foreach ($allEdges as $edge) {
94 1
                $equals = $this->graph->getEdgeTarget($edge)->equals($targetVertex);
95
                
96 1
                if ($equals && !$edges->contains($edge)) {
97 1
                    $edges[] = $edge;
98
                }
99
            }
100
        }
101
        
102 1
        return $edges;
103
    }
104
105
    /**
106
     * Get an edge connecting the source vertex to the target vertex
107
     *
108
     * @param VertexInterface $sourceVertex - source vertex
109
     * @param VertexInterface $targetVertex - target vertex
110
     *
111
     * @return null|EdgeInterface
112
     */
113 1
    public function getEdge(VertexInterface $sourceVertex, VertexInterface $targetVertex): ?EdgeInterface
114
    {
115
        if (
116 1
            $this->graph->containsVertex($sourceVertex)
117 1
            && $this->graph->containsVertex($targetVertex)
118
        ) {
119 1
            $edges = $this->getEdgeContainer($sourceVertex)->getOutgoing();
120
            
121 1
            foreach ($edges as $edge) {
122 1
                $equals = $this->graph->getEdgeTarget($edge)->equals($targetVertex);
123
                
124 1
                if ($equals) {
125 1
                    return $edge;
126
                }
127
            }
128
        }
129
        
130 1
        return null;
131
    }
132
133
    /**
134
     * Get all edges touching the specified vertex
135
     *
136
     * @param VertexInterface $vertex - the vertex for which a set of touching edges is to be returned
137
     *
138
     * @return EdgeSet
139
     */
140 1
    public function edgesOf(VertexInterface $vertex): EdgeSet
141
    {
142 1
        $edges = $this->getEdgeContainer($vertex)->getOutgoing()->getArrayCopy();
143 1
        $edges = new EdgeSet(array_merge($edges, $this->getEdgeContainer($vertex)->getIncoming()->getArrayCopy()));
144
        
145
        //remove only one copy of self-loop
146 1
        if ($this->graph->getType()->isAllowingSelfLoops()) {
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

146
        if ($this->graph->/** @scrutinizer ignore-call */ getType()->isAllowingSelfLoops()) {
Loading history...
147 1
            $loops = array_unique($this->getAllEdges($vertex, $vertex)->getArrayCopy());
148
            
149 1
            foreach ($edges as $key => $edge) {
150 1
                if (($id = array_search($edge, $loops)) !== false) {
151
                    unset($edges[$key]);
152 1
                    unset($loops[$id]);
153
                }
154
            }
155
        }
156
        
157 1
        return $edges;
158
    }
159
160
    /**
161
     * Add the specified edge to the edge containers of its source and target vertices.
162
     *
163
     * @param EdgeInterface $edge - the edge to be added
164
     */
165 1
    public function addEdgeToTouchingVertices(EdgeInterface $edge): void
166
    {
167 1
        $sourceVertex = $this->graph->getEdgeSource($edge);
168 1
        $targetVertex = $this->graph->getEdgeTarget($edge);
169
        
170 1
        $this->getEdgeContainer($sourceVertex)->addOutgoingEdge($edge);
171 1
        $this->getEdgeContainer($targetVertex)->addIncomingEdge($edge);
172 1
    }
173
174
    /**
175
     * Remove the specified edge from the edge containers of its source and target vertices.
176
     *
177
     * @param EdgeInterface $edge - the edge to be removed
178
     */
179
    public function removeEdgeFromTouchingVertices(EdgeInterface $edge): void
180
    {
181
        $sourceVertex = $this->graph->getEdgeSource($edge);
182
        $targetVertex = $this->graph->getEdgeTarget($edge);
183
        
184
        $this->getEdgeContainer($sourceVertex)->removeOutgoingEdge($edge);
185
        $this->getEdgeContainer($targetVertex)->removeIncomingEdge($edge);
186
    }
187
188
    /**
189
     * Get all edges outgoing from the specified vertex
190
     *
191
     * @param VertexInterface $vertex - the vertex for which the list of outgoing edges to be returned
192
     *
193
     * @return EdgeSet
194
     */
195 1
    public function outgoingEdgesOf(VertexInterface $vertex): EdgeSet
196
    {
197 1
        return $this->getEdgeContainer($vertex)->getOutgoing();
198
    }
199
200
    /**
201
     * Get all edges incoming into the specified vertex
202
     *
203
     * @param VertexInterface $vertex - the vertex for which the list of incoming edges to be returned
204
     *
205
     * @return EdgeSet
206
     */
207 1
    public function incomingEdgesOf(VertexInterface $vertex): EdgeSet
208
    {
209 1
        return $this->getEdgeContainer($vertex)->getIncoming();
210
    }
211
212
    /**
213
     * Get the degree of the specified vertex
214
     *
215
     * @param VertexInterface $vertex - the vertex whose degree is to be calculated
216
     *
217
     * @return int
218
     */
219
    public function degreeOf(VertexInterface $vertex): int
220
    {
221
        return $this->inDegreeOf($vertex) + $this->outDegreeOf($vertex);
222
    }
223
224
    /**
225
     * Get the "in degree" of the specified vertex
226
     *
227
     * @param VertexInterface $vertex - the vertex whose in degree is to be calculated
228
     *
229
     * @return int
230
     */
231
    public function inDegreeOf(VertexInterface $vertex): int
232
    {
233
        return count($this->getEdgeContainer($vertex)->getIncoming());
234
    }
235
236
    /**
237
     * Get the "out degree" of the specified vertex
238
     *
239
     * @param VertexInterface $vertex - the vertex whose out degree is to be calculated
240
     *
241
     * @return int
242
     */
243
    public function outDegreeOf(VertexInterface $vertex): int
244
    {
245
        return count($this->getEdgeContainer($vertex)->getOutgoing());
246
    }
247
248
    /**
249
     * Get the edge container for the specified vertex.
250
     *
251
     * @param VertexInterface $vertex - the vertex
252
     *
253
     * @return DirectedEdgeContainer
254
     */
255 1
    public function getEdgeContainer(VertexInterface $vertex): DirectedEdgeContainer
256
    {
257 1
        $ec = $this->vertexMap->get($vertex);
258
        
259 1
        if (is_null($ec)) {
260 1
            $ec = new DirectedEdgeContainer($this->edgeSetFactory, $vertex);
261 1
            $this->vertexMap->put($vertex, $ec);
262
        }
263
        
264 1
        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...
265
    }
266
}
267