Graph::buildWeightedEdgesFromHandle()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 20
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 9
nc 2
nop 4
dl 0
loc 20
rs 9.9666
c 0
b 0
f 0
1
<?php
2
3
namespace TemplesOfCode\NodesAndEdges;
4
5
use InvalidArgumentException;
6
7
/**
8
 * Class Graph
9
 * @package TemplesOfCode\NodesAndEdges
10
 */
11
abstract class Graph
12
{
13
    /**
14
     * @var int
15
     */
16
    protected $vertices;
17
18
    /**
19
     * @var int
20
     */
21
    protected $edges;
22
23
    /**
24
     * @var array
25
     */
26
    protected $adjacencyList;
27
28
    /**
29
     * Initializes an empty edge-weighted graph with {@code V} vertices and 0 edges.
30
     *
31
     * @param int $vertices
32
     * @param array|null $adjacencyList
33
     */
34
    public function __construct(int $vertices, array $adjacencyList = null)
35
    {
36
        //
37
        if ($vertices < 0) {
38
            throw new InvalidArgumentException(
39
                'Number of vertices must be non-negative'
40
            );
41
        }
42
        // set
43
        $this->vertices = $vertices;
44
        // init
45
        $this->edges = 0;
46
        // get the ne
47
        if (!empty($adjacencyList)) {
48
            // set it
49
            $this->adjacencyList= $adjacencyList;
50
        } else {
51
            // init
52
            $this->adjacencyList = [];
53
            // iterate over the set of vertices
54
            for ($vertex = 0; $vertex < $vertices; $vertex++) {
55
                // initialize each vertex adjacency list
56
                $this->adjacencyList[$vertex] = [];
57
            }
58
        }
59
    }
60
61
    /**
62
     * Returns the number of vertices in this graph.
63
     *
64
     * @return int
65
     */
66
    public function getVertices()
67
    {
68
        // return the amount
69
        return $this->vertices;
70
    }
71
72
    /**
73
     * Returns the number of edges in this graph.
74
     *
75
     * @return int
76
     */
77
    public function getEdges()
78
    {
79
        // return the number of edges
80
        return $this->edges;
81
    }
82
83
    /**
84
     * Returns the vertices adjacent to $vertex
85
     *
86
     * @param int $vertex
87
     * @return array
88
     */
89
    public function adjacent(int $vertex)
90
    {
91
        // validate the vertex
92
        Digraph::validateVertex($vertex, $this->getVertices());
93
        // return the adjacent vertices to it
94
        return $this->adjacencyList[$vertex];
95
    }
96
97
    /**
98
     * @param int $vertex
99
     * @return int
100
     */
101
    public function degree(int $vertex)
102
    {
103
        // validate the vertex
104
        Digraph::validateVertex($vertex, $this->getVertices());
105
        // return the count of neighbors
106
        return count($this->adjacent($vertex));
107
    }
108
109
    /**
110
     * Utility function
111
     *
112
     * @param int $vertex
113
     * @param int $vertices
114
     */
115
    public static function validateVertex(int $vertex, int $vertices)
116
    {
117
        // run the check
118
        if ($vertex < 0 || $vertex >= $vertices) {
119
            // this vertex is not valid
120
            throw new InvalidArgumentException(sprintf(
121
                'vertex %d is not between 0 and %d',
122
                $vertex,
123
                $vertices - 1
124
            ));
125
        }
126
    }
127
128
    /**
129
     * Returns a string representation of this graph.
130
     */
131
    public function __toString()
132
    {
133
        $vertices = $this->getVertices();
134
        // init
135
        $buffer = [];
136
        // add
137
        $buffer[] = sprintf(
138
            '%d vertices, %d edges',
139
            $vertices,
140
            $this->getEdges()
141
        );
142
        // iterate over the vertices
143
        for ($vertex = 0; $vertex < $vertices; $vertex++) {
144
            // get the adjacent vertices
145
            $adjacentVertices = $this->adjacent($vertex);
146
            // add
147
            $buffer[] = sprintf(
148
                '%d : %s',
149
                $vertex,
150
                implode(' ', $adjacentVertices)
151
            );
152
        }
153
        // convert to string
154
        return implode(PHP_EOL, $buffer);
155
    }
156
157
    /**
158
     * @param string $input
159
     * @param int $vertices
160
     * @param bool $weight
161
     * @return array
162
     */
163
    protected static function parseEdge(string $input,int $vertices, bool $weight = false)
164
    {
165
        // clean
166
        $trimmed = trim($input);
167
        // parse
168
        $exploded = explode(' ', $trimmed);
169
        // filter
170
        $filtered = array_filter($exploded, function($vertex) {
171
            // make sure it valid
172
            return (!empty($vertex) || (strlen($vertex) > 0));
173
        });
174
        // get values
175
        $edge = array_values($filtered);
176
        // get v
177
        $v = (int)filter_var(
178
            $edge[0],
179
            FILTER_SANITIZE_NUMBER_INT
180
        );
181
        // validate it
182
        Graph::validateVertex($v, $vertices);
183
        // get w
184
        $w = (int)filter_var(
185
            $edge[1],
186
            FILTER_SANITIZE_NUMBER_INT
187
        );
188
        // validate it
189
        Graph::validateVertex($w, $vertices);
190
        // create set
191
        $set = [
192
            $v,
193
            $w,
194
        ];
195
        // check if weight needs to be added
196
        if ($weight) {
197
            // get weight
198
            $inputWeight = (int)filter_var(
199
                $edge[2],
200
                FILTER_SANITIZE_NUMBER_INT
201
            );
202
            // add it to the set
203
            $set[] = $inputWeight;
204
        }
205
        // return set
206
        return $set;
207
    }
208
209
    /**
210
     * @param resource $handle
211
     * @return int[]
212
     */
213
    protected static function parseGraphVEFromResource($handle)
214
    {
215
        // read from stream
216
        $first = fgets($handle);
217
        // read from stream
218
        $second = fgets($handle);
219
        // delegate to string parser
220
        return self::parseGraphVEFromString(
221
            $first,
222
            $second
223
        );
224
    }
225
226
    /**
227
     * @param string $first
228
     * @param string $second
229
     * @return int[]
230
     */
231
    protected static function parseGraphVEFromString(string $first, string $second)
232
    {
233
        // open the stream for reading
234
        $vertices = (int)filter_var(
235
            $first,
236
            FILTER_SANITIZE_NUMBER_INT
237
        );
238
        // sanity check
239
        if ($vertices < 0) {
240
            // bad state
241
            throw new InvalidArgumentException(
242
                'number of vertices in a Graph must be non-negative'
243
            );
244
        }
245
246
        // read in the amount of edges in the stream
247
        $edges = (int)filter_var(
248
            $second,
249
            FILTER_SANITIZE_NUMBER_INT
250
        );
251
        // sanity check
252
        if ($edges < 0) {
253
            // bad state
254
            throw new InvalidArgumentException(
255
                'number of edges in a Graph must be non-negative'
256
            );
257
        }
258
        // return the set
259
        return [
260
            $vertices,
261
            $edges
262
        ];
263
    }
264
265
    /**
266
     * @param Graph $graph
267
     * @param int $vertices
268
     * @param int $edges
269
     * @param resource $handle
270
     */
271
    protected static function buildWeightedEdgesFromHandle(
272
        Graph $graph,
273
        int $vertices,
274
        int $edges,
275
        $handle
276
    ) {
277
        // read in the edges
278
        for ($i = 0; $i < $edges; $i++) {
279
            // fet from source
280
            $raw = fgets($handle);
281
            // parse data
282
            list (
283
                $v,
284
                $w,
285
                $weight
286
                ) = self::parseEdge($raw, $vertices, true);
287
            // re-use var here
288
            $edge = new Edge($v, $w, $weight);
289
            // add to the graph
290
            $graph->addEdge($edge);
0 ignored issues
show
Bug introduced by
The method addEdge() does not exist on TemplesOfCode\NodesAndEdges\Graph. Since it exists in all sub-types, consider adding an abstract or default implementation to TemplesOfCode\NodesAndEdges\Graph. ( Ignorable by Annotation )

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

290
            $graph->/** @scrutinizer ignore-call */ 
291
                    addEdge($edge);
Loading history...
291
        }
292
    }
293
294
    /**
295
     * @param Graph $graph
296
     * @param int $vertices
297
     * @param int $edges
298
     * @param array $lines
299
     */
300
    protected static function buildWeightedEdgesFromString(
301
        Graph $graph,
302
        int $vertices,
303
        int $edges,
304
        array $lines
305
    ) {
306
        // read in the edges
307
        for ($i = 0; $i < $edges; $i++) {
308
            // fet from source
309
            $raw = $lines[$i];
310
            // parse data
311
            list (
312
                $v,
313
                $w,
314
                $weight
315
            ) = self::parseEdge($raw, $vertices, true);
316
            // re-use var
317
            $edge = new Edge($v, $w, $weight);
318
            // add to the graph
319
            $graph->addEdge($edge);
320
        }
321
    }
322
}