Skip to content

Commit 5aa0141

Browse files
committed
dry index checking code; added integer check
1 parent bf23c4a commit 5aa0141

File tree

1 file changed

+54
-52
lines changed

1 file changed

+54
-52
lines changed

dijkstra.ts

Lines changed: 54 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,16 @@ interface IPath {
1616
readonly weight: number;
1717
}
1818

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+
1929
/**
2030
* Implementation of Dijkstra's Shortest Path algorithm. This quickly finds the shortest path between a single point
2131
* and every other point it it connected to.
@@ -31,7 +41,11 @@ interface IPath {
3141
* as our priority queue. All map-likes have been eliminated, but there are still object references. So this is
3242
* not as fast as possible, but it should be plenty fast and not too heavy on memory.
3343
*/
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+
3549
private constructor(
3650
protected readonly adjacencyList: IEdge[][],
3751
) {
@@ -70,7 +84,7 @@ export class DijkstraShortestPathSolver {
7084
/**
7185
* The number of nodes in the graph. Nodes are numbered from `0` to `n-1`.
7286
*/
73-
protected get nodes(): number {
87+
get nodes(): number {
7488
return this.adjacencyList.length;
7589
}
7690

@@ -98,21 +112,9 @@ export class DijkstraShortestPathSolver {
98112
* @param weight Weight of the edge. Must be greater than 0.
99113
*/
100114
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);
116118

117119
this.adjacencyList[fromNode].push({ toNode, weight });
118120
}
@@ -124,41 +126,20 @@ export class DijkstraShortestPathSolver {
124126
* @param weight Weight of the edge. Must be greater than 0.
125127
*/
126128
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);
142132

143133
this.adjacencyList[fromNode].push({ toNode, weight });
144134
this.adjacencyList[toNode].push({ toNode: fromNode, weight });
145135
}
146136

147-
// TODO: Not ready for general consumption.
148-
// setEdges(node: number, edges: IEdge[]): void {
149-
// this.adjacencyList[node] = edges;
150-
// }
151-
152137
/**
153138
* Calculate shortest paths for all nodes for the given start node.
154139
* @param startNode The start node.
155140
*/
156141
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);
162143

163144
const weights: number[] = new Array(this.nodes).fill(Infinity);
164145
weights[startNode] = 0;
@@ -193,7 +174,9 @@ export class DijkstraShortestPathSolver {
193174
/**
194175
* Shortest paths result.
195176
*/
196-
export class ShortestPaths {
177+
export class ShortestPaths implements NodeBag {
178+
private readonly endNodeChecker = new NodeIndexChecker(this, "endNode");
179+
197180
constructor(
198181
public readonly nodes: number,
199182
public readonly startNode: number,
@@ -207,11 +190,7 @@ export class ShortestPaths {
207190
* @throws {@link Error} No path found.
208191
*/
209192
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);
215194

216195
const path = [endNode];
217196
let lastStep = endNode;
@@ -232,12 +211,35 @@ export class ShortestPaths {
232211
* @param endNode The end node.
233212
*/
234213
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) {
236240
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}`,
238242
);
239243
}
240-
241-
return this.weights[endNode];
242244
}
243245
}

0 commit comments

Comments
 (0)