bmssp 0.16.0 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/heap.mjs +167 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bmssp",
3
- "version": "0.16.0",
3
+ "version": "0.17.0",
4
4
  "description": "Javascript package implementation of the bmssp algorithm.",
5
5
  "main": "index.mjs",
6
6
  "keywords": [
package/src/heap.mjs ADDED
@@ -0,0 +1,167 @@
1
+ /**
2
+ * Array-backed indexed binary min-heap for the BMSSP base case (Algorithm 2
3
+ * of "Breaking the Sorting Barrier for Directed Single-Source Shortest
4
+ * Paths"). Holds <key, value> pairs (vertex, distance estimate) ordered by
5
+ * value and supports exactly the operations BaseCase(B, S) needs:
6
+ *
7
+ * insert(key, value) — add a key not currently stored
8
+ * extractMin() — remove and return the smallest-valued pair
9
+ * decreaseKey(key, value) — lower the value of a stored key
10
+ * has(key) — membership test ("is v in H?")
11
+ *
12
+ * A position map (key -> array index) makes decreaseKey and has O(log n) /
13
+ * O(1), matching the paper's heap literally instead of the lazy
14
+ * duplicate-and-skip variant used inside src/dijkstra.mjs.
15
+ */
16
+ class MinHeap {
17
+ constructor() {
18
+ // entries[i] = [key, value], heap-ordered by value
19
+ this.entries = [];
20
+ // key -> index of that key in entries, for O(1) membership / lookup
21
+ this.position = new Map();
22
+ }
23
+
24
+ // Number of pairs currently stored
25
+ get size() {
26
+ return this.entries.length;
27
+ }
28
+
29
+ isEmpty() {
30
+ return this.entries.length === 0;
31
+ }
32
+
33
+ has(key) {
34
+ return this.position.has(key);
35
+ }
36
+
37
+ /**
38
+ * Current value of a stored key, or undefined if the key is not present.
39
+ * @param {*} key - Typically a node ID
40
+ * @returns {number|undefined}
41
+ */
42
+ getValue(key) {
43
+ const index = this.position.get(key);
44
+ return index === undefined ? undefined : this.entries[index][1];
45
+ }
46
+
47
+ /**
48
+ * Smallest-valued pair without removing it.
49
+ * @returns {{ key: *, value: number }}
50
+ * @throws {Error} If the heap is empty
51
+ */
52
+ peekMin() {
53
+ if (this.entries.length === 0) {
54
+ throw new Error("heap is empty");
55
+ }
56
+ const [key, value] = this.entries[0];
57
+ return { key, value };
58
+ }
59
+
60
+ /**
61
+ * Add a pair for a key that is not currently stored (Algorithm 2 Insert).
62
+ * A key that was extracted earlier may be inserted again.
63
+ * @param {*} key - Typically a node ID
64
+ * @param {number} value - Priority; smaller comes out first
65
+ * @throws {Error} If the key is already stored, or value is not a number
66
+ */
67
+ insert(key, value) {
68
+ if (typeof value !== "number" || Number.isNaN(value)) {
69
+ throw new Error("value must be a number");
70
+ }
71
+ if (this.position.has(key)) {
72
+ throw new Error("key already in heap — use decreaseKey");
73
+ }
74
+ this.entries.push([key, value]);
75
+ this.position.set(key, this.entries.length - 1);
76
+ this.siftUp(this.entries.length - 1);
77
+ }
78
+
79
+ /**
80
+ * Lower the value of a stored key (Algorithm 2 DecreaseKey). A value that
81
+ * would not decrease the stored one is ignored — the smallest value wins,
82
+ * mirroring the `<=` edge relaxation which re-relaxes with equal sums.
83
+ * @param {*} key - Must be currently stored
84
+ * @param {number} value - New priority; applied only when smaller
85
+ * @throws {Error} If the key is not stored, or value is not a number
86
+ */
87
+ decreaseKey(key, value) {
88
+ if (typeof value !== "number" || Number.isNaN(value)) {
89
+ throw new Error("value must be a number");
90
+ }
91
+ const index = this.position.get(key);
92
+ if (index === undefined) {
93
+ throw new Error("key not in heap — use insert");
94
+ }
95
+ if (this.entries[index][1] <= value) return;
96
+ this.entries[index][1] = value;
97
+ this.siftUp(index);
98
+ }
99
+
100
+ /**
101
+ * Remove and return the smallest-valued pair (Algorithm 2 ExtractMin).
102
+ * @returns {{ key: *, value: number }}
103
+ * @throws {Error} If the heap is empty
104
+ */
105
+ extractMin() {
106
+ if (this.entries.length === 0) {
107
+ throw new Error("heap is empty");
108
+ }
109
+ const [key, value] = this.entries[0];
110
+ this.position.delete(key);
111
+ const last = this.entries.pop();
112
+ if (this.entries.length > 0) {
113
+ this.entries[0] = last;
114
+ this.position.set(last[0], 0);
115
+ this.siftDown(0);
116
+ }
117
+ return { key, value };
118
+ }
119
+
120
+ // Internal: swap two entries and keep the position map in sync
121
+ swap(i, j) {
122
+ const a = this.entries[i];
123
+ const b = this.entries[j];
124
+ this.entries[i] = b;
125
+ this.entries[j] = a;
126
+ this.position.set(b[0], i);
127
+ this.position.set(a[0], j);
128
+ }
129
+
130
+ // Internal: move the entry at index up until its parent is not larger
131
+ siftUp(index) {
132
+ let i = index;
133
+ while (i > 0) {
134
+ const parent = (i - 1) >> 1;
135
+ if (this.entries[parent][1] <= this.entries[i][1]) break;
136
+ this.swap(parent, i);
137
+ i = parent;
138
+ }
139
+ }
140
+
141
+ // Internal: move the entry at index down until no child is smaller
142
+ siftDown(index) {
143
+ let i = index;
144
+ for (;;) {
145
+ const left = 2 * i + 1;
146
+ const right = left + 1;
147
+ let smallest = i;
148
+ if (
149
+ left < this.entries.length &&
150
+ this.entries[left][1] < this.entries[smallest][1]
151
+ ) {
152
+ smallest = left;
153
+ }
154
+ if (
155
+ right < this.entries.length &&
156
+ this.entries[right][1] < this.entries[smallest][1]
157
+ ) {
158
+ smallest = right;
159
+ }
160
+ if (smallest === i) break;
161
+ this.swap(smallest, i);
162
+ i = smallest;
163
+ }
164
+ }
165
+ }
166
+
167
+ export { MinHeap };