Completed
Push — master ( d0d021...195fbe )
by Ventaquil
10:35
created

Dijkstra::dist()   B

Complexity

Conditions 4
Paths 6

Size

Total Lines 25
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 4
Bugs 0 Features 0
Metric Value
c 4
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 = array();
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 DijkstraCache::loadMany($this->generated, function () {
34
            $results = []; # Prepare results array
35
36
            # Analyze all relations
37
            foreach ($this->relations as $point => $relation) {
38
                $results[$point] = DijkstraCache::loadOne($this->generated, $point, function () use ($point) {
39
                    return $this->distances($point);
40
                });
41
            }
42
43
            return $results;
44
        });
45
    }
46
47
    private function init($point)
48
    {
49
        $this->points = array();
50
51
        $this->points[$point] = DijkstraCache::loadOne($this->generated, $point, function () {
52
           return array(0, ''); 
53
        });
54
    }
55
56
    private function dist($source, $pointId, &$visited = [])
57
    {
58
        $visited[$pointId] = true; # Set current point as visited
59
60
        # Prepare help variables
61
        $min_ptr = -1;
62
        $min = 0;
63
64
        # Analyzes point neighborhood
65
        foreach ($this->relations[$pointId] as $relation) {
66
            if ($relation[0] != $source) { # If current point is different than source
67
                list($min_ptr, $min) = $this->setMinPtr($relation, $visited, $min_ptr, $min);
68
69
                # Change the shortest way to current point
70
                $distance = $this->getShortestDistance($pointId);
71
72
                $this->updatePoint($pointId, $relation, $distance);
73
            }
74
        }
75
76
        # If isset unvisited point with minimal way go for it
77
        if ($min_ptr != -1) {
78
            $this->dist($source, $min_ptr, $visited);
79
        }
80
    }
81
82
    private function getShortestDistance($pointId)
83
    {
84
        return isset($this->points[$pointId][0]) ? $this->points[$pointId][0] : 0;
85
    }
86
87
    private function setMinPtr($relation, $visited, $min_ptr, $min)
88
    {
89
        if (empty($visited[$relation[0]])) { # If current point is not visited
90
            if ($min_ptr == -1) { # When minimal point is not finded
91
                $min_ptr = $relation[0];
92
                $min = $relation[1];
93
            } elseif ($min > $relation[1]) {
94
                $min_ptr = $relation[0];
95
                $min = $relation[1];
96
            }
97
        }
98
99
        return array($min_ptr, $min);
100
    }
101
102
    private function updatePoint($pointId, $relation, $distance)
103
    {
104
        if (empty($this->points[$relation[0]])) {
105
            $this->points[$relation[0]] = [
106
                $distance + $relation[1],
107
                ((empty($this->points[$pointId][1])) ? $pointId : $this->points[$pointId][1]) . ':' . $relation[0],
108
            ];
109
        } else {
110
            if ($this->points[$relation[0]][0] > ($this->points[$pointId][0] + $relation[1])) {
111
                $this->points[$relation[0]] = [
112
                    (isset($this->points[$pointId][0]) ? $this->points[$pointId][0] : 0) + $relation[1],
113
                    (empty($this->points[$pointId][1]) ? null : $this->points[$pointId][1] . ':') . $relation[0],
114
                ];
115
            }
116
        }
117
    }
118
119
    public function distances($point)
120
    {
121
        return DijkstraCache::loadOne($this->generated, $point, function () use ($point) {
122
            $this->init($point);
123
            $this->dist($point, $point);
124
            return $this->points;
125
        });
126
    }
127
128
    public static function validate($relations_array)
129
    {
130
        $return = is_array($relations_array);
131
132
        if ($return) {
133
            foreach ($relations_array as $relations) {
134
                $return &= self::checkPointRelations($relations);
135
            }
136
        }
137
138
        return $return;
139
    }
140
141
    private static function checkPointRelations($relations)
142
    {
143
        $return = is_array($relations);
144
145
        if ($return) {
146
            foreach ($relations as $relation) {
147
                $return &= self::checkSingleRelation($relation);
148
            }
149
        }
150
151
        return $return;
152
    }
153
154
    private static function checkSingleRelation($relation)
155
    {
156
        return (is_array($relation) && (count($relation) == 2) && isset($relation[0]) && isset($relation[1]));
157
    }
158
}
159