Passed
Push — master ( 979da0...21dcf7 )
by Jesse
01:38
created

SingleDijkstraPathfinder::between()   A

Complexity

Conditions 6
Paths 6

Size

Total Lines 32
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 19
dl 0
loc 32
rs 9.0111
c 0
b 0
f 0
cc 6
nc 6
nop 2
1
<?php declare(strict_types=1);
2
3
namespace Stratadox\Pathfinder;
4
5
use const INF;
6
use SplPriorityQueue;
7
use Stratadox\Pathfinder\Reconstruction\PathRetracer;
8
9
final class SingleDijkstraPathfinder implements SinglePathfinder
10
{
11
    private $network;
12
    private $path;
13
14
    private function __construct(Network $network)
15
    {
16
        $this->network = $network;
17
        $this->path = new PathRetracer();
18
    }
19
20
    public static function operatingIn(Network $graph): SinglePathfinder
21
    {
22
        return new self($graph);
23
    }
24
25
    public function between(string $start, string $goal): array
26
    {
27
        if (!$this->network->has($start)) {
28
            throw NonExistingStart::tried($start);
29
        }
30
31
        $distanceTo = [$start => 0];
32
        $lastStepBefore = [];
33
34
        $underConsideration = new SplPriorityQueue();
35
        $underConsideration->insert($start, 0);
36
37
        while (!$underConsideration->isEmpty()) {
38
            $node = $underConsideration->top();
39
            $underConsideration->next();
40
41
            if ($goal === $node) {
42
                return $this->path->retrace($start, $goal, $lastStepBefore);
43
            }
44
45
            foreach ($this->network->neighboursOf($node) as $neighbour) {
46
                $alternativeCost = ($distanceTo[$node] ?? INF) +
47
                    $this->network->movementCostBetween($node, $neighbour);
48
49
                if ($alternativeCost < ($distanceTo[$neighbour] ?? INF)) {
50
                    $distanceTo[$neighbour] = $alternativeCost;
51
                    $lastStepBefore[$neighbour] = $node;
52
                    $underConsideration->insert($neighbour, -$alternativeCost);
53
                }
54
            }
55
        }
56
        throw NoSuchPath::between($start, $goal);
57
    }
58
}
59