oakscriptjs 0.9.5 → 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 +84 -27
  2. package/dist/cjs/array/index.d.ts.map +1 -1
  3. package/dist/cjs/array/index.js +235 -78
  4. package/dist/cjs/array/index.js.map +3 -3
  5. package/dist/cjs/callsite/index.d.ts +81 -2
  6. package/dist/cjs/callsite/index.d.ts.map +1 -1
  7. package/dist/cjs/callsite/index.js +62 -1
  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 +76 -21
  23. package/dist/cjs/math/index.d.ts.map +1 -1
  24. package/dist/cjs/math/index.js +155 -41
  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 +86 -43
  53. package/dist/cjs/ta/index.d.ts.map +1 -1
  54. package/dist/cjs/ta/index.js +173 -267
  55. package/dist/cjs/ta/index.js.map +3 -3
  56. package/dist/cjs/ta/running-sum.d.ts +54 -2
  57. package/dist/cjs/ta/running-sum.d.ts.map +1 -1
  58. package/dist/cjs/ta/running-sum.js +132 -52
  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 +84 -27
  80. package/dist/esm/array/index.d.ts.map +1 -1
  81. package/dist/esm/array/index.js +373 -111
  82. package/dist/esm/array/index.js.map +1 -1
  83. package/dist/esm/callsite/index.d.ts +81 -2
  84. package/dist/esm/callsite/index.d.ts.map +1 -1
  85. package/dist/esm/callsite/index.js +145 -3
  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 +76 -21
  100. package/dist/esm/math/index.d.ts.map +1 -1
  101. package/dist/esm/math/index.js +227 -61
  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 +86 -43
  130. package/dist/esm/ta/index.d.ts.map +1 -1
  131. package/dist/esm/ta/index.js +296 -348
  132. package/dist/esm/ta/index.js.map +1 -1
  133. package/dist/esm/ta/running-sum.d.ts +54 -2
  134. package/dist/esm/ta/running-sum.d.ts.map +1 -1
  135. package/dist/esm/ta/running-sum.js +166 -71
  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 +1 -1
  157. package/src/array/index.ts +352 -127
  158. package/src/callsite/index.ts +147 -3
  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 +256 -74
  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 +306 -386
  172. package/src/ta/running-sum.ts +187 -74
  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) {
@@ -290,85 +427,105 @@ function binary_search_rightmost(id, val) {
290
427
  return at > 0 && compareSorted(id[at - 1], val) === 0 ? at - 1 : at;
291
428
  }
292
429
  function covariance(id1, id2, biased = true) {
293
- if (id1.length === 0 || id2.length === 0) {
294
- return NaN;
295
- }
296
430
  if (id1.length !== id2.length) {
297
431
  return NaN;
298
432
  }
299
- const n = id1.length;
300
- const mean1 = avg(id1);
301
- 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;
302
450
  let sum2 = 0;
303
451
  for (let i = 0; i < n; i++) {
304
- sum2 += (id1[i] - mean1) * (id2[i] - mean2);
452
+ sum2 += (xs[i] - mean1) * (ys[i] - mean2);
305
453
  }
306
- const divisor = biased ? n : n - 1;
307
- return sum2 / divisor;
454
+ return sum2 / (biased ? n : n - 1);
308
455
  }
309
456
  function percentile_linear_interpolation(id, percentage) {
310
- if (id.length === 0) {
457
+ if (id.length === 0 || Number.isNaN(percentage)) {
311
458
  return NaN;
312
459
  }
460
+ checkPercentage("percentile_linear_interpolation", percentage);
313
461
  const s = sortedNaLast(id);
314
462
  const n = s.length;
315
- const pos = percentage / 100 * n - 0.5;
316
- if (pos <= 0) return s[0];
317
- if (pos >= n - 1) return s[n - 1];
318
- const lo = Math.floor(pos);
319
- const f = pos - lo;
320
- if (f === 0) return s[lo];
321
- 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;
322
473
  }
323
474
  function sortedNaLast(id) {
324
475
  const numbers = id.filter((v) => !Number.isNaN(v)).sort((a, b) => a - b);
325
476
  return [...numbers, ...Array(id.length - numbers.length).fill(NaN)];
326
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
+ }
327
483
  function percentile_nearest_rank(id, percentage) {
328
484
  if (id.length === 0) {
329
485
  return NaN;
330
486
  }
487
+ const pct = Number.isNaN(percentage) ? 0 : percentage;
488
+ checkPercentage("percentile_nearest_rank", pct);
331
489
  const s = sortedNaLast(id);
332
- 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)));
333
491
  return s[rank - 1];
334
492
  }
335
493
  function percentrank(id, index) {
336
- if (id.length === 0 || index < 0 || index >= id.length) {
494
+ const i = Number.isNaN(index) ? 0 : Math.trunc(index);
495
+ if (id.length === 0) {
337
496
  return NaN;
338
497
  }
339
- const value = id[index];
340
- if (isNaN(value)) {
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) {
502
+ return NaN;
503
+ }
504
+ const value = id[i];
505
+ if (Number.isNaN(value)) {
341
506
  return NaN;
342
507
  }
343
508
  let count = 0;
344
- for (let i = 0; i < id.length; i++) {
345
- if (!isNaN(id[i]) && id[i] <= value) {
346
- count++;
347
- }
509
+ for (const v of id) {
510
+ if (v <= value) count++;
348
511
  }
349
- return count / id.length * 100;
512
+ return (count - 1) * 100 / (id.length - 1);
350
513
  }
351
514
  function sort_indices(id, order = "asc") {
352
- const indices = Array.from({ length: id.length }, (_, i) => i);
353
- indices.sort((a, b) => {
354
- if (order === "asc") {
355
- return id[a] - id[b];
356
- } else {
357
- return id[b] - id[a];
358
- }
359
- });
360
- return indices;
515
+ return sortedPositions(id, order);
361
516
  }
362
517
  function standardize(id) {
363
- if (id.length === 0) {
364
- return [];
518
+ const values = id.filter((v) => !Number.isNaN(v));
519
+ if (!values.length) {
520
+ return id.map(() => NaN);
365
521
  }
366
- const mean = avg(id);
367
- 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));
368
525
  if (stdDev === 0) {
369
- return id.map(() => NaN);
526
+ return id.map((v) => Number.isNaN(v) ? NaN : 1);
370
527
  }
371
- return id.map((value) => (value - mean) / stdDev);
528
+ return id.map((v) => Number.isNaN(v) ? NaN : (v - mean) / stdDev);
372
529
  }
373
530
  function newtype(size2 = 0, initial_value) {
374
531
  return new_array(size2, initial_value);