@@ -16,6 +16,16 @@ interface IPath {
16
16
readonly weight : number ;
17
17
}
18
18
19
+ interface NodeBag {
20
+ readonly nodes : number ;
21
+ }
22
+
23
+ function checkWeight ( weight : number ) : void {
24
+ if ( weight < 0 ) {
25
+ throw new RangeError ( "weight must be >= 0" ) ;
26
+ }
27
+ }
28
+
19
29
/**
20
30
* Implementation of Dijkstra's Shortest Path algorithm. This quickly finds the shortest path between a single point
21
31
* and every other point it it connected to.
@@ -31,7 +41,11 @@ interface IPath {
31
41
* as our priority queue. All map-likes have been eliminated, but there are still object references. So this is
32
42
* not as fast as possible, but it should be plenty fast and not too heavy on memory.
33
43
*/
34
- export class DijkstraShortestPathSolver {
44
+ export class DijkstraShortestPathSolver implements NodeBag {
45
+ private readonly startNodeChecker = new NodeIndexChecker ( this , "startNode" ) ;
46
+ private readonly fromNodeChecker = new NodeIndexChecker ( this , "fromNode" ) ;
47
+ private readonly toNodeChecker = new NodeIndexChecker ( this , "toNode" ) ;
48
+
35
49
private constructor (
36
50
protected readonly adjacencyList : IEdge [ ] [ ] ,
37
51
) {
@@ -70,7 +84,7 @@ export class DijkstraShortestPathSolver {
70
84
/**
71
85
* The number of nodes in the graph. Nodes are numbered from `0` to `n-1`.
72
86
*/
73
- protected get nodes ( ) : number {
87
+ get nodes ( ) : number {
74
88
return this . adjacencyList . length ;
75
89
}
76
90
@@ -98,21 +112,9 @@ export class DijkstraShortestPathSolver {
98
112
* @param weight Weight of the edge. Must be greater than 0.
99
113
*/
100
114
addEdge ( fromNode : number , toNode : number , weight : number ) : void {
101
- if ( weight < 0 ) {
102
- throw new RangeError ( "weight must be >= 0" ) ;
103
- }
104
-
105
- if ( fromNode < 0 || fromNode >= this . nodes ) {
106
- throw new RangeError (
107
- `fromNode must be in range 0..${ this . nodes - 1 } : ${ fromNode } ` ,
108
- ) ;
109
- }
110
-
111
- if ( toNode < 0 || toNode >= this . nodes ) {
112
- throw new RangeError (
113
- `toNode must be in range 0..${ this . nodes - 1 } : ${ toNode } ` ,
114
- ) ;
115
- }
115
+ checkWeight ( weight ) ;
116
+ this . fromNodeChecker . check ( fromNode ) ;
117
+ this . toNodeChecker . check ( toNode ) ;
116
118
117
119
this . adjacencyList [ fromNode ] . push ( { toNode, weight } ) ;
118
120
}
@@ -124,41 +126,20 @@ export class DijkstraShortestPathSolver {
124
126
* @param weight Weight of the edge. Must be greater than 0.
125
127
*/
126
128
addBidirEdge ( fromNode : number , toNode : number , weight : number ) : void {
127
- if ( weight < 0 ) {
128
- throw new RangeError ( "weight must be >= 0" ) ;
129
- }
130
-
131
- if ( fromNode < 0 || fromNode >= this . nodes ) {
132
- throw new RangeError (
133
- `fromNode must be in range 0..${ this . nodes - 1 } : ${ fromNode } ` ,
134
- ) ;
135
- }
136
-
137
- if ( toNode < 0 || toNode >= this . nodes ) {
138
- throw new RangeError (
139
- `toNode must be in range 0..${ this . nodes - 1 } : ${ toNode } ` ,
140
- ) ;
141
- }
129
+ checkWeight ( weight ) ;
130
+ this . fromNodeChecker . check ( fromNode ) ;
131
+ this . toNodeChecker . check ( toNode ) ;
142
132
143
133
this . adjacencyList [ fromNode ] . push ( { toNode, weight } ) ;
144
134
this . adjacencyList [ toNode ] . push ( { toNode : fromNode , weight } ) ;
145
135
}
146
136
147
- // TODO: Not ready for general consumption.
148
- // setEdges(node: number, edges: IEdge[]): void {
149
- // this.adjacencyList[node] = edges;
150
- // }
151
-
152
137
/**
153
138
* Calculate shortest paths for all nodes for the given start node.
154
139
* @param startNode The start node.
155
140
*/
156
141
calculateFor ( startNode : number ) : ShortestPaths {
157
- if ( startNode < 0 || startNode >= this . nodes ) {
158
- throw new RangeError (
159
- `startNode must be in range 0..${ this . nodes - 1 } : ${ startNode } ` ,
160
- ) ;
161
- }
142
+ this . startNodeChecker . check ( startNode ) ;
162
143
163
144
const weights : number [ ] = new Array ( this . nodes ) . fill ( Infinity ) ;
164
145
weights [ startNode ] = 0 ;
@@ -193,7 +174,9 @@ export class DijkstraShortestPathSolver {
193
174
/**
194
175
* Shortest paths result.
195
176
*/
196
- export class ShortestPaths {
177
+ export class ShortestPaths implements NodeBag {
178
+ private readonly endNodeChecker = new NodeIndexChecker ( this , "endNode" ) ;
179
+
197
180
constructor (
198
181
public readonly nodes : number ,
199
182
public readonly startNode : number ,
@@ -207,11 +190,7 @@ export class ShortestPaths {
207
190
* @throws {@link Error } No path found.
208
191
*/
209
192
shortestPathTo ( endNode : number ) : number [ ] {
210
- if ( endNode < 0 || endNode >= this . nodes ) {
211
- throw new RangeError (
212
- `end-node must be in range 0 to ${ this . nodes - 1 } : ${ endNode } ` ,
213
- ) ;
214
- }
193
+ this . endNodeChecker . check ( endNode ) ;
215
194
216
195
const path = [ endNode ] ;
217
196
let lastStep = endNode ;
@@ -232,12 +211,35 @@ export class ShortestPaths {
232
211
* @param endNode The end node.
233
212
*/
234
213
weightOfPathTo ( endNode : number ) : number {
235
- if ( endNode < 0 || endNode >= this . nodes ) {
214
+ this . endNodeChecker . check ( endNode ) ;
215
+
216
+ return this . weights [ endNode ] ;
217
+ }
218
+ }
219
+
220
+ /**
221
+ * Simple range check for various node index inputs.
222
+ */
223
+ class NodeIndexChecker implements NodeBag {
224
+ constructor (
225
+ private readonly nodeBag : NodeBag ,
226
+ private readonly label : string ,
227
+ ) {
228
+ }
229
+
230
+ get nodes ( ) : number {
231
+ return this . nodeBag . nodes ;
232
+ }
233
+
234
+ check ( index : number ) : void {
235
+ if ( ! Number . isInteger ( index ) ) {
236
+ throw new RangeError ( `${ this . label } must be an integer: ${ index } ` ) ;
237
+ }
238
+
239
+ if ( index < 0 || index >= this . nodes ) {
236
240
throw new RangeError (
237
- `end-node must be in range 0 to ${ this . nodes - 1 } : ${ endNode } ` ,
241
+ `${ this . label } must be in range 0.. ${ this . nodes - 1 } : ${ index } ` ,
238
242
) ;
239
243
}
240
-
241
- return this . weights [ endNode ] ;
242
244
}
243
245
}
0 commit comments