Completed
Push — master ( 47991e...d0d021 )
by Ventaquil
02:08
created

Dijkstra::dist()   B

Complexity

Conditions 4
Paths 6

Size

Total Lines 25
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
c 3
b 0
f 0
dl 0
loc 25
rs 8.5806
cc 4
eloc 11
nc 6
nop 3
1
<?php
2
namespace Algorithms;
3
4
class Dijkstra
5
{
6
    protected $points;
7
    protected $relations;
8
    protected $generated = null;
9
10
    public function __construct($relations = null)
11
    {
12
        if (!empty($relations)) {
13
            $this->setRelations($relations);
14
        }
15
    }
16
17
    public function setRelations($relations)
18
    {
19
        if (self::validate($relations)) {
20
            $this->relations = $relations;
21
        } else {
22
            if (is_callable($relations) && ($relations instanceof \Closure)) {
23
                $creator = new GraphTools\Creator;
24
                call_user_func($relations, $creator);
25
26
                $this->relations = $creator->createConnections();
27
            }
28
        }
29
    }
30
31
    public function generate()
32
    {
33
        return $this->generated(function () {
34
            $results = []; # Prepare results array
35
36
            # Analyze all relations
37
            foreach ($this->relations as $point => $relation) {
38
                # Prepare $points array by isset source point
39
                $this->points = [
40
                    $point => [
41
                        0,
42
                        '',
43
                    ],
44
                ];
45
46
                $this->dist($point, $point);
47
                $results[$point] = $this->points; # Copy $points content to results array
48
            }
49
50
            return $results;
51
        });
52
    }
53
54
    private function generated($function)
55
    {
56
        if (empty($this->generated)) {
57
            $this->generated = $function();
58
        }
59
60
        return $this->generated;
61
    }
62
63
    private function dist($source, $pointId, &$visited = [])
64
    {
65
        $visited[$pointId] = true; # Set current point as visited
66
67
        # Prepare help variables
68
        $min_ptr = -1;
69
        $min = 0;
70
71
        # Analyzes point neighborhood
72
        foreach ($this->relations[$pointId] as $relation) {
73
            if ($relation[0] != $source) { # If current point is different than source
74
                list($min_ptr, $min) = $this->setMinPtr($relation, $visited, $min_ptr, $min);
75
76
                # Change the shortest way to current point
77
                $distance = $this->getShortestDistance($pointId);
78
79
                $this->updatePoint($pointId, $relation, $distance);
80
            }
81
        }
82
83
        # If isset unvisited point with minimal way go for it
84
        if ($min_ptr != -1) {
85
            $this->dist($source, $min_ptr, $visited);
86
        }
87
    }
88
89
    private function getShortestDistance($pointId)
90
    {
91
        return isset($this->points[$pointId][0]) ? $this->points[$pointId][0] : 0;
92
    }
93
94
    private function setMinPtr($relation, $visited, $min_ptr, $min)
95
    {
96
        if (empty($visited[$relation[0]])) { # If current point is not visited
97
            if ($min_ptr == -1) { # When minimal point is not finded
98
                $min_ptr = $relation[0];
99
                $min = $relation[1];
100
            } elseif ($min > $relation[1]) {
101
                $min_ptr = $relation[0];
102
                $min = $relation[1];
103
            }
104
        }
105
106
        return array($min_ptr, $min);
107
    }
108
109
    private function updatePoint($pointId, $relation, $distance)
110
    {
111
        if (empty($this->points[$relation[0]])) {
112
                $this->points[$relation[0]] = [
113
                    $distance + $relation[1],
114
                    ((empty($this->points[$pointId][1])) ? $pointId : $this->points[$pointId][1]) . ':' . $relation[0],
115
                ];
116
            } else {
117
                if ($this->points[$relation[0]][0] > ($this->points[$pointId][0] + $relation[1])) {
118
                    $this->points[$relation[0]] = [
119
                        (isset($this->points[$pointId][0]) ? $this->points[$pointId][0] : 0) + $relation[1],
120
                        (empty($this->points[$pointId][1]) ? null : $this->points[$pointId][1] . ':') . $relation[0],
121
                    ];
122
                }
123
            }
124
    }
125
126
    public function distances($point)
127
    {
128
        $this->dist($point, $point);
129
        return $this->points;
130
    }
131
132
    public static function validate($relations_array)
133
    {
134
        $return = is_array($relations_array);
135
136
        if ($return) {
137
            foreach ($relations_array as $relations) {
138
                $return &= self::checkPointRelations($relations);
139
            }
140
        }
141
142
        return $return;
143
    }
144
145
    private static function checkPointRelations($relations)
146
    {
147
        $return = is_array($relations);
148
149
        if ($return) {
150
            foreach ($relations as $relation) {
151
                $return &= self::checkSingleRelation($relation);
152
            }
153
        }
154
155
        return $return;
156
    }
157
158
    private static function checkSingleRelation($relation)
159
    {
160
        return (is_array($relation) && (count($relation) == 2) && isset($relation[0]) && isset($relation[1]));
161
    }
162
}
163