oakscriptjs 0.9.4 → 0.9.6

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 (177) hide show
  1. package/dist/cjs/array/index.d.ts +88 -27
  2. package/dist/cjs/array/index.d.ts.map +1 -1
  3. package/dist/cjs/array/index.js +257 -117
  4. package/dist/cjs/array/index.js.map +3 -3
  5. package/dist/cjs/callsite/index.d.ts +122 -4
  6. package/dist/cjs/callsite/index.d.ts.map +1 -1
  7. package/dist/cjs/callsite/index.js +102 -3
  8. package/dist/cjs/callsite/index.js.map +2 -2
  9. package/dist/cjs/color/index.d.ts +7 -3
  10. package/dist/cjs/color/index.d.ts.map +1 -1
  11. package/dist/cjs/color/index.js +6 -1
  12. package/dist/cjs/color/index.js.map +2 -2
  13. package/dist/cjs/index.d.ts +1 -1
  14. package/dist/cjs/index.js +1 -1
  15. package/dist/cjs/index.js.map +1 -1
  16. package/dist/cjs/label/index.d.ts.map +1 -1
  17. package/dist/cjs/label/index.js +2 -1
  18. package/dist/cjs/label/index.js.map +2 -2
  19. package/dist/cjs/linefill/index.d.ts.map +1 -1
  20. package/dist/cjs/linefill/index.js +1 -0
  21. package/dist/cjs/linefill/index.js.map +2 -2
  22. package/dist/cjs/math/index.d.ts +79 -22
  23. package/dist/cjs/math/index.d.ts.map +1 -1
  24. package/dist/cjs/math/index.js +157 -43
  25. package/dist/cjs/math/index.js.map +3 -3
  26. package/dist/cjs/matrix/index.d.ts.map +1 -1
  27. package/dist/cjs/matrix/index.js +42 -36
  28. package/dist/cjs/matrix/index.js.map +2 -2
  29. package/dist/cjs/runtime/series.d.ts +9 -0
  30. package/dist/cjs/runtime/series.d.ts.map +1 -1
  31. package/dist/cjs/runtime/series.js +16 -2
  32. package/dist/cjs/runtime/series.js.map +2 -2
  33. package/dist/cjs/script/index.d.ts +15 -31
  34. package/dist/cjs/script/index.d.ts.map +1 -1
  35. package/dist/cjs/script/index.js +32 -0
  36. package/dist/cjs/script/index.js.map +2 -2
  37. package/dist/cjs/session/calendar.d.ts.map +1 -1
  38. package/dist/cjs/session/calendar.js +15 -2
  39. package/dist/cjs/session/calendar.js.map +2 -2
  40. package/dist/cjs/str/index.d.ts +50 -38
  41. package/dist/cjs/str/index.d.ts.map +1 -1
  42. package/dist/cjs/str/index.js +43 -15
  43. package/dist/cjs/str/index.js.map +2 -2
  44. package/dist/cjs/str/messageformat.d.ts +2 -1
  45. package/dist/cjs/str/messageformat.d.ts.map +1 -1
  46. package/dist/cjs/str/messageformat.js +4 -1
  47. package/dist/cjs/str/messageformat.js.map +2 -2
  48. package/dist/cjs/str/numberformat.d.ts +4 -0
  49. package/dist/cjs/str/numberformat.d.ts.map +1 -1
  50. package/dist/cjs/str/numberformat.js +26 -4
  51. package/dist/cjs/str/numberformat.js.map +3 -3
  52. package/dist/cjs/ta/index.d.ts +95 -46
  53. package/dist/cjs/ta/index.d.ts.map +1 -1
  54. package/dist/cjs/ta/index.js +193 -318
  55. package/dist/cjs/ta/index.js.map +3 -3
  56. package/dist/cjs/ta/running-sum.d.ts +54 -1
  57. package/dist/cjs/ta/running-sum.d.ts.map +1 -1
  58. package/dist/cjs/ta/running-sum.js +132 -47
  59. package/dist/cjs/ta/running-sum.js.map +2 -2
  60. package/dist/cjs/ta-series.d.ts +4 -4
  61. package/dist/cjs/ta-series.d.ts.map +1 -1
  62. package/dist/cjs/ta-series.js +4 -3
  63. package/dist/cjs/ta-series.js.map +2 -2
  64. package/dist/cjs/time/datestring.d.ts +1 -0
  65. package/dist/cjs/time/datestring.d.ts.map +1 -1
  66. package/dist/cjs/time/datestring.js +7 -0
  67. package/dist/cjs/time/datestring.js.map +2 -2
  68. package/dist/cjs/time/index.d.ts.map +1 -1
  69. package/dist/cjs/time/index.js +2 -1
  70. package/dist/cjs/time/index.js.map +2 -2
  71. package/dist/cjs/time/session.d.ts +28 -9
  72. package/dist/cjs/time/session.d.ts.map +1 -1
  73. package/dist/cjs/time/session.js +74 -28
  74. package/dist/cjs/time/session.js.map +2 -2
  75. package/dist/cjs/time/timezone.d.ts +5 -0
  76. package/dist/cjs/time/timezone.d.ts.map +1 -1
  77. package/dist/cjs/time/timezone.js +15 -1
  78. package/dist/cjs/time/timezone.js.map +2 -2
  79. package/dist/esm/array/index.d.ts +88 -27
  80. package/dist/esm/array/index.d.ts.map +1 -1
  81. package/dist/esm/array/index.js +408 -157
  82. package/dist/esm/array/index.js.map +1 -1
  83. package/dist/esm/callsite/index.d.ts +122 -4
  84. package/dist/esm/callsite/index.d.ts.map +1 -1
  85. package/dist/esm/callsite/index.js +228 -8
  86. package/dist/esm/callsite/index.js.map +1 -1
  87. package/dist/esm/color/index.d.ts +7 -3
  88. package/dist/esm/color/index.d.ts.map +1 -1
  89. package/dist/esm/color/index.js +15 -3
  90. package/dist/esm/color/index.js.map +1 -1
  91. package/dist/esm/index.d.ts +1 -1
  92. package/dist/esm/index.js +1 -1
  93. package/dist/esm/label/index.d.ts.map +1 -1
  94. package/dist/esm/label/index.js +2 -1
  95. package/dist/esm/label/index.js.map +1 -1
  96. package/dist/esm/linefill/index.d.ts.map +1 -1
  97. package/dist/esm/linefill/index.js +3 -0
  98. package/dist/esm/linefill/index.js.map +1 -1
  99. package/dist/esm/math/index.d.ts +79 -22
  100. package/dist/esm/math/index.d.ts.map +1 -1
  101. package/dist/esm/math/index.js +229 -63
  102. package/dist/esm/math/index.js.map +1 -1
  103. package/dist/esm/matrix/index.d.ts.map +1 -1
  104. package/dist/esm/matrix/index.js +57 -43
  105. package/dist/esm/matrix/index.js.map +1 -1
  106. package/dist/esm/runtime/series.d.ts +9 -0
  107. package/dist/esm/runtime/series.d.ts.map +1 -1
  108. package/dist/esm/runtime/series.js +18 -1
  109. package/dist/esm/runtime/series.js.map +1 -1
  110. package/dist/esm/script/index.d.ts +15 -31
  111. package/dist/esm/script/index.d.ts.map +1 -1
  112. package/dist/esm/script/index.js +44 -1
  113. package/dist/esm/script/index.js.map +1 -1
  114. package/dist/esm/session/calendar.d.ts.map +1 -1
  115. package/dist/esm/session/calendar.js +22 -2
  116. package/dist/esm/session/calendar.js.map +1 -1
  117. package/dist/esm/str/index.d.ts +50 -38
  118. package/dist/esm/str/index.d.ts.map +1 -1
  119. package/dist/esm/str/index.js +103 -52
  120. package/dist/esm/str/index.js.map +1 -1
  121. package/dist/esm/str/messageformat.d.ts +2 -1
  122. package/dist/esm/str/messageformat.d.ts.map +1 -1
  123. package/dist/esm/str/messageformat.js +7 -2
  124. package/dist/esm/str/messageformat.js.map +1 -1
  125. package/dist/esm/str/numberformat.d.ts +4 -0
  126. package/dist/esm/str/numberformat.d.ts.map +1 -1
  127. package/dist/esm/str/numberformat.js +38 -4
  128. package/dist/esm/str/numberformat.js.map +1 -1
  129. package/dist/esm/ta/index.d.ts +95 -46
  130. package/dist/esm/ta/index.d.ts.map +1 -1
  131. package/dist/esm/ta/index.js +341 -414
  132. package/dist/esm/ta/index.js.map +1 -1
  133. package/dist/esm/ta/running-sum.d.ts +54 -1
  134. package/dist/esm/ta/running-sum.d.ts.map +1 -1
  135. package/dist/esm/ta/running-sum.js +166 -60
  136. package/dist/esm/ta/running-sum.js.map +1 -1
  137. package/dist/esm/ta-series.d.ts +4 -4
  138. package/dist/esm/ta-series.d.ts.map +1 -1
  139. package/dist/esm/ta-series.js +5 -4
  140. package/dist/esm/ta-series.js.map +1 -1
  141. package/dist/esm/time/datestring.d.ts +1 -0
  142. package/dist/esm/time/datestring.d.ts.map +1 -1
  143. package/dist/esm/time/datestring.js +12 -0
  144. package/dist/esm/time/datestring.js.map +1 -1
  145. package/dist/esm/time/index.d.ts.map +1 -1
  146. package/dist/esm/time/index.js +3 -1
  147. package/dist/esm/time/index.js.map +1 -1
  148. package/dist/esm/time/session.d.ts +28 -9
  149. package/dist/esm/time/session.d.ts.map +1 -1
  150. package/dist/esm/time/session.js +113 -42
  151. package/dist/esm/time/session.js.map +1 -1
  152. package/dist/esm/time/timezone.d.ts +5 -0
  153. package/dist/esm/time/timezone.d.ts.map +1 -1
  154. package/dist/esm/time/timezone.js +21 -1
  155. package/dist/esm/time/timezone.js.map +1 -1
  156. package/package.json +2 -2
  157. package/src/array/index.ts +382 -174
  158. package/src/callsite/index.ts +232 -8
  159. package/src/color/index.ts +15 -5
  160. package/src/index.ts +1 -1
  161. package/src/label/index.ts +2 -1
  162. package/src/linefill/index.ts +2 -0
  163. package/src/math/index.ts +261 -77
  164. package/src/matrix/index.ts +52 -52
  165. package/src/runtime/series.ts +22 -1
  166. package/src/script/index.ts +53 -1
  167. package/src/session/calendar.ts +22 -2
  168. package/src/str/index.ts +95 -51
  169. package/src/str/messageformat.ts +7 -2
  170. package/src/str/numberformat.ts +39 -4
  171. package/src/ta/index.ts +356 -458
  172. package/src/ta/running-sum.ts +187 -62
  173. package/src/ta-series.ts +8 -7
  174. package/src/time/datestring.ts +10 -0
  175. package/src/time/index.ts +3 -1
  176. package/src/time/session.ts +97 -47
  177. package/src/time/timezone.ts +21 -1
