1<?php
2// (c) Copyright by authors of the Tiki Wiki CMS Groupware Project
3//
4// All Rights Reserved. See copyright.txt for details and a complete list of authors.
5// Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details.
6// $Id$
7
8/**
9 * This class implements the Dijkstra algorithm for finding the shortest path
10 * through a graph.
11 *
12 * For details on this algorithm, see:
13 *   http://en.wikipedia.org/wiki/Dijkstra%27s_algorithm
14 *
15 * For details on how to use the class, see the unit test file
16 * ShortestPathFinderTest.php.
17 */
18
19
20class Multilingual_Aligner_ShortestPathFinder
21{
22	var $visited = [];
23	var $distance = [];
24	var $previousNode = [];
25	var $startnode = null;
26	var $map = [];
27	var $infiniteDistance = 0;
28	var $numberOfNodes = 0;
29	var $bestPath = 0;
30	var $matrixWidth = 0;
31	var $shortestPathes = [];
32	var $nodes = [];
33
34	function __construct(&$ourMap, $infiniteDistance)
35	{
36		$this -> infiniteDistance = $infiniteDistance;
37		$this -> map = &$ourMap;
38		$this -> bestPath = 0;
39
40		$this->nodes = $this->_nodesInMmatrix($ourMap);
41		$this -> numberOfNodes = count($this->nodes);
42	}
43
44	function _nodesInMmatrix($distanceMatrix)
45	{
46		$nodes = [];
47		foreach (array_keys($distanceMatrix) as $originNode) {
48			if (! in_array($originNode, $nodes)) {
49				array_push($nodes, $originNode);
50			}
51
52			$distancesFromThisNode = $distanceMatrix[$originNode];
53			foreach (array_keys($distancesFromThisNode) as $destinationNode) {
54				if (! in_array($destinationNode, $nodes)) {
55					array_push($nodes, $destinationNode);
56				}
57			}
58		}
59		sort($nodes);
60
61		//		echo "-- _nodesInMmatrix: outputting nodes:\n"; var_dump($nodes);
62
63		return $nodes;
64	}
65
66	function computeShortestPathes($start, $to = null)
67	{
68		$this -> startnode = $start;
69		foreach ($this->nodes as $currNode) {
70			if ($currNode == $this -> startnode) {
71				$this -> visited[$currNode] = true;
72				$this -> distance[$currNode] = 0;
73			} else {
74				$this -> visited[$currNode] = false;
75				$this -> distance[$currNode] = isset($this -> map[$this -> startnode][$currNode])
76					? $this -> map[$this -> startnode][$currNode]
77					: $this -> infiniteDistance;
78			}
79			$this -> previousNode[$currNode] = $this -> startnode;
80		}
81
82		$maxTries = $this -> numberOfNodes;
83
84		//		echo "-- computeShortestPathes: \$maxTries=$maxTries\n";
85
86		$tries = 0;
87		while (in_array(false, $this -> visited, true) && $tries <= $maxTries) {
88			//		echo "-- computeShortestPathes: \$tries=$tries\n\$this->distance=\n";var_dump($this->distance);echo "\n\$this->previousNode=\n";var_dump($this->previousNode);print "\n";
89			$this -> bestPath = $this->findBestPath($this->distance, array_keys($this -> visited, false, true));
90			if ($to !== null && $this -> bestPath === $to) {
91				break;
92			}
93			$this -> updateDistanceAndPrevious($this -> bestPath);
94			$this -> visited[$this -> bestPath] = true;
95			$tries++;
96		}
97		$this -> shortestPathes = $this->getShortestPathesInfo();
98
99		return $this -> shortestPathes;
100	}
101
102	function findBestPath($ourDistance, $ourNodesLeft)
103	{
104		//		echo "-- findBestPath: \ourDistance=\n";var_dump($ourDistance);echo "\n\$ourNodesLeft=\n";var_dump($ourNodesLeft);
105		$bestPath = $this -> infiniteDistance;
106		$bestNode = null;
107		foreach ($ourNodesLeft as $currNode) {
108			//			echo "-- findBestPath: processing node: $currNode, \$ourDistance[\$currNode]=$ourDistance[$currNode], \$bestPath=$bestPath\n";
109			if ($ourDistance[$currNode] < $bestPath) {
110				$bestPath = $ourDistance[$currNode];
111				$bestNode = $currNode;
112			}
113		}
114			//		echo "-- findBestPath: upon exit, \$bestNode=$bestNode, \ourDistance=\n";var_dump($ourDistance);echo "\n\$ourNodesLeft=\n";var_dump($ourNodesLeft);
115		return $bestNode;
116	}
117
118	function updateDistanceAndPrevious($obp)
119	{
120		//		echo "-- updateDistanceAndPrevious: \$obp=$obp\n";
121		foreach ($this->nodes as $currNode) {
122			//			echo "-- updateDistanceAndPrevious: processing \$currNode=$currNode\n";
123			if ((isset($this->map[$obp][$currNode]))
124					&&	(! ($this->map[$obp][$currNode] == $this->infiniteDistance) || ($this->map[$obp][$currNode] == 0 ))
125					&&	(($this->distance[$obp] + $this->map[$obp][$currNode]) < $this -> distance[$currNode])
126			) {
127				//			echo "-- updateDistanceAndPrevious: found shorter rout to \$currNode, through \$obp.\n";
128				$this -> distance[$currNode] = $this -> distance[$obp] + $this -> map[$obp][$currNode];
129				$this -> previousNode[$currNode] = $obp;
130			}
131		}
132
133		//		echo "-- updateDistanceAndPrevious: upon exit, \$this->distance=\n";var_dump($this->distance);echo "\n\$this->previousNode=\n";var_dump($this->previousNode); echo"\n";
134	}
135
136	function shortestPathTo($destination_node_num)
137	{
138		return $this->shortestPathes[$destination_node_num];
139	}
140
141	function shortestDistanceTo($destination_node_num)
142	{
143		return $this->distance[$destination_node_num];
144	}
145
146	function printMap(&$map)
147	{
148		$placeholder = ' %' . strlen($this -> infiniteDistance) . 'd';
149		$foo = '';
150		for ($i = 0, $im = count($map); $i < $im; $i++) {
151			for ($k = 0, $m = $im; $k < $m; $k++) {
152				$foo .= sprintf($placeholder, isset($map[$i][$k]) ? $map[$i][$k] : $this -> infiniteDistance);
153			}
154			$foo .= "\n";
155		}
156		return $foo;
157	}
158
159	function getShortestPathesInfo($to = null)
160	{
161		$ourShortestPath = [];
162		foreach ($this->nodes as $aNode) {
163			if ($to !== null && $to !== $aNode) {
164				continue;
165			}
166			$ourShortestPath[$aNode] = [];
167			$endNode = null;
168			$currNode = $aNode;
169			$ourShortestPath[$aNode][] = $aNode;
170			while ($endNode === null || $endNode != $this -> startnode) {
171				$ourShortestPath[$aNode][] = $this -> previousNode[$currNode];
172				$endNode = $this -> previousNode[$currNode];
173				$currNode = $this -> previousNode[$currNode];
174			}
175			$ourShortestPath[$aNode] = array_reverse($ourShortestPath[$aNode]);
176			if ($to === null || $to === $aNode) {
177				if ($to === $aNode) {
178					break;
179				}
180			}
181		}
182		return $ourShortestPath;
183	}
184
185	function getResults($to = null)
186	{
187		$ourShortestPath = [];
188		$foo = '';
189		foreach ($this->nodes as $aNode) {
190			if ($to !== null && $to !== $aNode) {
191				continue;
192			}
193			$ourShortestPath[$aNode] = [];
194			$endNode = null;
195			$currNode = $aNode;
196			$ourShortestPath[$aNode][] = $aNode;
197			while ($endNode === null || $endNode != $this -> startnode) {
198				$ourShortestPath[$aNode][] = $this -> previousNode[$currNode];
199				$endNode = $this -> previousNode[$currNode];
200				$currNode = $this -> previousNode[$currNode];
201			}
202			$ourShortestPath[$aNode] = array_reverse($ourShortestPath[$aNode]);
203			if ($to === null || $to === $aNode) {
204				if ($this -> distance[$aNode] >= $this -> infiniteDistance) {
205					$foo .= sprintf("no route from %d to %d. \n", $this -> startnode, $aNode);
206				} else {
207					$foo .= sprintf(
208						'%d => %d = %d [%d]: (%s).' . "\n",
209						$this -> startnode,
210						$aNode,
211						$this -> distance[$aNode],
212						count($ourShortestPath[$aNode]),
213						implode('-', $ourShortestPath[$aNode])
214					);
215				}
216				$foo .= str_repeat('-', 20) . "\n";
217				if ($to === $aNode) {
218					break;
219				}
220			}
221		}
222		return $foo;
223	}
224}
225