@@ -85,11 +85,27 @@ function new_array(size2 = 0, initial_value) {
85
85
  function size(id) {
86
86
  return id.length;
87
87
  }
88
+ function isNa(v) {
89
+ return v === void 0 || v === null || typeof v === "number" && Number.isNaN(v);
90
+ }
91
+ function naOf(id) {
92
+ return id.length === 0 || typeof id[0] === "number" ? NaN : void 0;
93
+ }
94
+ function position(name, id, index, extra = 0) {
95
+ const i = Math.trunc(index);
96
+ const n = id.length;
97
+ if (i < -n || i >= n + extra) {
98
+ throw new Error(`array.${name}: index ${i} is out of bounds, array size is ${n}`);
99
+ }
100
+ return i < 0 ? i + n : i;
101
+ }
88
102
  function get(id, index) {
89
- return id[index];
103
+ if (Number.isNaN(index)) return naOf(id);
104
+ return id[position("get", id, index)];
90
105
  }
91
106
  function set(id, index, value) {
92
- id[index] = value;
107
+ if (Number.isNaN(index)) return;
108
+ id[position("set", id, index)] = value;
93
109
  }
94
110
  function push(id, value) {
95
111
  id.push(value);
@@ -107,25 +123,46 @@ function clear(id) {
107
123
  id.length = 0;
108
124
  }
109
125
  function insert(id, index, value) {
110
- id.splice(index, 0, value);
126
+ if (Number.isNaN(index)) {
127
+ id.push(value);
128
+ return;
129
+ }
130
+ id.splice(position("insert", id, index, 1), 0, value);
111
131
  }
112
132
  function remove(id, index) {
113
- return id.splice(index, 1)[0];
133
+ if (Number.isNaN(index)) return naOf(id);
134
+ return id.splice(position("remove", id, index), 1)[0];
135
+ }
136
+ function pineEqual(a, b) {
137
+ if (isNa(a) || isNa(b)) return false;
138
+ if (a === b) return true;
139
+ if (typeof a === "number" && typeof b === "number") {
140
+ const d = a - b;
141
+ return -1e-10 <= d && d <= 1e-10;
142
+ }
143
+ return false;
114
144
  }
115
145
  function includes(id, value) {
116
- return id.includes(value);
146
+ return indexof(id, value) >= 0;
117
147
  }
118
148
  function indexof(id, value) {
119
- return id.indexOf(value);
149
+ for (let i = 0; i < id.length; i++) {
150
+ if (pineEqual(id[i], value)) return i;
151
+ }
152
+ return -1;
120
153
  }
121
154
  function lastindexof(id, value) {
122
- return id.lastIndexOf(value);
155
+ for (let i = id.length - 1; i >= 0; i--) {
156
+ if (pineEqual(id[i], value)) return i;
157
+ }
158
+ return -1;
123
159
  }
124
160
  function copy(id) {
125
161
  return [...id];
126
162
  }
127
163
  function concat(id1, id2) {
128
- return [...id1, ...id2];
164
+ for (const v of Array.from(id2)) id1.push(v);
165
+ return id1;
129
166
  }
130
167
  function join(id, separator = ",") {
131
168
  return id.join(separator);
@@ -134,64 +171,162 @@ function reverse(id) {
134
171
  id.reverse();
135
172
  }
136
173
  function slice(id, index_from, index_to) {
137
- return id.slice(index_from, index_to);
138
- }
139
- function sort(id, order = "asc") {
140
- id.sort((a, b) => {
141
- if (order === "asc") {
142
- return a - b;
143
- } else {
144
- return b - a;
174
+ const n = id.length;
175
+ const start = Number.isNaN(index_from) ? 0 : Math.trunc(index_from);
176
+ const stop = index_to === void 0 || Number.isNaN(index_to) ? n : Math.trunc(index_to);
177
+ if (start < 0 || start >= n) throw new Error(`array.slice: index ${start} is out of bounds, array size is ${n}`);
178
+ if (stop < 0 || stop > n) throw new Error(`array.slice: index ${stop} is out of bounds, array size is ${n}`);
179
+ if (start > stop) throw new Error(`array.slice: index_from ${start} is greater than index_to ${stop}`);
180
+ return sliceView(id, start, stop - start);
181
+ }
182
+ function indexKey(p) {
183
+ if (typeof p !== "string") return -1;
184
+ const i = Number(p);
185
+ return Number.isInteger(i) && i >= 0 && String(i) === p ? i : -1;
186
+ }
187
+ function sliceView(parent, start, size2) {
188
+ const resize = (target) => {
189
+ if (target > size2) parent.splice(start + size2, 0, ...Array(target - size2).fill(void 0));
190
+ else if (target < size2) parent.splice(start + target, size2 - target);
191
+ size2 = target;
192
+ };
193
+ return new Proxy([], {
194
+ get(target, p, receiver) {
195
+ const i = indexKey(p);
196
+ if (i >= 0) return i < size2 ? parent[start + i] : void 0;
197
+ if (p === "length") return size2;
198
+ return Reflect.get(target, p, receiver);
199
+ },
200
+ set(target, p, value, receiver) {
201
+ const i = indexKey(p);
202
+ if (i >= 0) {
203
+ if (i >= size2) resize(i + 1);
204
+ parent[start + i] = value;
205
+ return true;
206
+ }
207
+ if (p === "length") {
208
+ resize(Number(value));
209
+ return true;
210
+ }
211
+ return Reflect.set(target, p, value, receiver);
212
+ },
213
+ has(target, p) {
214
+ const i = indexKey(p);
215
+ return i >= 0 ? i < size2 : Reflect.has(target, p);
216
+ },
217
+ // Array methods delete the slots past the new end before they set the length, which does the removal.
218
+ deleteProperty(target, p) {
219
+ return indexKey(p) >= 0 ? true : Reflect.deleteProperty(target, p);
220
+ },
221
+ ownKeys(target) {
222
+ return [...Array.from({ length: size2 }, (_, i) => String(i)), ...Reflect.ownKeys(target)];
223
+ },
224
+ getOwnPropertyDescriptor(target, p) {
225
+ const i = indexKey(p);
226
+ if (i >= 0) {
227
+ return i < size2 ? { value: parent[start + i], writable: true, enumerable: true, configurable: true } : void 0;
228
+ }
229
+ if (p === "length") return { value: size2, writable: true, enumerable: false, configurable: false };
230
+ return Reflect.getOwnPropertyDescriptor(target, p);
145
231
  }
146
232
  });
147
233
  }
234
+ function sortedPositions(id, order) {
235
+ const values = [];
236
+ const nas = [];
237
+ for (let i = 0; i < id.length; i++) (isNa(id[i]) ? nas : values).push(i);
238
+ values.sort((a, b) => {
239
+ const x = id[a];
240
+ const y = id[b];
241
+ return x < y ? -1 : x > y ? 1 : 0;
242
+ });
243
+ const naFirst = values.length > 0 && typeof id[values[0]] === "string";
244
+ const positions = naFirst ? [...nas, ...values] : [...values, ...nas];
245
+ if (order === "desc" || order === "descending") positions.reverse();
246
+ return positions;
247
+ }
248
+ function sort(id, order = "asc") {
249
+ const sorted = sortedPositions(id, order).map((p) => id[p]);
250
+ for (let i = 0; i < sorted.length; i++) id[i] = sorted[i];
251
+ }
148
252
  function sum(id) {
149
- return id.reduce((acc, val) => acc + val, 0);
253
+ let total = 0;
254
+ let count = 0;
255
+ for (const v of id) {
256
+ if (Number.isNaN(v)) continue;
257
+ count++;
258
+ total += v;
259
+ if (Math.abs(total) <= 1e-10) total = 0;
260
+ }
261
+ return count ? total : NaN;
150
262
  }
151
263
  function avg(id) {
152
264
  const values = id.filter((v) => !Number.isNaN(v));
153
265
  return values.length ? values.reduce((a, b) => a + b, 0) / values.length : NaN;
154
266
  }
267
+ function nthValue(name, id, nth, compare) {
268
+ const rank = Number.isNaN(nth) ? 0 : Math.trunc(nth);
269
+ const values = id.filter((v) => !Number.isNaN(v));
270
+ if (!values.length) return NaN;
271
+ if (rank < 0 || rank >= id.length) {
272
+ throw new Error(`array.${name}: nth ${rank} is out of bounds, array size is ${id.length}`);
273
+ }
274
+ values.sort(compare);
275
+ return values[Math.min(rank, values.length - 1)];
276
+ }
155
277
  function min(id, nth = 0) {
156
- const values = id.filter((v) => !Number.isNaN(v)).sort((a, b) => a - b);
157
- return values.length ? values[Math.min(nth, values.length - 1)] : NaN;
278
+ return nthValue("min", id, nth, (a, b) => a - b);
158
279
  }
159
280
  function max(id, nth = 0) {
160
- const values = id.filter((v) => !Number.isNaN(v)).sort((a, b) => b - a);
161
- return values.length ? values[Math.min(nth, values.length - 1)] : NaN;
281
+ return nthValue("max", id, nth, (a, b) => b - a);
162
282
  }
163
283
  function median(id) {
164
- const sorted = [...id].sort((a, b) => a - b);
284
+ const sorted = id.filter((v) => !Number.isNaN(v)).sort((a, b) => a - b);
285
+ if (!sorted.length) return NaN;
165
286
  const mid = Math.floor(sorted.length / 2);
166
287
  return sorted.length % 2 === 0 ? (sorted[mid - 1] + sorted[mid]) / 2 : sorted[mid];
167
288
  }
168
289
  function mode(id) {
169
290
  const frequency = /* @__PURE__ */ new Map();
170
- let maxFreq = 0;
171
- let mode2 = id[0];
172
- for (const val of id) {
173
- const freq = (frequency.get(val) || 0) + 1;
174
- frequency.set(val, freq);
175
- if (freq > maxFreq) {
176
- maxFreq = freq;
177
- mode2 = val;
291
+ for (const v of id) {
292
+ if (!Number.isNaN(v)) frequency.set(v, (frequency.get(v) ?? 0) + 1);
293
+ }
294
+ let best = NaN;
295
+ let bestCount = 0;
296
+ for (const [v, count] of frequency) {
297
+ if (count > bestCount || count === bestCount && v < best) {
298
+ best = v;
299
+ bestCount = count;
178
300
  }
179
301
  }
180
- return mode2;
302
+ return best;
181
303
  }
182
304
  function stdev(id, biased = true) {
183
305
  return Math.sqrt(variance(id, biased));
184
306
  }
307
+ function moments(values) {
308
+ let p = 0;
309
+ let q = 0;
310
+ for (const v of values) p += v;
311
+ for (const v of values) q += v * v;
312
+ return [p, q];
313
+ }
185
314
  function variance(id, biased = true) {
186
315
  const values = id.filter((v) => !Number.isNaN(v));
187
- if (!values.length) return NaN;
188
- const mean = values.reduce((a, b) => a + b, 0) / values.length;
189
- const squares = values.reduce((a, b) => a + (b - mean) * (b - mean), 0);
190
- return squares / (biased ? values.length : values.length - 1);
316
+ const n = values.length;
317
+ if (!n) return NaN;
318
+ if (n < 2) return biased ? 0 : NaN;
319
+ const [p, q] = moments(values);
320
+ const mean = p / n;
321
+ return biased ? Math.max(0, q / n - mean * mean) : Math.max(0, q / (n - 1) - p / (n - 1) * mean);
191
322
  }
192
323
  function fill(id, value, index_from = 0, index_to) {
193
- const end = index_to ?? id.length;
194
- for (let i = index_from; i < end; i++) {
324
+ const start = index_from === void 0 || index_from === null || Number.isNaN(index_from) ? 0 : index_from;
325
+ const end = index_to === void 0 || index_to === null || Number.isNaN(index_to) ? id.length : index_to;
326
+ for (const i of [start, end]) {
327
+ if (i < 0 || i > id.length) throw new Error(`array.fill: index ${i} is out of bounds, array size is ${id.length}`);
328
+ }
329
+ for (let i = start; i < end; i++) {
195
330
  id[i] = value;
196
331
  }
197
332
  }
@@ -199,9 +334,11 @@ function from(...values) {
199
334
  return [...values];
200
335
  }
201
336
  function first(id) {
337
+ if (id.length === 0) throw new Error("Cannot call array.first() if array is empty");
202
338
  return id[0];
203
339
  }
204
340
  function last(id) {
341
+ if (id.length === 0) throw new Error("Cannot call array.last() if array is empty");
205
342
  return id[id.length - 1];
206
343
  }
207
344
  function some(id) {
@@ -263,129 +400,132 @@ function binary_search(id, val) {
263
400
  return -1;
264
401
  }
265
402
  function binary_search_leftmost(id, val) {
266
- let left = 0;
267
- let right = id.length - 1;
268
- let result = -1;
269
- let found = false;
270
- while (left <= right) {
271
- const mid = Math.floor((left + right) / 2);
272
- if (id[mid] < val) {
273
- if (!found) {
274
- result = mid;
275
- }
276
- left = mid + 1;
277
- } else if (id[mid] === val) {
278
- result = mid;
279
- found = true;
280
- right = mid - 1;
281
- } else {
282
- right = mid - 1;
283
- }
403
+ if (id.length === 0) return -1;
404
+ const at = searchBound(id, val, false);
405
+ return at < id.length && compareSorted(id[at], val) === 0 ? at : Math.max(0, at - 1);
406
+ }
407
+ function compareSorted(a, b) {
408
+ const an = Number.isNaN(a);
409
+ const bn = Number.isNaN(b);
410
+ if (an || bn) return an === bn ? 0 : an ? 1 : -1;
411
+ return a < b ? -1 : a > b ? 1 : 0;
412
+ }
413
+ function searchBound(id, val, after) {
414
+ let lo = 0;
415
+ let hi = id.length;
416
+ while (lo < hi) {
417
+ const mid = lo + hi >>> 1;
418
+ const c = compareSorted(id[mid], val);
419
+ if (c < 0 || after && c === 0) lo = mid + 1;
420
+ else hi = mid;
284
421
  }
285
- return result;
422
+ return lo;
286
423
  }
287
424
  function binary_search_rightmost(id, val) {
288
- let left = 0;
289
- let right = id.length - 1;
290
- let result = -1;
291
- let found = false;
292
- while (left <= right) {
293
- const mid = Math.floor((left + right) / 2);
294
- if (id[mid] <= val) {
295
- if (id[mid] === val) {
296
- result = mid;
297
- found = true;
298
- }
299
- left = mid + 1;
300
- } else {
301
- if (!found) {
302
- result = mid;
303
- }
304
- right = mid - 1;
305
- }
306
- }
307
- return result;
425
+ if (id.length === 0) return -1;
426
+ const at = searchBound(id, val, true);
427
+ return at > 0 && compareSorted(id[at - 1], val) === 0 ? at - 1 : at;
308
428
  }
309
429
  function covariance(id1, id2, biased = true) {
310
- if (id1.length === 0 || id2.length === 0) {
311
- return NaN;
312
- }
313
430
  if (id1.length !== id2.length) {
314
431
  return NaN;
315
432
  }
316
- const n = id1.length;
317
- const mean1 = avg(id1);
318
- const mean2 = avg(id2);
433
+ const xs = [];
434
+ const ys = [];
435
+ for (let i = 0; i < id1.length; i++) {
436
+ if (Number.isNaN(id1[i]) || Number.isNaN(id2[i])) continue;
437
+ xs.push(id1[i]);
438
+ ys.push(id2[i]);
439
+ }
440
+ const n = xs.length;
441
+ if (!n || !biased && n < 2) return NaN;
442
+ let mean1 = 0;
443
+ let mean2 = 0;
444
+ for (let i = 0; i < n; i++) {
445
+ mean1 += xs[i];
446
+ mean2 += ys[i];
447
+ }
448
+ mean1 /= n;
449
+ mean2 /= n;
319
450
  let sum2 = 0;
320
451
  for (let i = 0; i < n; i++) {
321
- sum2 += (id1[i] - mean1) * (id2[i] - mean2);
452
+ sum2 += (xs[i] - mean1) * (ys[i] - mean2);
322
453
  }
323
- const divisor = biased ? n : n - 1;
324
- return sum2 / divisor;
454
+ return sum2 / (biased ? n : n - 1);
325
455
  }
326
456
  function percentile_linear_interpolation(id, percentage) {
327
- if (id.length === 0) {
457
+ if (id.length === 0 || Number.isNaN(percentage)) {
328
458
  return NaN;
329
459
  }
460
+ checkPercentage("percentile_linear_interpolation", percentage);
330
461
  const s = sortedNaLast(id);
331
462
  const n = s.length;
332
- const pos = percentage / 100 * n - 0.5;
333
- if (pos <= 0) return s[0];
334
- if (pos >= n - 1) return s[n - 1];
335
- const lo = Math.floor(pos);
336
- const f = pos - lo;
337
- if (f === 0) return s[lo];
338
- return Number.isNaN(s[n - 1]) ? NaN : s[lo] + f * (s[lo + 1] - s[lo]);
463
+ let pos = n * percentage / 100 + 0.5;
464
+ const nearest = Math.round(pos);
465
+ if (Math.abs(pos - nearest) < 1e-9) pos = nearest;
466
+ if (pos <= 1) return s[0];
467
+ if (pos >= n) return s[n - 1];
468
+ const lower = Math.floor(pos);
469
+ const frac = pos - lower;
470
+ if (frac === 0) return s[lower - 1];
471
+ if (Number.isNaN(s[n - 1])) return NaN;
472
+ return s[lower - 1] * (1 - frac) + s[lower] * frac;
339
473
  }
340
474
  function sortedNaLast(id) {
341
475
  const numbers = id.filter((v) => !Number.isNaN(v)).sort((a, b) => a - b);
342
476
  return [...numbers, ...Array(id.length - numbers.length).fill(NaN)];
343
477
  }
478
+ function checkPercentage(name, percentage) {
479
+ if (!(percentage >= 0 && percentage <= 100)) {
480
+ throw new Error(`array.${name}: percentage ${percentage} must be between 0 and 100`);
481
+ }
482
+ }
344
483
  function percentile_nearest_rank(id, percentage) {
345
484
  if (id.length === 0) {
346
485
  return NaN;
347
486
  }
487
+ const pct = Number.isNaN(percentage) ? 0 : percentage;
488
+ checkPercentage("percentile_nearest_rank", pct);
348
489
  const s = sortedNaLast(id);
349
- const rank = Math.min(s.length, Math.max(1, Math.ceil(percentage / 100 * s.length)));
490
+ const rank = Math.min(s.length, Math.max(1, Math.ceil(pct * s.length / 100)));
350
491
  return s[rank - 1];
351
492
  }
352
493
  function percentrank(id, index) {
353
- if (id.length === 0 || index < 0 || index >= id.length) {
494
+ const i = Number.isNaN(index) ? 0 : Math.trunc(index);
495
+ if (id.length === 0) {
496
+ return NaN;
497
+ }
498
+ if (i < 0 || i >= id.length) {
499
+ throw new Error(`array.percentrank: index ${i} is out of bounds, array size is ${id.length}`);
500
+ }
501
+ if (id.length === 1) {
354
502
  return NaN;
355
503
  }
356
- const value = id[index];
357
- if (isNaN(value)) {
504
+ const value = id[i];
505
+ if (Number.isNaN(value)) {
358
506
  return NaN;
359
507
  }
360
508
  let count = 0;
361
- for (let i = 0; i < id.length; i++) {
362
- if (!isNaN(id[i]) && id[i] <= value) {
363
- count++;
364
- }
509
+ for (const v of id) {
510
+ if (v <= value) count++;
365
511
  }
366
- return count / id.length * 100;
512
+ return (count - 1) * 100 / (id.length - 1);
367
513
  }
368
514
  function sort_indices(id, order = "asc") {
369
- const indices = Array.from({ length: id.length }, (_, i) => i);
370
- indices.sort((a, b) => {
371
- if (order === "asc") {
372
- return id[a] - id[b];
373
- } else {
374
- return id[b] - id[a];
375
- }
376
- });
377
- return indices;
515
+ return sortedPositions(id, order);
378
516
  }
379
517
  function standardize(id) {
380
- if (id.length === 0) {
381
- return [];
518
+ const values = id.filter((v) => !Number.isNaN(v));
519
+ if (!values.length) {
520
+ return id.map(() => NaN);
382
521
  }
383
- const mean = avg(id);
384
- const stdDev = stdev(id);
522
+ const [p, q] = moments(values);
523
+ const mean = p / values.length;
524
+ const stdDev = Math.sqrt(Math.max(0, q / values.length - mean * mean));
385
525
  if (stdDev === 0) {
386
- return id.map(() => NaN);
526
+ return id.map((v) => Number.isNaN(v) ? NaN : 1);
387
527
  }
388
- return id.map((value) => (value - mean) / stdDev);
528
+ return id.map((v) => Number.isNaN(v) ? NaN : (v - mean) / stdDev);
389
529
  }
390
530
  function newtype(size2 = 0, initial_value) {
391
531
  return new_array(size2, initial_value);